Change the way save_story() creates or reads the story.
[gruta.git] / Gruta / Template / Artemus.pm
blob5895c4f5c6c359d0eb6e8a6ea7c2e1fc4b26b982
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{url} = sub {
37 my $t = shift;
39 return $data->url($t, @_);
42 $f{aurl} = sub {
43 my $t = shift;
45 my $ret = $data->url($t, @_);
47 if ($ret !~ /^http:/) {
48 $ret = "http://{-cfg_host_name}/$ret";
51 return $ret;
54 $f{date} = sub {
55 my $fmt = shift;
56 my $d = shift || Gruta::Data::today();
58 return Gruta::Data::format_date($d, $fmt);
61 foreach my $p (Gruta::Data::Topic->new->afields()) {
62 $f{'topic_' . $p} = sub {
63 my $topic = shift;
64 my $ret = '';
66 if ($topic ne '[]') {
67 if (my $topic = $data->source->topic($topic)) {
68 $ret = $topic->get($p) || '';
72 return $ret;
76 foreach my $p (Gruta::Data::Story->new->afields()) {
77 $f{'story_' . $p} = sub {
78 my $topic_id = shift;
79 my $id = shift;
80 my $ret = '';
82 if ($id ne '[]') {
83 my $story;
85 if ($story = $data->source->story($topic_id, $id)) {
86 $ret = $story->get($p);
90 return $self->{_artemus}->armor($ret);
94 $f{story_abstract} = sub {
95 my $story = $data->source->story($_[0], $_[1]);
96 my $ret = $data->special_uris($story->get('abstract'));
98 return $self->{_artemus}->armor($ret);
101 $f{story_body} = sub {
102 my $topic_id = shift;
103 my $id = shift;
104 my $ret = undef;
106 if (my $topic = $data->source->topic($topic_id)) {
107 if (my $story = $data->source->story($topic_id, $id)) {
108 my $date2 = $story->get('date2');
110 # if no user and story is not freed, bounce
111 if (!$data->auth() && $date2 && $date2 > Gruta::Data::today()) {
112 # return directly to avoid armoring
113 return '{-restricted_access}';
115 else {
116 # touch the story if user is not
117 # (potentially) involved on it
118 if (! $topic->is_editor($data->auth())) {
119 $story->touch();
122 $ret = $data->special_uris($story->get('body'));
127 if (!defined($ret)) {
128 $data->cgi->redirect('404');
129 $ret = '';
132 return $self->{_artemus}->armor($ret);
135 $f{story_date} = sub {
136 my $format = shift;
137 my $topic_id = shift;
138 my $id = shift;
139 my $ret = '';
141 if ($id ne '[]') {
142 my $story;
144 if ($story = $data->source->story($topic_id, $id)) {
145 $ret = $story->date($format);
149 return $self->{_artemus}->armor($ret);
152 $f{story_date2} = sub {
153 my $format = shift;
154 my $topic_id = shift;
155 my $id = shift;
156 my $ret = '';
158 if ($id ne '[]') {
159 my $story;
161 if ($story = $data->source->story($topic_id, $id)) {
162 $ret = $story->date2($format);
166 return $self->{_artemus}->armor($ret);
169 foreach my $p (Gruta::Data::User->new->afields()) {
170 $f{'user_' . $p} = sub {
171 my $id = shift;
172 my $ret = '';
174 if ($id ne '[]') {
175 $ret = $data->source->user($id)->get($p);
178 return $self->{_artemus}->armor($ret);
182 $f{user_xdate} = sub {
183 my $format = shift;
184 my $id = shift;
185 my $ret = '';
187 if ($id ne '[]') {
188 $ret = $data->source->user($id)->xdate($format);
191 return $ret;
194 $f{template} = sub {
195 my $t = shift;
196 my $ret = '';
198 if ($t ne '[]') {
199 $t = $data->template->template($t);
200 $ret = $self->{_artemus}->armor($t);
203 return $ret;
206 $f{save_template} = sub {
207 my $template = shift;
208 my $content = shift;
209 my $msg = shift;
211 $content = $self->{_artemus}->unarmor($content);
212 $data->template->save_template($template, $content);
214 return $msg || "OK";
217 $f{is_logged_in} = sub {
218 return $data->auth() ? 1 : 0;
221 $f{is_admin} = sub {
222 return $data->auth() && $data->auth->get('is_admin') ? 1 : 0;
225 $f{is_topic_editor} = sub {
226 if (my $topic = $data->source->topic($_[0])) {
227 return $topic->is_editor($data->auth()) ? 1 : 0;
230 return 0;
233 $f{login} = sub {
234 my $user_id = shift;
235 my $password = shift;
236 my $error_msg = shift;
238 if ($user_id eq '') {
239 $error_msg = '{-block_login}';
241 elsif (my $sid = $data->login($user_id, $password)) {
242 $data->cgi->cookie("sid=$sid");
243 $data->cgi->redirect('INDEX');
244 $self->{abort} = 1;
247 return $error_msg || 'Login incorrect.';
250 $f{logout} = sub {
251 $data->logout();
252 $data->cgi->redirect('INDEX');
253 $self->{abort} = 1;
256 $f{assert} = sub {
257 my $cond = shift;
258 my $redir = shift || 'ADMIN';
260 if (! $cond) {
261 $data->cgi->redirect($redir);
262 $self->{abort} = 1;
265 return '';
268 $f{username} = sub {
269 return $data->auth() && $data->auth->get('username') || '';
272 $f{userid} = sub {
273 return $data->auth() && $data->auth->get('id') || '';
276 $f{search_stories} = sub {
277 my $topic_id = shift;
278 my $query = shift;
279 my $future = shift;
280 my $template = shift || 'link_to_story_with_edit';
281 my $sep = shift || '';
283 my $ret = '';
284 my @l = $data->source->search_stories($topic_id, $query, $future);
286 if (@l) {
287 $ret = "<p><b>{-topic_name|$topic_id}</b><br>\n";
289 $ret .= '<ul>';
290 $ret .= join($sep, map { "<li>{-$template|$topic_id|$_}</li>" } @l);
291 $ret .= '</ul>';
293 $self->{search_count} += scalar(@l);
296 return $ret;
299 $f{is_visible_story} = sub {
300 if (my $story = $data->source->story($_[0], $_[1])) {
301 return $story->is_visible($data->auth()) ? 1 : 0;
304 return 0;
307 $f{redir_if_archived} = sub {
308 my $template = shift;
309 my $topic_id = shift;
310 my $id = shift;
312 if ($topic_id =~ /-arch$/) {
313 return '';
316 my $story = $data->source->story($topic_id, $id);
318 if ($story && $story->get('topic_id') =~ /-arch$/) {
319 $data->cgi->redirect(
320 $template,
321 'topic' => $story->get('topic_id'),
322 'id' => $id
324 $self->{abort} = 1;
327 return '';
330 $f{topic_has_archive} = sub {
331 return $data->source->topic($_[0] . '-arch') ? 1 : 0;
334 $f{save_topic} = sub {
335 my $topic_id = shift || return 'Error 1';
337 my $topic = undef;
339 if (not $topic = $data->source->topic($topic_id)) {
340 $topic = Gruta::Data::Topic->new (
341 id => $topic_id );
344 $topic->set('name', shift);
345 $topic->set('editors', shift);
346 $topic->set('internal', shift eq 'on' ? 1 : 0);
347 $topic->set('max_stories', shift);
348 $topic->set('description', shift);
350 # update or insert
351 if ($topic->source()) {
352 $topic = $topic->save();
354 else {
355 $topic = $data->source->insert_topic($topic);
358 return $topic ? 'OK' : 'Error 2';
361 $f{save_story} = sub {
362 my $topic_id = shift || return 'Error 1';
363 my $id = shift;
365 my $story = undef;
367 if ($id eq '[]' || !$id) {
368 $story = Gruta::Data::Story->new (
369 topic_id => $topic_id,
370 id => $id
373 else {
374 $story = $data->source->story($topic_id, $id);
377 my $content = shift;
378 $content = $self->{_artemus}->unarmor($content);
379 $content =~ s/\r//g;
381 $story->set('content', $content);
383 # pick date and drop time
384 my $y = shift;
385 my $m = shift;
386 my $d = shift;
387 shift; shift; shift;
388 my $date = Gruta::Data::today();
390 if ($y && $m && $d) {
391 $date = sprintf("%04d%02d%02d000000", $y, $m, $d);
394 $story->set('date', $date);
395 $story->set('format', shift || 'grutatxt');
397 # get the tags
398 my $tags = shift;
400 # get date2
401 $y = shift;
402 $m = shift;
403 $d = shift;
405 if ($y && $m && $d) {
406 $date = sprintf("%04d%02d%02d000000", $y, $m, $d);
408 else {
409 $date = '';
412 $story->set('date2', $date);
414 $story->set('description', shift);
416 # if there is no userid, add one
417 if (!$story->get('userid')) {
418 $story->set('userid', $data->auth->get('id'));
421 # render the story
422 $data->render($story);
424 if ($story->source()) {
425 $story = $story->save();
427 else {
428 $story = $data->source->insert_story($story);
431 $story->tags(split(/\s*,\s*/, $tags));
433 return $story ? $story->get('id') : 'Error 2';
436 $f{save_user} = sub {
437 shift; # new (ignored)
438 my $id = shift || return 'Error 1';
439 my $username = shift;
440 my $email = shift;
441 my $is_admin = shift;
442 my $can_upload = shift;
443 my $pass1 = shift;
444 my $pass2 = shift;
445 my $xy = shift;
446 my $xm = shift;
447 my $xd = shift;
449 if ($data->auth->get('username') ne $username &&
450 ! $data->auth->get('is_admin')) {
451 $data->cgi->redirect('LOGIN');
452 $self->{abort} = 1;
453 return '';
456 my $user = undef;
458 if (not $user = $data->source->user($id)) {
459 $user = Gruta::Data::User->new (
460 id => $id,
461 is_admin => 0,
462 can_upload => 0,
463 xdate => ''
467 $user->set('username', $username);
468 $user->set('email', $email);
470 # these params can only be set by an admin
471 if ($data->auth->get('is_admin')) {
473 $user->set('is_admin', $is_admin eq 'on' ? 1 : 0);
474 $user->set('can_upload', $can_upload eq 'on' ? 1 : 0);
476 if ($xy and $xm and $xd) {
477 $user->set('xdate',
478 sprintf('%04d%02d%02d000000',
479 $xy, $xm, $xd));
481 else {
482 $user->set('xdate', '');
486 if ($pass1 and $pass2) {
487 if ($pass1 ne $pass2) {
488 croak "Passwords are different";
491 $user->password($pass1);
494 if ($user->source()) {
495 $user = $user->save();
497 else {
498 $user = $data->source->insert_user($user);
501 return $user ? 'OK' : 'Error 2';
504 $f{upload} = sub {
506 $data->cgi->upload($_[0], $_[1]);
507 return 'OK';
510 $f{delete_story} = sub {
511 my $topic_id = shift || return 'Error 1';
512 my $id = shift;
514 $data->source->story($topic_id, $id)->delete();
516 return 'OK';
519 $f{search_count} = sub { $self->{search_count}; };
521 $f{content_type} = sub {
522 $data->cgi->http_headers('Content-Type' => $_[0]);
523 return '';
526 $f{topics} = sub { join(':', $data->source->topics()); };
527 $f{templates} = sub { join(':', $data->template->templates()); };
528 $f{users} = sub { join(':', $data->source->users()); };
530 $f{renderers} = sub { join(':', sort(keys(%{$data->{renderers_h}}))); };
531 $f{upload_dirs} = sub { join(':', $data->cgi->upload_dirs()); };
532 $f{tags} = sub { join(':',
533 map { $_->[0] . ',' . $_->[1] } $data->source->tags()); };
535 $f{var} = sub {
536 my $ret = $self->{cgi_vars}->{$_[0]} || $_[1] || '';
538 return $self->{_artemus}->armor($ret);
541 $f{story_tags} = sub {
542 my $topic_id = shift;
543 my $id = shift;
544 my $ret = '';
546 if ($id ne '[]') {
547 if (my $story = $data->source->story($topic_id, $id)) {
548 $ret = join(':', $story->tags());
552 return $ret;
555 $f{stories_by_date} = sub {
556 my $topic = shift;
557 my $num = shift;
558 my $offset = shift;
559 my $from_date = shift;
560 my $to_date = shift;
561 my $future = shift;
563 my @ret = map { join(',', @{$_}) }
564 $data->source->stories_by_date(
565 $topic ?
566 [ map { (split(',', $_))[0] }
567 split(':', $topic)
568 ] : undef,
569 num => $num,
570 offset => $offset,
571 from => $from_date,
572 to => $to_date,
573 future => $future
576 # $self->{search_count} += scalar(@ret);
578 return join(':', @ret);
581 $f{stories_by_tag} = sub {
582 my $topic = shift;
583 my $tag = shift;
584 my $future = shift;
586 my @ret = $data->source->stories_by_tag(
587 $topic ?
588 [ map { (split(',', $_))[0] }
589 split(':', $topic)
590 ] : undef,
591 $tag, $future);
593 $self->{search_count} += scalar(@ret);
595 return join(':', map { join(',', @{$_}) } @ret);
598 $f{stories_top_ten} = sub {
599 my $num = shift;
601 return join(':', map { join(',', @{$_}) }
602 $data->source->stories_top_ten($num)
606 $f{about} = sub {
607 return 'Gruta ' . $data->version();
610 $self->{abort} = 0;
611 $self->{unresolved} = [];
612 $self->{search_count} = 0;
614 $self->{_artemus} = Artemus->new(
615 'include-path' => $self->{path},
616 'funcs' => \%f,
617 'vars' => \%v,
618 'unresolved' => $self->{unresolved},
619 'abort' => \$self->{abort},
623 return $self->{_artemus};
627 sub data {
628 my $self = shift;
629 my $data = shift;
631 if (defined($data)) {
632 $self->{data} = $data;
633 $self->{_artemus} = undef;
636 return $self->{data};
640 sub cgi_vars {
641 my $self = shift;
643 if (@_) {
644 $self->{cgi_vars} = shift;
645 $self->{_artemus} = undef;
648 return $self->{cgi_vars};
652 sub process {
653 my $self = shift;
654 my $st = shift;
656 my $ret = $self->_artemus->process('{-' . $st . '}');
658 # process special HTML variables
659 my $t;
661 if ($t = $self->{_artemus}->{vars}->{html_title}) {
662 $ret =~ s!</head>!<title>$t</title></head>!i;
665 if ($t = $self->{_artemus}->{vars}->{html_description}) {
666 $ret =~ s!</head>!<meta name="description" content="$t"></head>!i;
669 if ($t = $self->{_artemus}->{vars}->{html_keywords}) {
670 $ret =~ s!</head>!<meta name="keywords" content="$t"></head>!i;
673 return $ret;