On Artemus save_story(), set userid if not set.
[gruta.git] / Gruta / Template / Artemus.pm
blob7a485a6163af937936e343a5866ff74bb1bffa1a
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 $a->create();
23 return $a;
27 sub _artemus {
28 my $self = shift;
30 if (not $self->{_artemus}) {
31 my $data = $self->data();
33 my %f = ();
34 my %v = ();
36 $f{l} = sub {
37 my $t = shift;
39 return '?t=' . $t . ';' . join(';', @_);
42 $f{'add'} = sub { $_[0] + $_[1]; };
43 $f{'sub'} = sub { $_[0] - $_[1]; };
44 $f{'gt'} = sub { $_[0] > $_[1]; };
45 $f{'lt'} = sub { $_[0] < $_[1]; };
46 $f{'eq'} = sub { $_[0] eq $_[1] ? 1 : 0; };
48 $f{date} = sub {
49 my $fmt = shift;
50 my $d = shift || Gruta::Data::today();
52 return Gruta::Data::format_date($d, $fmt);
55 $f{random} = sub { $_[rand(scalar(@_))]; };
57 foreach my $p (Gruta::Data::Topic->new->afields()) {
58 $f{'topic_' . $p} = sub {
59 my $topic = shift;
60 my $ret = '';
62 if ($topic ne '[]') {
63 if (my $topic = $data->topic($topic)) {
64 $ret = $topic->get($p) || '';
68 return $ret;
72 foreach my $p (Gruta::Data::Story->new->afields()) {
73 $f{'story_' . $p} = sub {
74 my $topic_id = shift;
75 my $id = shift;
76 my $ret = '';
78 if ($id ne '[]') {
79 my $story;
81 if ($story = $data->story($topic_id, $id)) {
82 $ret = $story->get($p);
86 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 $topic_id = shift;
98 my $id = shift;
99 my $ret = undef;
101 if (my $topic = $data->topic($topic_id)) {
102 if (my $story = $data->story($topic_id, $id)) {
103 my $date2 = $story->get('date2');
105 # if no user and story is not freed, bounce
106 if (!$data->auth() && $date2 && $date2 > Gruta::Data::today()) {
107 $ret = '{-restricted_access}';
109 else {
110 # touch the story if user is not
111 # (potentially) involved on it
112 if (! $topic->is_editor($data->auth())) {
113 $story->touch();
116 $ret = $data->special_uris($story->get('body'));
121 if (!defined($ret)) {
122 $data->cgi->redirect('?t=404');
123 $ret = '';
126 return $ret;
129 $f{story_date} = sub {
130 my $format = shift;
131 my $topic_id = shift;
132 my $id = shift;
133 my $ret = '';
135 if ($id ne '[]') {
136 my $story;
138 if ($story = $data->story($topic_id, $id)) {
139 $ret = $story->date($format);
143 return $ret;
146 $f{story_date2} = sub {
147 my $format = shift;
148 my $topic_id = shift;
149 my $id = shift;
150 my $ret = '';
152 if ($id ne '[]') {
153 my $story;
155 if ($story = $data->story($topic_id, $id)) {
156 $ret = $story->date2($format);
160 return $ret;
163 foreach my $p (Gruta::Data::User->new->afields()) {
164 $f{'user_' . $p} = sub {
165 my $id = shift;
166 my $ret = '';
168 if ($id ne '[]') {
169 $ret = $data->user($id)->get($p);
172 return $ret;
176 $f{user_xdate} = sub {
177 my $format = shift;
178 my $id = shift;
179 my $ret = '';
181 if ($id ne '[]') {
182 $ret = $data->user($id)->xdate($format);
185 return $ret;
188 $f{template} = sub {
189 my $t = shift;
190 my $ret = '';
192 if ($t ne '[]') {
193 $t = $data->template->template($t);
194 $ret = $self->{_artemus}->armor($t);
197 return $ret;
200 $f{save_template} = sub {
201 my $template = shift;
202 my $content = shift;
203 my $msg = shift;
205 $content = $self->{_artemus}->unarmor($content);
206 $data->template->save_template($template, $content);
208 return $msg || "Template saved.";
211 $f{loop_topics} = sub {
212 my $template = shift;
213 my $sep = shift;
215 return join($sep, map {
216 my $t = $data->topic($_);
217 sprintf('{-%s|%s|%s}',
218 $template, $t->get('name'),
219 $t->get('id')
221 } $data->topics());
224 $f{loop_users} = sub {
225 return join($_[1], map { "{-$_[0]|$_}" } $data->users());
228 $f{loop_renderers} = sub {
229 return join($_[1], map { "{-$_[0]|$_}" }
230 sort(keys(%{$data->{renderers_h}})));
233 $f{loop_templates} = sub {
234 return join($_[1], map { "{-$_[0]|$_}" }
235 $data->template->templates());
238 $f{loop_upload_dirs} = sub {
239 return join($_[1], map { "{-$_[0]|$_}" }
240 $data->cgi->upload_dirs());
243 $f{loop_story_tags} = sub {
244 my $topic_id = shift;
245 my $id = shift;
246 my $ret = '';
248 my $story;
250 if ($story = $data->story($topic_id, $id)) {
251 $ret = join($_[1], map { "{-$_[0]|$_}" }
252 $story->tags());
255 return $ret;
258 $f{story_loop_by_date} = sub {
259 my $topic = shift;
260 my $num = shift;
261 my $offset = shift;
262 my $template = shift;
263 my $sep = shift;
264 my $from = shift;
265 my $to = shift;
266 my $future = shift;
268 return join($sep, map { "{-$template|$topic|$_->[0]}" }
269 $data->stories_by_date(
270 [ $topic ],
271 num => $num,
272 offset => $offset,
273 from => $from,
274 to => $to,
275 future => $future
280 $f{is_logged_in} = sub {
281 return $data->auth() ? 1 : 0;
284 $f{is_admin} = sub {
285 return $data->auth() && $data->auth->get('is_admin') ? 1 : 0;
288 $f{is_topic_editor} = sub {
289 if (my $topic = $data->topic($_[0])) {
290 return $topic->is_editor($data->auth()) ? 1 : 0;
293 return 0;
296 $f{login} = sub {
297 my $user_id = shift;
298 my $password = shift;
299 my $error_msg = shift;
301 if ($user_id eq '' || $user_id eq 'cgi-userid') {
302 $error_msg = '{-login_box}';
304 elsif (my $sid = $data->login($user_id, $password)) {
305 $data->cgi->cookie("sid=$sid");
306 $data->cgi->redirect('?t=INDEX');
307 $self->{abort} = 1;
310 return $error_msg || 'Login incorrect.';
313 $f{logout} = sub {
314 $data->logout();
315 $data->cgi->redirect('?t=INDEX');
316 $self->{abort} = 1;
319 $f{assert} = sub {
320 my $cond = shift;
321 my $redir = shift || 'ADMIN';
323 if (! $cond) {
324 $data->cgi->redirect('?t=' . $redir);
325 $self->{abort} = 1;
328 return '';
331 $f{username} = sub {
332 return $data->auth() && $data->auth->get('username') || '';
335 $f{userid} = sub {
336 return $data->auth() && $data->auth->get('id') || '';
339 $f{search_stories} = sub {
340 my $topic_id = shift;
341 my $query = shift;
342 my $future = shift;
343 my $template = shift || '_story_link_as_item_with_edit';
344 my $sep = shift || '';
346 my $ret = '';
347 my @l = $data->search_stories($topic_id, $query, $future);
349 if (@l) {
350 $ret = "<p><b>{-topic_name|$topic_id}</b><br>\n";
352 $ret .= '<ul>';
353 $ret .= join($sep, map { "{-$template|$topic_id|$_}" } @l);
354 $ret .= '</ul>';
356 $self->{search_count} += scalar(@l);
359 return $ret;
362 $f{is_visible_story} = sub {
363 if (my $story = $data->story($_[0], $_[1])) {
364 return $story->is_visible($data->auth()) ? 1 : 0;
367 return 0;
370 $f{redir_if_archived} = sub {
371 my $template = shift;
372 my $topic_id = shift;
373 my $id = shift;
375 if ($topic_id =~ /-arch$/) {
376 return '';
379 my $story = $data->story($topic_id, $id);
381 if ($story && $story->get('topic_id') =~ /-arch$/) {
382 $data->cgi->redirect(
383 sprintf('?t=%s;topic=%s;id=%s',
384 $template,
385 $story->get('topic_id'),
386 $id)
388 $self->{abort} = 1;
391 return '';
394 $f{topic_has_archive} = sub {
395 return $data->topic($_[0] . '-arch') ? 1 : 0;
398 $f{save_topic} = sub {
399 my $topic_id = shift || return 'Error 1';
401 my $topic = undef;
403 if (not $topic = $data->topic($topic_id)) {
404 $topic = Gruta::Data::Topic->new (
405 id => $topic_id );
408 $topic->set('name', shift);
409 $topic->set('editors', shift);
410 $topic->set('internal', shift eq 'on' ? 1 : 0);
411 $topic->set('max_stories', shift);
413 # update or insert
414 if ($topic->source()) {
415 $topic = $topic->save();
417 else {
418 $topic = $data->insert_topic($topic);
421 return $topic ? 'OK' : 'Error 2';
424 $f{save_story} = sub {
425 my $topic_id = shift || return 'Error 1';
426 my $id = shift;
428 my $story = undef;
430 if (not $story = $data->story($topic_id, $id)) {
431 $story = Gruta::Data::Story->new (
432 topic_id => $topic_id,
433 id => $id
437 my $content = shift;
438 $content = $self->{_artemus}->unarmor($content);
440 $story->set('content', $content);
442 # pick date and drop time
443 my $y = shift;
444 my $m = shift;
445 my $d = shift;
446 shift; shift; shift;
447 my $date = Gruta::Data::today();
449 if ($y && $m && $d) {
450 $date = sprintf("%04d%02d%02d000000", $y, $m, $d);
453 $story->set('date', $date);
454 $story->set('format', shift || 'grutatxt');
456 # get the tags
457 my $tags = shift;
459 # get date2
460 $y = shift;
461 $m = shift;
462 $d = shift;
464 if ($y && $m && $d) {
465 $date = sprintf("%04d%02d%02d000000", $y, $m, $d);
467 else {
468 $date = '';
471 $story->set('date2', $date);
473 # if there is no userid, add one
474 if (!$story->get('userid')) {
475 $story->set('userid', $data->auth->get('id'));
478 # drop all cached stories
479 $data->flush_story_cache();
481 if ($story->source()) {
482 $story = $story->save();
484 else {
485 $story = $data->insert_story($story);
488 if ($tags ne 'cgi-tags') {
489 $story->tags(split(/\s*,\s*/, $tags));
492 return $story ? $story->get('id') : 'Error 2';
495 $f{save_user} = sub {
496 shift; # new (ignored)
497 my $id = shift || return 'Error 1';
498 my $username = shift;
499 my $email = shift;
500 my $is_admin = shift;
501 my $can_upload = shift;
502 my $pass1 = shift;
503 my $pass2 = shift;
504 my $xy = shift;
505 my $xm = shift;
506 my $xd = shift;
508 if ($data->auth->get('username') ne $username &&
509 ! $data->auth->get('is_admin')) {
510 $data->cgi->redirect('?t=LOGIN');
511 $self->{abort} = 1;
512 return '';
515 my $user = undef;
517 if (not $user = $data->user($id)) {
518 $user = Gruta::Data::User->new (
519 id => $id,
520 is_admin => 0,
521 can_upload => 0,
522 xdate => ''
526 $user->set('username', $username);
527 $user->set('email', $email);
529 # these params can only be set by an admin
530 if ($data->auth->get('is_admin')) {
532 $user->set('is_admin', $is_admin eq 'on' ? 1 : 0);
533 $user->set('can_upload', $can_upload eq 'on' ? 1 : 0);
535 if ($xy and $xm and $xd) {
536 $user->set('xdate',
537 sprintf('%04d%02d%02d000000',
538 $xy, $xm, $xd));
540 else {
541 $user->set('xdate', '');
545 if ($pass1 and $pass2) {
546 if ($pass1 ne $pass2) {
547 croak "Passwords are different";
550 $user->password($pass1);
553 if ($user->source()) {
554 $user = $user->save();
556 else {
557 $user = $data->insert_user($user);
560 return $user ? 'OK' : 'Error 2';
563 $f{upload} = sub {
565 $data->cgi->upload($_[0], $_[1]);
566 return 'OK';
569 $f{delete_story} = sub {
570 my $topic_id = shift || return 'Error 1';
571 my $id = shift;
573 $data->story($topic_id, $id)->delete();
575 # drop all cached stories
576 $data->flush_story_cache();
578 return 'OK';
581 $f{search_count} = sub { $self->{search_count}; };
583 $f{content_type} = sub {
584 $data->cgi->http_headers('Content-Type' => $_[0]);
585 return '';
588 $f{topics} = sub { join(':', $data->topics()); };
589 $f{templates} = sub { join(':', $data->template->templates()); };
590 $f{users} = sub { join(':', $data->users()); };
592 $f{renderers} = sub { join(':', sort(keys(%{$data->{renderers_h}}))); };
593 $f{upload_dirs} = sub { join(':', $data->cgi->upload_dirs()); };
594 $f{tags} = sub { join(':', map { $_->[0] . ',' . $_->[1] } $data->tags()); };
596 $f{var} = sub {
597 my $ret = $self->{cgi_vars}->{$_[0]} || $_[1] || '';
599 return $self->{_artemus}->armor($ret);
602 $f{story_tags} = sub {
603 my $topic_id = shift;
604 my $id = shift;
605 my $ret = '';
607 if ($id ne '[]') {
608 my $story = $data->story($topic_id, $id);
610 $ret = join(':', $story->tags());
613 return $ret;
616 $f{stories_by_date} = sub {
617 my $topic = shift;
618 my $num = shift;
619 my $offset = shift;
620 my $from_date = shift;
621 my $to_date = shift;
622 my $future = shift;
624 my @ret = map { join(',', @{$_}) }
625 $data->stories_by_date(
626 $topic ?
627 [ map { (split(',', $_))[0] }
628 split(':', $topic)
629 ] : undef,
630 num => $num,
631 offset => $offset,
632 from => $from_date,
633 to => $to_date,
634 future => $future
637 $self->{search_count} += scalar(@ret);
639 return join(':', @ret);
642 $f{stories_by_tag} = sub {
643 my $topic = shift;
644 my $tag = shift;
645 my $future = shift;
647 my @ret = $data->stories_by_tag(
648 $topic ?
649 [ map { (split(',', $_))[0] }
650 split(':', $topic)
651 ] : undef,
652 $tag, $future);
654 $self->{search_count} += scalar(@ret);
656 return join(':', map { $_->[0] . ',' . $_->[1] } @ret);
659 $f{stories_top_ten} = sub {
660 my $num = shift;
662 return join(':', map { join(',', @{$_}) }
663 $data->stories_top_ten($num)
667 $self->{abort} = 0;
668 $self->{unresolved} = [];
669 $self->{search_count} = 0;
671 $self->{_artemus} = Artemus->new(
672 'include-path' => $self->{path},
673 'funcs' => \%f,
674 'vars' => \%v,
675 'unresolved' => $self->{unresolved},
676 'abort' => \$self->{abort},
679 if ($self->{cgi_vars}) {
680 foreach my $k (keys(%{ $self->{cgi_vars} })) {
681 my $c = $self->{_artemus}->
682 armor($self->{cgi_vars}->{$k});
683 $c =~ s/\r//g;
685 $v{"cgi-${k}"} = $c;
690 return $self->{_artemus};
694 sub data {
695 my $self = shift;
696 my $data = shift;
698 if (defined($data)) {
699 $self->{data} = $data;
700 $self->{_artemus} = undef;
703 return $self->{data};
707 sub cgi_vars {
708 my $self = shift;
710 if (@_) {
711 $self->{cgi_vars} = shift;
712 $self->{_artemus} = undef;
715 return $self->{cgi_vars};
719 sub process { $_[0]->_artemus->process('{-' . $_[1] . '}'); }