Artemus save_story also saves story2.
[gruta.git] / Gruta / Template / Artemus.pm
blob4a05c6130870b0fb4e4038d4473ac2d74c3ad296
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 foreach my $p (Gruta::Data::Topic->new->afields()) {
50 $f{'topic_' . $p} = sub {
51 my $topic = shift;
52 my $ret = '';
54 if ($topic ne '[]') {
55 $ret = $data->topic($topic)->get($p) || '';
58 return $ret;
62 foreach my $p (Gruta::Data::Story->new->afields()) {
63 $f{'story_' . $p} = sub {
64 my $topic_id = shift;
65 my $id = shift;
66 my $ret = '';
68 if ($id ne '[]') {
69 $ret = $data->story($topic_id, $id)->get($p);
72 return $ret;
76 $f{story_tags} = sub {
77 my $topic_id = shift;
78 my $id = shift;
79 my $ret = '';
81 if ($id ne '[]') {
82 my $story = $data->story($topic_id, $id);
84 $ret = join(', ', $story->tags());
87 return $ret;
90 $f{story_abstract} = sub {
91 my $story = $data->story($_[0], $_[1]);
93 return $data->special_uris($story->get('abstract'));
96 $f{story_body} = sub {
97 my $story = $data->story($_[0], $_[1]);
99 if (not $data->auth()) {
100 $story->touch();
103 return $data->special_uris($story->get('body'));
106 $f{story_date} = sub {
107 my $format = shift;
108 my $topic_id = shift;
109 my $id = shift;
110 my $ret = '';
112 if ($id ne '[]') {
113 $ret = $data->story($topic_id, $id)->date($format);
116 return $ret;
119 $f{story_date2} = sub {
120 my $format = shift;
121 my $topic_id = shift;
122 my $id = shift;
123 my $ret = '';
125 if ($id ne '[]') {
126 $ret = $data->story($topic_id, $id)->date2($format);
129 return $ret;
132 foreach my $p (Gruta::Data::User->new->afields()) {
133 $f{'user_' . $p} = sub {
134 my $id = shift;
135 my $ret = '';
137 if ($id ne '[]') {
138 $ret = $data->user($id)->get($p);
141 return $ret;
145 $f{user_xdate} = sub {
146 my $format = shift;
147 my $id = shift;
148 my $ret = '';
150 if ($id ne '[]') {
151 $ret = $data->user($id)->xdate($format);
154 return $ret;
157 $f{template} = sub {
158 my $t = shift;
159 my $ret = '';
161 if ($t ne '[]') {
162 $t = $data->template->template($t);
163 $ret = $self->{_artemus}->armor($t);
166 return $ret;
169 $f{save_template} = sub {
170 my $template = shift;
171 my $content = shift;
172 my $msg = shift;
174 $content = $self->{_artemus}->unarmor($content);
175 $data->template->save_template($template, $content);
177 return $msg || "Template saved.";
180 $f{loop_topics} = sub {
181 my $template = shift;
182 my $sep = shift;
184 return join($sep, map {
185 my $t = $data->topic($_);
186 sprintf('{-%s|%s|%s}',
187 $template, $t->get('name'),
188 $t->get('id')
190 } $data->topics());
193 $f{loop_users} = sub {
194 return join($_[1], map { "{-$_[0]|$_}" } $data->users());
197 $f{loop_renderers} = sub {
198 return join($_[1], map { "{-$_[0]|$_}" }
199 sort(keys(%{$data->{renderers_h}})));
202 $f{loop_templates} = sub {
203 return join($_[1], map { "{-$_[0]|$_}" }
204 $data->template->templates());
207 $f{loop_upload_dirs} = sub {
208 return join($_[1], map { "{-$_[0]|$_}" }
209 $data->cgi->upload_dirs());
212 $f{loop_story_tags} = sub {
213 my $topic_id = shift;
214 my $id = shift;
216 return join($_[1], map { "{-$_[0]|$_}" }
217 $data->story($topic_id, $id)->tags());
220 $f{story_loop_by_date} = sub {
221 my $topic = shift;
222 my $num = shift;
223 my $offset = shift;
224 my $template = shift;
225 my $sep = shift;
226 my $from = shift;
227 my $to = shift;
228 my $future = shift;
230 return join($sep, map { "{-$template|$topic|$_}" }
231 $data->stories_by_date(
232 $topic,
233 num => $num,
234 offset => $offset,
235 from => $from,
236 to => $to,
237 future => $future
242 $f{is_logged_in} = sub {
243 return $data->auth() ? 1 : 0;
246 $f{is_admin} = sub {
247 if ($data->auth() and $data->auth->get('is_admin')) {
248 return 1;
250 return 0;
253 $f{is_topic_editor} = sub {
254 my $auth;
256 if (not $auth = $data->auth()) {
257 return 0;
260 if ($auth->get('is_admin')) {
261 return 1;
264 my $topic;
266 if (not $topic = $data->topic($_[0])) {
267 return 0;
270 if (my $editors = $topic->get('editors') and
271 my $user_id = $auth->get('id')) {
272 return 1 if $editors =~/\b$user_id\b/;
275 return 0;
278 $f{login} = sub {
279 my $user_id = shift;
280 my $password = shift;
281 my $error_msg = shift;
283 if ($user_id eq '' || $user_id eq 'cgi-userid') {
284 $error_msg = '{-login_box}';
286 elsif (my $sid = $data->login($user_id, $password)) {
287 $data->cgi->cookie("sid=$sid");
288 $data->cgi->redirect('?t=INDEX');
289 $self->{abort} = 1;
292 return $error_msg || 'Login incorrect.';
295 $f{logout} = sub {
296 $data->logout();
297 $data->cgi->redirect('?t=INDEX');
298 $self->{abort} = 1;
301 $f{assert} = sub {
302 my $cond = shift;
303 my $redir = shift || 'ADMIN';
305 if (! $cond) {
306 $data->cgi->redirect('?t=' . $redir);
307 $self->{abort} = 1;
310 return '';
313 $f{username} = sub {
314 my $ret = '';
316 if ($data->auth()) {
317 $ret = $data->auth->get('username');
320 return $ret;
323 $f{search_stories} = sub {
324 my $topic_id = shift;
325 my $query = shift;
326 my $future = shift;
327 my $template = shift || '_story_link_as_item_with_edit';
328 my $sep = shift || '';
330 my $ret = '';
331 my @l = $data->search_stories($topic_id, $query, $future);
333 if (@l) {
334 $ret = "<p><b>{-topic_name|$topic_id}</b><br>\n";
336 $ret .= join($sep, map { "{-$template|$topic_id|$_}" } @l);
338 $self->{search_count} += scalar(@l);
341 return $ret;
344 $f{story_loop_top_ten} = sub {
345 my $num = shift;
346 my $internal = shift; # ignored
347 my $template = shift;
348 my $sep = shift;
350 return join($sep, map { "{-$template|$_->[1]|$_->[2]}" }
351 $data->stories_top_ten($num)
355 $f{is_visible_story} = sub {
356 my $story = $data->story($_[0], $_[1]);
358 if (!$data->auth() && $story->get('date') > Gruta::Data::today()) {
359 return 0;
362 return 1;
365 $f{redir_if_archived} = sub {
366 my $template = shift;
367 my $topic_id = shift;
368 my $id = shift;
370 if ($topic_id =~ /-arch$/) {
371 return '';
374 my $story = $data->story($topic_id, $id);
376 if ($story->get('topic_id') =~ /-arch$/) {
377 $data->cgi->redirect(
378 sprintf('?t=%s;topic=%s;id=%s',
379 $template,
380 $story->get('topic_id'),
381 $id)
383 $self->{abort} = 1;
386 return '';
389 $f{topic_has_archive} = sub {
390 return $data->topic($_[0] . '-arch') ? 1 : 0;
393 $f{save_topic} = sub {
394 my $topic_id = shift || return 'Error 1';
396 my $topic = undef;
398 if (not $topic = $data->topic($topic_id)) {
399 $topic = Gruta::Data::Topic->new (
400 id => $topic_id );
403 $topic->set('name', shift);
404 $topic->set('editors', shift);
405 $topic->set('internal', shift eq 'on' ? 1 : 0);
406 $topic->set('max_stories', shift);
408 # update or insert
409 if ($topic->source()) {
410 $topic = $topic->save();
412 else {
413 $topic = $data->insert_topic($topic);
416 return $topic ? 'OK' : 'Error 2';
419 $f{save_story} = sub {
420 my $topic_id = shift || return 'Error 1';
421 my $id = shift;
423 my $story = undef;
425 if (not $story = $data->story($topic_id, $id)) {
426 $story = Gruta::Data::Story->new (
427 topic_id => $topic_id,
428 id => $id
432 my $content = shift;
433 $content = $self->{_artemus}->unarmor($content);
435 $story->set('content', $content);
437 # pick date and drop time
438 my $y = shift;
439 my $m = shift;
440 my $d = shift;
441 shift; shift; shift;
442 my $date = Gruta::Data::today();
444 if ($y && $m && $d) {
445 $date = sprintf("%04d%02d%02d000000", $y, $m, $d);
448 $story->set('date', $date);
449 $story->set('format', shift || 'grutatxt');
451 # get the tags
452 my $tags = shift;
454 # get date2
455 $y = shift;
456 $m = shift;
457 $d = shift;
459 if ($y && $m && $d) {
460 $date = sprintf("%04d%02d%02d000000", $y, $m, $d);
462 else {
463 $date = '';
466 $story->set('date2', $date);
468 # drop all cached stories
469 $data->flush_story_cache();
471 if ($story->source()) {
472 $story = $story->save();
474 else {
475 $story = $data->insert_story($story);
478 if ($tags ne 'cgi-tags') {
479 $story->tags(split(/\s*,\s*/, $tags));
482 return $story ? $story->get('id') : 'Error 2';
485 $f{save_user} = sub {
486 shift; # new (ignored)
487 my $id = shift || return 'Error 1';
488 my $username = shift;
489 my $email = shift;
490 my $is_admin = shift;
491 my $can_upload = shift;
492 my $pass1 = shift;
493 my $pass2 = shift;
494 my $xy = shift;
495 my $xm = shift;
496 my $xd = shift;
498 if ($data->auth->get('username') ne $username &&
499 ! $data->auth->get('is_admin')) {
500 $data->cgi->redirect('?t=LOGIN');
501 $self->{abort} = 1;
502 return '';
505 my $user = undef;
507 if (not $user = $data->user($id)) {
508 $user = Gruta::Data::User->new (
509 id => $id,
510 is_admin => 0,
511 can_upload => 0,
512 xdate => ''
516 $user->set('username', $username);
517 $user->set('email', $email);
519 # these params can only be set by an admin
520 if ($data->auth->get('is_admin')) {
522 $user->set('is_admin', $is_admin eq 'on' ? 1 : 0);
523 $user->set('can_upload', $can_upload eq 'on' ? 1 : 0);
525 if ($xy and $xm and $xd) {
526 $user->set('xdate',
527 sprintf('%04d%02d%02d000000',
528 $xy, $xm, $xd));
530 else {
531 $user->set('xdate', '');
535 if ($pass1 and $pass2) {
536 if ($pass1 ne $pass2) {
537 croak "Passwords are different";
540 my $salt = sprintf('%02d', rand(100));
541 my $pw = crypt($pass1, $salt);
543 $user->set('password', $pw);
546 if ($user->source()) {
547 $user = $user->save();
549 else {
550 $user = $data->insert_user($user);
553 return $user ? 'OK' : 'Error 2';
556 $f{upload} = sub {
558 $data->cgi->upload($_[0], $_[1]);
559 return 'OK';
562 $f{delete_story} = sub {
563 my $topic_id = shift || return 'Error 1';
564 my $id = shift;
566 $data->story($topic_id, $id)->delete();
568 # drop all cached stories
569 $data->flush_story_cache();
571 return 'OK';
574 $f{search_stories_by_tag} = sub {
575 my $tag = shift;
576 my $template = shift;
577 my $sep = shift;
578 my $future = shift;
580 my @ret = $data->search_stories_by_tag($tag, $future);
581 $self->{search_count} = scalar(@ret);
583 return join($sep, map { "{-$template|$_->[0]|$_->[1]}" } @ret);
586 $f{search_count} = sub { $self->{search_count}; };
588 $f{content_type} = sub {
589 $data->cgi->http_headers('Content-Type' => $_[0]);
590 return '';
593 $f{loop_tags} = sub {
594 return join($_[1], map { "{-$_[0]|$_->[0]|$_->[1]}" }
595 $data->tags());
598 $self->{abort} = 0;
599 $self->{unresolved} = [];
600 $self->{search_count} = 0;
602 $self->{_artemus} = Artemus->new(
603 'include-path' => $self->{path},
604 'funcs' => \%f,
605 'vars' => \%v,
606 'unresolved' => $self->{unresolved},
607 'abort' => \$self->{abort},
610 if ($self->{cgi_vars}) {
611 foreach my $k (keys(%{ $self->{cgi_vars} })) {
612 my $c = $self->{_artemus}->
613 armor($self->{cgi_vars}->{$k});
614 $c =~ s/\r//g;
616 $v{"cgi-${k}"} = $c;
621 return $self->{_artemus};
625 sub data {
626 my $self = shift;
627 my $data = shift;
629 if (defined($data)) {
630 $self->{data} = $data;
631 $self->{_artemus} = undef;
634 return $self->{data};
638 sub cgi_vars {
639 my $self = shift;
641 if (@_) {
642 $self->{cgi_vars} = shift;
643 $self->{_artemus} = undef;
646 return $self->{cgi_vars};
650 sub process { $_[0]->_artemus->process('{-' . $_[1] . '}'); }