Unified some templates.
[gruta.git] / Gruta / Template / Artemus.pm
blobfb0c69a34eeea4ed5549a2097e416ecb5b9c32dc
1 package Gruta::Template::Artemus;
3 use strict;
4 use warnings;
5 use Carp;
7 use base 'Gruta::Template::BASE';
9 use Artemus;
10 use Gruta::Data;
12 sub new {
13 my $class = shift;
14 my %args = @_;
16 my $a = bless( {}, $class );
18 $a->{_artemus} = undef;
19 $a->{path} = $args{path};
21 return $a;
25 sub _artemus {
26 my $self = shift;
28 if (not $self->{_artemus}) {
29 my $data = $self->data();
31 my %f = ();
32 my %v = ();
34 $f{l} = sub {
35 my $t = shift;
37 return '?t=' . $t . ';' . join(';', @_);
40 $f{'add'} = sub { $_[0] + $_[1]; };
41 $f{'sub'} = sub { $_[0] - $_[1]; };
42 $f{'gt'} = sub { $_[0] > $_[1]; };
43 $f{'lt'} = sub { $_[0] < $_[1]; };
44 $f{'eq'} = sub { $_[0] eq $_[1] ? 1 : 0; };
46 $f{date} = sub { Gruta::Data::today(); };
47 $f{random} = sub { $_[rand(scalar(@_))]; };
49 $f{foreach} = sub {
50 my $list = shift;
51 my $code = shift;
52 my $sep = shift;
53 my @ret = ();
55 $code =~ s/\[/{/g;
56 $code =~ s/\]/}/g;
57 $code =~ s@/@|@g;
59 foreach my $e (split(/;/, $list)) {
60 my @e = split(/,/, $e);
62 my $c = $code;
63 my $n = 0;
65 foreach my $i (@e) {
66 $c =~ s/%$n/$i/g;
67 $n++;
70 push(@ret, $c);
73 return join($sep, @ret);
76 foreach my $p (Gruta::Data::Topic->new->afields()) {
77 $f{'topic_' . $p} = sub {
78 my $topic = shift;
79 my $ret = '';
81 if ($topic ne '[]') {
82 $ret = $data->topic($topic)->get($p) || '';
85 return $ret;
89 foreach my $p (Gruta::Data::Story->new->afields()) {
90 $f{'story_' . $p} = sub {
91 my $topic_id = shift;
92 my $id = shift;
93 my $ret = '';
95 if ($id ne '[]') {
96 $ret = $data->story($topic_id, $id)->get($p);
99 return $ret;
103 $f{story_tags} = sub {
104 my $topic_id = shift;
105 my $id = shift;
106 my $ret = '';
108 if ($id ne '[]') {
109 my $story = $data->story($topic_id, $id);
111 $ret = join(', ', $story->tags());
114 return $ret;
117 $f{story_abstract} = sub {
118 my $story = $data->story($_[0], $_[1]);
120 return $data->special_uris($story->get('abstract'));
123 $f{story_body} = sub {
124 my $topic_id = shift;
125 my $id = shift;
126 my $ret = '{-404}';
128 if (my $topic = $data->topic($topic_id)) {
129 if (my $story = $data->story($topic_id, $id)) {
130 my $date2 = $story->get('date2');
132 # if no user and story is not freed, bounce
133 if (!$data->auth() && $date2 && $date2 > Gruta::Data::today()) {
134 $ret = '{-restricted_access}';
136 else {
137 # touch the story if user is not
138 # (potentially) involved on it
139 if (! $topic->is_editor($data->auth())) {
140 $story->touch();
143 $ret = $data->special_uris($story->get('body'));
148 return $ret;
151 $f{story_date} = sub {
152 my $format = shift;
153 my $topic_id = shift;
154 my $id = shift;
155 my $ret = '';
157 if ($id ne '[]') {
158 $ret = $data->story($topic_id, $id)->date($format);
161 return $ret;
164 $f{story_date2} = sub {
165 my $format = shift;
166 my $topic_id = shift;
167 my $id = shift;
168 my $ret = '';
170 if ($id ne '[]') {
171 $ret = $data->story($topic_id, $id)->date2($format);
174 return $ret;
177 foreach my $p (Gruta::Data::User->new->afields()) {
178 $f{'user_' . $p} = sub {
179 my $id = shift;
180 my $ret = '';
182 if ($id ne '[]') {
183 $ret = $data->user($id)->get($p);
186 return $ret;
190 $f{user_xdate} = sub {
191 my $format = shift;
192 my $id = shift;
193 my $ret = '';
195 if ($id ne '[]') {
196 $ret = $data->user($id)->xdate($format);
199 return $ret;
202 $f{template} = sub {
203 my $t = shift;
204 my $ret = '';
206 if ($t ne '[]') {
207 $t = $data->template->template($t);
208 $ret = $self->{_artemus}->armor($t);
211 return $ret;
214 $f{save_template} = sub {
215 my $template = shift;
216 my $content = shift;
217 my $msg = shift;
219 $content = $self->{_artemus}->unarmor($content);
220 $data->template->save_template($template, $content);
222 return $msg || "Template saved.";
225 $f{loop_topics} = sub {
226 my $template = shift;
227 my $sep = shift;
229 return join($sep, map {
230 my $t = $data->topic($_);
231 sprintf('{-%s|%s|%s}',
232 $template, $t->get('name'),
233 $t->get('id')
235 } $data->topics());
238 $f{loop_users} = sub {
239 return join($_[1], map { "{-$_[0]|$_}" } $data->users());
242 $f{loop_renderers} = sub {
243 return join($_[1], map { "{-$_[0]|$_}" }
244 sort(keys(%{$data->{renderers_h}})));
247 $f{loop_templates} = sub {
248 return join($_[1], map { "{-$_[0]|$_}" }
249 $data->template->templates());
252 $f{loop_upload_dirs} = sub {
253 return join($_[1], map { "{-$_[0]|$_}" }
254 $data->cgi->upload_dirs());
257 $f{loop_story_tags} = sub {
258 my $topic_id = shift;
259 my $id = shift;
261 return join($_[1], map { "{-$_[0]|$_}" }
262 $data->story($topic_id, $id)->tags());
265 $f{story_loop_by_date} = sub {
266 my $topic = shift;
267 my $num = shift;
268 my $offset = shift;
269 my $template = shift;
270 my $sep = shift;
271 my $from = shift;
272 my $to = shift;
273 my $future = shift;
275 return join($sep, map { "{-$template|$topic|$_}" }
276 $data->stories_by_date(
277 $topic,
278 num => $num,
279 offset => $offset,
280 from => $from,
281 to => $to,
282 future => $future
287 $f{is_logged_in} = sub {
288 return $data->auth() ? 1 : 0;
291 $f{is_admin} = sub {
292 return $data->auth() && $data->auth->get('is_admin') ? 1 : 0;
295 $f{is_topic_editor} = sub {
296 if (my $topic = $data->topic($_[0])) {
297 return $topic->is_editor($data->auth()) ? 1 : 0;
300 return 0;
303 $f{login} = sub {
304 my $user_id = shift;
305 my $password = shift;
306 my $error_msg = shift;
308 if ($user_id eq '' || $user_id eq 'cgi-userid') {
309 $error_msg = '{-login_box}';
311 elsif (my $sid = $data->login($user_id, $password)) {
312 $data->cgi->cookie("sid=$sid");
313 $data->cgi->redirect('?t=INDEX');
314 $self->{abort} = 1;
317 return $error_msg || 'Login incorrect.';
320 $f{logout} = sub {
321 $data->logout();
322 $data->cgi->redirect('?t=INDEX');
323 $self->{abort} = 1;
326 $f{assert} = sub {
327 my $cond = shift;
328 my $redir = shift || 'ADMIN';
330 if (! $cond) {
331 $data->cgi->redirect('?t=' . $redir);
332 $self->{abort} = 1;
335 return '';
338 $f{username} = sub {
339 return $data->auth() && $data->auth->get('username') || '';
342 $f{userid} = sub {
343 return $data->auth() && $data->auth->get('id') || '';
346 $f{search_stories} = sub {
347 my $topic_id = shift;
348 my $query = shift;
349 my $future = shift;
350 my $template = shift || '_story_link_as_item_with_edit';
351 my $sep = shift || '';
353 my $ret = '';
354 my @l = $data->search_stories($topic_id, $query, $future);
356 if (@l) {
357 $ret = "<p><b>{-topic_name|$topic_id}</b><br>\n";
359 $ret .= join($sep, map { "{-$template|$topic_id|$_}" } @l);
361 $self->{search_count} += scalar(@l);
364 return $ret;
367 $f{story_loop_top_ten} = sub {
368 my $num = shift;
369 my $internal = shift; # ignored
370 my $template = shift;
371 my $sep = shift;
373 return join($sep, map { "{-$template|$_->[1]|$_->[2]}" }
374 $data->stories_top_ten($num)
378 $f{is_visible_story} = sub {
379 if (my $story = $data->story($_[0], $_[1])) {
380 return $story->is_visible($data->auth()) ? 1 : 0;
383 return 0;
386 $f{redir_if_archived} = sub {
387 my $template = shift;
388 my $topic_id = shift;
389 my $id = shift;
391 if ($topic_id =~ /-arch$/) {
392 return '';
395 my $story = $data->story($topic_id, $id);
397 if ($story->get('topic_id') =~ /-arch$/) {
398 $data->cgi->redirect(
399 sprintf('?t=%s;topic=%s;id=%s',
400 $template,
401 $story->get('topic_id'),
402 $id)
404 $self->{abort} = 1;
407 return '';
410 $f{topic_has_archive} = sub {
411 return $data->topic($_[0] . '-arch') ? 1 : 0;
414 $f{save_topic} = sub {
415 my $topic_id = shift || return 'Error 1';
417 my $topic = undef;
419 if (not $topic = $data->topic($topic_id)) {
420 $topic = Gruta::Data::Topic->new (
421 id => $topic_id );
424 $topic->set('name', shift);
425 $topic->set('editors', shift);
426 $topic->set('internal', shift eq 'on' ? 1 : 0);
427 $topic->set('max_stories', shift);
429 # update or insert
430 if ($topic->source()) {
431 $topic = $topic->save();
433 else {
434 $topic = $data->insert_topic($topic);
437 return $topic ? 'OK' : 'Error 2';
440 $f{save_story} = sub {
441 my $topic_id = shift || return 'Error 1';
442 my $id = shift;
444 my $story = undef;
446 if (not $story = $data->story($topic_id, $id)) {
447 $story = Gruta::Data::Story->new (
448 topic_id => $topic_id,
449 id => $id
453 my $content = shift;
454 $content = $self->{_artemus}->unarmor($content);
456 $story->set('content', $content);
458 # pick date and drop time
459 my $y = shift;
460 my $m = shift;
461 my $d = shift;
462 shift; shift; shift;
463 my $date = Gruta::Data::today();
465 if ($y && $m && $d) {
466 $date = sprintf("%04d%02d%02d000000", $y, $m, $d);
469 $story->set('date', $date);
470 $story->set('format', shift || 'grutatxt');
472 # get the tags
473 my $tags = shift;
475 # get date2
476 $y = shift;
477 $m = shift;
478 $d = shift;
480 if ($y && $m && $d) {
481 $date = sprintf("%04d%02d%02d000000", $y, $m, $d);
483 else {
484 $date = '';
487 $story->set('date2', $date);
489 # drop all cached stories
490 $data->flush_story_cache();
492 if ($story->source()) {
493 $story = $story->save();
495 else {
496 $story = $data->insert_story($story);
499 if ($tags ne 'cgi-tags') {
500 $story->tags(split(/\s*,\s*/, $tags));
503 return $story ? $story->get('id') : 'Error 2';
506 $f{save_user} = sub {
507 shift; # new (ignored)
508 my $id = shift || return 'Error 1';
509 my $username = shift;
510 my $email = shift;
511 my $is_admin = shift;
512 my $can_upload = shift;
513 my $pass1 = shift;
514 my $pass2 = shift;
515 my $xy = shift;
516 my $xm = shift;
517 my $xd = shift;
519 if ($data->auth->get('username') ne $username &&
520 ! $data->auth->get('is_admin')) {
521 $data->cgi->redirect('?t=LOGIN');
522 $self->{abort} = 1;
523 return '';
526 my $user = undef;
528 if (not $user = $data->user($id)) {
529 $user = Gruta::Data::User->new (
530 id => $id,
531 is_admin => 0,
532 can_upload => 0,
533 xdate => ''
537 $user->set('username', $username);
538 $user->set('email', $email);
540 # these params can only be set by an admin
541 if ($data->auth->get('is_admin')) {
543 $user->set('is_admin', $is_admin eq 'on' ? 1 : 0);
544 $user->set('can_upload', $can_upload eq 'on' ? 1 : 0);
546 if ($xy and $xm and $xd) {
547 $user->set('xdate',
548 sprintf('%04d%02d%02d000000',
549 $xy, $xm, $xd));
551 else {
552 $user->set('xdate', '');
556 if ($pass1 and $pass2) {
557 if ($pass1 ne $pass2) {
558 croak "Passwords are different";
561 my $salt = sprintf('%02d', rand(100));
562 my $pw = crypt($pass1, $salt);
564 $user->set('password', $pw);
567 if ($user->source()) {
568 $user = $user->save();
570 else {
571 $user = $data->insert_user($user);
574 return $user ? 'OK' : 'Error 2';
577 $f{upload} = sub {
579 $data->cgi->upload($_[0], $_[1]);
580 return 'OK';
583 $f{delete_story} = sub {
584 my $topic_id = shift || return 'Error 1';
585 my $id = shift;
587 $data->story($topic_id, $id)->delete();
589 # drop all cached stories
590 $data->flush_story_cache();
592 return 'OK';
595 $f{search_stories_by_tag} = sub {
596 my $tag = shift;
597 my $template = shift;
598 my $sep = shift;
599 my $future = shift;
601 my @ret = $data->search_stories_by_tag($tag, $future);
602 $self->{search_count} = scalar(@ret);
604 return join($sep, map { "{-$template|$_->[0]|$_->[1]}" } @ret);
607 $f{search_count} = sub { $self->{search_count}; };
609 $f{content_type} = sub {
610 $data->cgi->http_headers('Content-Type' => $_[0]);
611 return '';
614 $f{loop_tags} = sub {
615 return join($_[1], map { "{-$_[0]|$_->[0]|$_->[1]}" }
616 $data->tags());
619 $self->{abort} = 0;
620 $self->{unresolved} = [];
621 $self->{search_count} = 0;
623 $self->{_artemus} = Artemus->new(
624 'include-path' => $self->{path},
625 'funcs' => \%f,
626 'vars' => \%v,
627 'unresolved' => $self->{unresolved},
628 'abort' => \$self->{abort},
631 if ($self->{cgi_vars}) {
632 foreach my $k (keys(%{ $self->{cgi_vars} })) {
633 my $c = $self->{_artemus}->
634 armor($self->{cgi_vars}->{$k});
635 $c =~ s/\r//g;
637 $v{"cgi-${k}"} = $c;
642 return $self->{_artemus};
646 sub data {
647 my $self = shift;
648 my $data = shift;
650 if (defined($data)) {
651 $self->{data} = $data;
652 $self->{_artemus} = undef;
655 return $self->{data};
659 sub cgi_vars {
660 my $self = shift;
662 if (@_) {
663 $self->{cgi_vars} = shift;
664 $self->{_artemus} = undef;
667 return $self->{cgi_vars};
671 sub process { $_[0]->_artemus->process('{-' . $_[1] . '}'); }