Fixed a typo in Artemus save_user.
[gruta.git] / Gruta / Template / Artemus.pm
blob8eb36f307e29ac7fd45e179a01d4acdd4af0b683
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_body} = sub {
91 my $story = $data->story($_[0], $_[1]);
92 my $ret = $story->get('body');
94 if (not $data->auth()) {
95 $story->touch();
98 return $ret;
101 $f{story_date} = sub {
102 my $format = shift;
103 my $topic_id = shift;
104 my $id = shift;
105 my $ret = '';
107 if ($id ne '[]') {
108 $ret = $data->story($topic_id, $id)->date($format);
111 return $ret;
114 foreach my $p (Gruta::Data::User->new->afields()) {
115 $f{'user_' . $p} = sub {
116 my $id = shift;
117 my $ret = '';
119 if ($id ne '[]') {
120 $ret = $data->user($id)->get($p);
123 return $ret;
127 $f{user_xdate} = sub {
128 my $format = shift;
129 my $id = shift;
130 my $ret = '';
132 if ($id ne '[]') {
133 $ret = $data->user($id)->xdate($format);
136 return $ret;
139 $f{template} = sub {
140 my $t = shift;
141 my $ret = '';
143 if ($t ne '[]') {
144 $t = $data->template->template($t);
145 $ret = $self->{_artemus}->armor($t);
148 return $ret;
151 $f{save_template} = sub {
152 my $template = shift;
153 my $content = shift;
154 my $msg = shift;
156 $content = $self->{_artemus}->unarmor($content);
157 $data->template->save_template($template, $content);
159 return $msg || "Template saved.";
162 $f{loop_topics} = sub {
163 my $template = shift;
164 my $sep = shift;
166 return join($sep, map {
167 my $t = $data->topic($_);
168 sprintf('{-%s|%s|%s}',
169 $template, $t->get('name'),
170 $t->get('id')
172 } $data->topics());
175 $f{loop_users} = sub {
176 return join($_[1], map { "{-$_[0]|$_}" } $data->users());
179 $f{loop_renderers} = sub {
180 return join($_[1], map { "{-$_[0]|$_}" }
181 sort(keys(%{$data->{renderers_h}})));
184 $f{loop_templates} = sub {
185 return join($_[1], map { "{-$_[0]|$_}" }
186 $data->template->templates());
189 $f{loop_upload_dirs} = sub {
190 return join($_[1], map { "{-$_[0]|$_}" }
191 $data->cgi->upload_dirs());
194 $f{loop_story_tags} = sub {
195 my $topic_id = shift;
196 my $id = shift;
198 return join($_[1], map { "{-$_[0]|$_}" }
199 $data->story($topic_id, $id)->tags());
202 $f{story_loop_by_date} = sub {
203 my $topic = shift;
204 my $num = shift;
205 my $offset = shift;
206 my $template = shift;
207 my $sep = shift;
208 my $from = shift;
209 my $to = shift;
210 my $future = shift;
212 return join($sep, map { "{-$template|$topic|$_}" }
213 $data->stories_by_date(
214 $topic,
215 num => $num,
216 offset => $offset,
217 from => $from,
218 to => $to,
219 future => $future
224 $f{is_logged_in} = sub {
225 return $data->auth() ? 1 : 0;
228 $f{is_admin} = sub {
229 if ($data->auth() and $data->auth->get('is_admin')) {
230 return 1;
232 return 0;
235 $f{is_topic_editor} = sub {
236 my $auth;
238 if (not $auth = $data->auth()) {
239 return 0;
242 if ($auth->get('is_admin')) {
243 return 1;
246 my $topic;
248 if (not $topic = $data->topic($_[0])) {
249 return 0;
252 if (my $editors = $topic->get('editors') and
253 my $user_id = $auth->get('id')) {
254 return 1 if $editors =~/\b$user_id\b/;
257 return 0;
260 $f{login} = sub {
261 my $user_id = shift;
262 my $password = shift;
263 my $error_msg = shift;
265 if ($user_id eq '' || $user_id eq 'cgi-userid') {
266 $error_msg = '{-login_box}';
268 elsif (my $sid = $data->login($user_id, $password)) {
269 $data->cgi->cookie("sid=$sid");
270 $data->cgi->redirect('?t=INDEX');
271 $self->{abort} = 1;
274 return $error_msg || 'Login incorrect.';
277 $f{logout} = sub {
278 $data->logout();
279 $data->cgi->redirect('?t=INDEX');
280 $self->{abort} = 1;
283 $f{assert} = sub {
284 my $cond = shift;
285 my $redir = shift || 'ADMIN';
287 if (! $cond) {
288 $data->cgi->redirect('?t=' . $redir);
289 $self->{abort} = 1;
292 return '';
295 $f{username} = sub {
296 my $ret = '';
298 if ($data->auth()) {
299 $ret = $data->auth->get('username');
302 return $ret;
305 $f{search_stories} = sub {
306 my $topic_id = shift;
307 my $query = shift;
308 my $future = shift;
309 my $template = shift || '_story_link_as_item_with_edit';
310 my $sep = shift || '';
312 my $ret = '';
313 my @l = $data->search_stories($topic_id, $query, $future);
315 if (@l) {
316 $ret = "<p><b>{-topic_name|$topic_id}</b><br>\n";
318 $ret .= join($sep, map { "{-$template|$topic_id|$_}" } @l);
320 $self->{search_count} += scalar(@l);
323 return $ret;
326 $f{story_loop_top_ten} = sub {
327 my $num = shift;
328 my $internal = shift; # ignored
329 my $template = shift;
330 my $sep = shift;
332 return join($sep, map { "{-$template|$_->[1]|$_->[2]}" }
333 $data->stories_top_ten($num)
337 $f{is_visible_story} = sub {
338 my $story = $data->story($_[0], $_[1]);
340 if (!$data->auth() && $story->get('date') > Gruta::Data::today()) {
341 return 0;
344 return 1;
347 $f{redir_if_archived} = sub {
348 my $template = shift;
349 my $topic_id = shift;
350 my $id = shift;
352 if ($topic_id =~ /-arch$/) {
353 return '';
356 my $story = $data->story($topic_id, $id);
358 if ($story->get('topic_id') =~ /-arch$/) {
359 $data->cgi->redirect(
360 sprintf('?t=%s;topic=%s;id=%s',
361 $template,
362 $story->get('topic_id'),
363 $id)
365 $self->{abort} = 1;
368 return '';
371 $f{topic_has_archive} = sub {
372 return $data->topic($_[0] . '-arch') ? 1 : 0;
375 $f{save_topic} = sub {
376 my $topic_id = shift || return 'Error 1';
378 my $topic = undef;
380 if (not $topic = $data->topic($topic_id)) {
381 $topic = Gruta::Data::Topic->new (
382 id => $topic_id );
385 $topic->set('name', shift);
386 $topic->set('editors', shift);
387 $topic->set('internal', shift eq 'on' ? 1 : 0);
388 $topic->set('max_stories', shift);
390 # update or insert
391 if ($topic->source()) {
392 $topic = $topic->save();
394 else {
395 $topic = $data->insert_topic($topic);
398 return $topic ? 'OK' : 'Error 2';
401 $f{save_story} = sub {
402 my $topic_id = shift || return 'Error 1';
403 my $id = shift;
405 my $story = undef;
407 if (not $story = $data->story($topic_id, $id)) {
408 $story = Gruta::Data::Story->new (
409 topic_id => $topic_id,
410 id => $id
414 my $content = shift;
415 $content = $self->{_artemus}->unarmor($content);
417 $story->set('content', $content);
419 # pick date and drop time
420 my $y = shift;
421 my $m = shift;
422 my $d = shift;
423 shift; shift; shift;
424 my $date = Gruta::Data::today();
426 if ($y && $m && $d) {
427 $date = sprintf("%04d%02d%02d000000", $y, $m, $d);
430 $story->set('date', $date);
431 $story->set('format', shift || 'grutatxt');
433 # drop all cached stories
434 $data->flush_story_cache();
436 if ($story->source()) {
437 $story = $story->save();
439 else {
440 $story = $data->insert_story($story);
443 # store the tags
444 my $tags = shift;
446 if ($tags ne 'cgi-tags') {
447 $story->tags(split(/\s*,\s*/, $tags));
450 return $story ? $story->get('id') : 'Error 2';
453 $f{save_user} = sub {
454 shift; # new (ignored)
455 my $id = shift || return 'Error 1';
456 my $username = shift;
457 my $email = shift;
458 my $is_admin = shift;
459 my $can_upload = shift;
460 my $pass1 = shift;
461 my $pass2 = shift;
462 my $xy = shift;
463 my $xm = shift;
464 my $xd = shift;
466 if ($data->auth->get('username') ne $username &&
467 ! $data->auth->get('is_admin')) {
468 $data->cgi->redirect('?t=LOGIN');
469 $self->{abort} = 1;
470 return '';
473 my $user = undef;
475 if (not $user = $data->user($id)) {
476 $user = Gruta::Data::User->new (
477 id => $id,
478 is_admin => 0,
479 can_upload => 0,
480 xdate => ''
484 $user->set('username', $username);
485 $user->set('email', $email);
487 # these params can only be set by an admin
488 if ($data->auth->get('is_admin')) {
490 $user->set('is_admin', $is_admin eq 'on' ? 1 : 0);
491 $user->set('can_upload', $can_upload eq 'on' ? 1 : 0);
493 if ($xy and $xm and $xd) {
494 $user->set('xdate',
495 sprintf('%04d%02d%02d000000',
496 $xy, $xm, $xd));
498 else {
499 $user->set('xdate', '');
503 if ($pass1 and $pass2) {
504 if ($pass1 ne $pass2) {
505 croak "Passwords are different";
508 my $salt = sprintf('%02d', rand(100));
509 my $pw = crypt($pass1, $salt);
511 $user->set('password', $pw);
514 if ($user->source()) {
515 $user = $user->save();
517 else {
518 $user = $data->insert_user($user);
521 return $user ? 'OK' : 'Error 2';
524 $f{upload} = sub {
526 $data->cgi->upload($_[0], $_[1]);
527 return 'OK';
530 $f{delete_story} = sub {
531 my $topic_id = shift || return 'Error 1';
532 my $id = shift;
534 $data->story($topic_id, $id)->delete();
536 # drop all cached stories
537 $data->flush_story_cache();
539 return 'OK';
542 $f{search_stories_by_tag} = sub {
543 my $tag = shift;
544 my $template = shift;
545 my $sep = shift;
546 my $future = shift;
548 my @ret = $data->search_stories_by_tag($tag, $future);
549 $self->{search_count} = scalar(@ret);
551 return join($sep, map { "{-$template|$_->[0]|$_->[1]}" } @ret);
554 $f{search_count} = sub { $self->{search_count}; };
556 $self->{abort} = 0;
557 $self->{unresolved} = [];
558 $self->{search_count} = 0;
560 $self->{_artemus} = Artemus->new(
561 'include-path' => $self->{path},
562 'funcs' => \%f,
563 'vars' => \%v,
564 'unresolved' => $self->{unresolved},
565 'abort' => \$self->{abort},
568 if ($self->{cgi_vars}) {
569 foreach my $k (keys(%{ $self->{cgi_vars} })) {
570 my $c = $self->{_artemus}->
571 armor($self->{cgi_vars}->{$k});
572 $c =~ s/\r//g;
574 $v{"cgi-${k}"} = $c;
579 return $self->{_artemus};
583 sub data {
584 my $self = shift;
585 my $data = shift;
587 if (defined($data)) {
588 $self->{data} = $data;
589 $self->{_artemus} = undef;
592 return $self->{data};
596 sub cgi_vars {
597 my $self = shift;
599 if (@_) {
600 $self->{cgi_vars} = shift;
601 $self->{_artemus} = undef;
604 return $self->{cgi_vars};
608 sub process { $_[0]->_artemus->process('{-' . $_[1] . '}'); }