allow use of the new template system from static pages
[bse.git] / site / cgi-bin / modules / Generate / Article.pm
CommitLineData
defa98aa
TC
1package Generate::Article;
2use strict;
aefcabcb
TC
3use BSE::Template;
4use Constants qw(%LEVEL_DEFAULTS $CGI_URI $ADMIN_URI $IMAGES_URI
5 $UNLISTED_LEVEL1_IN_CRUMBS);
f40af7e2 6use BSE::TB::Images;
defa98aa
TC
7use vars qw(@ISA);
8use Generate;
2990391c 9use BSE::Regen qw(generate_button);
c76e86ea 10use BSE::Util::Tags qw(tag_article);
6430ee52 11use BSE::TB::ArticleFiles;
defa98aa 12@ISA = qw/Generate/;
3f9c8a96 13use BSE::Util::HTML;
d09682dd
TC
14use BSE::Arrows;
15use Carp 'confess';
c76e86ea 16use BSE::Util::Iterate;
defa98aa 17
599fe373 18our $VERSION = "1.004";
cb7fd78d 19
defa98aa
TC
20my $excerptSize = 300;
21
22my %level_names = map { $_, $LEVEL_DEFAULTS{$_}{display} }
23 grep { $LEVEL_DEFAULTS{$_}{display} } keys %LEVEL_DEFAULTS;
24
d09682dd
TC
25sub new {
26 my ($class, %opts) = @_;
27
28 $opts{top} or confess "Please supply 'top' to $class->new";
29
30 return $class->SUPER::new(%opts);
31}
32
defa98aa
TC
33sub edit_link {
34 my ($self, $id) = @_;
35 return "$CGI_URI/admin/add.pl?id=$id";
36}
37
defa98aa
TC
38sub link_to_form {
39 my ($self, $link, $text, $target) = @_;
40
41 my ($url, $query) = split /\?/, $link;
42 my $form = qq!<form action="$url"!;
43 $form .= qq! target="$target"! if $target;
44 $form .= '>';
45 if (defined $query && length $query) {
46 for my $attr (split /&/, $query) {
47 my ($name, $value) = split /=/, $attr, 2;
48 # I'm assuming none of the values are uri escaped
918735d1 49 $value = escape_html($value);
d09682dd 50 $form .= qq!<input type=hidden name="$name" value="$value" />!
defa98aa
TC
51 }
52 }
d09682dd 53 $form .= qq!<input type=submit value="!.escape_html($text).'" />';
defa98aa
TC
54 $form .= "</form>";
55
56 return $form;
57}
58
59sub generate_low {
60 my ($self, $template, $article, $articles, $embedded) = @_;
61 my %acts;
62 %acts = $self -> baseActs($articles, \%acts, $article, $embedded);
63
599fe373
TC
64 my $page = BSE::Template->replace($template, $self->{cfg}, \%acts,
65 $self->variables);
9c5943b6
TC
66
67 %acts = (); # try to destroy any circular refs
68
69 return $page;
defa98aa
TC
70}
71
4772671f 72sub tag_title {
1da7ba81 73 my ($cfg, $article, $images, $args, $acts, $funcname, $templater) = @_;
4772671f
TC
74
75 my $which = $args || 'article';
76
77 exists $acts->{$which}
78 or return "** no such object $which **";
79
c76e86ea 80 my $title = $templater->perform($acts, $which, 'title');
4772671f 81 my $imagename = $which eq 'article' ? $article->{titleImage} :
c76e86ea 82 $templater->perform($acts, $which, 'titleImage');
1da7ba81
TC
83 my $xhtml = $cfg->entry("basic", "xhtml", 1);
84 if ($imagename) {
85 my $html = qq!<img src="/images/titles/$imagename"!;
86 $html .= ' border="0"' unless $xhtml;
87 $html .= qq! class="bse_image_title" alt="$title" />!;
88 }
4772671f
TC
89 my $im;
90 if ($which eq 'article') {
91 ($im) = grep lc $_->{name} eq 'bse_title', @$images;
92 }
93 else {
c76e86ea 94 my $id = $templater->perform($acts, $which, 'id');
f40af7e2
TC
95 require BSE::TB::Images;
96 my @images = BSE::TB::Images->getBy(articleId=>$id);
4772671f
TC
97 ($im) = grep lc $_->{name} eq 'bse_title', @$images;
98 }
99
100 if ($im) {
e63c3728
TC
101 my $src = $im->{src} || "/images/$im->{image}";
102 $src = escape_html($src);
103 return qq!<img src="$src" width="$im->{width}"!
1da7ba81 104 . qq! height="$im->{height}" alt="$title" class="bse_image_title" />!;
4772671f
TC
105 }
106 else {
107 return $title;
108 }
109}
110
00dd8d82
TC
111sub _default_admin {
112 my ($self, $article, $embedded) = @_;
113
114 my $req = $self->{request};
115 my $html = <<HTML;
116<table><tr>
117<td><form action="$CGI_URI/admin/add.pl" name="edit">
118<input type=submit value="Edit $level_names{$article->{level}}">
119<input type=hidden name=id value="$article->{id}">
120</form></td>
121<td><form action="$ADMIN_URI">
122<input type=submit value="Admin menu">
123</form></td>
124HTML
125 if (exists $level_names{1+$article->{level}}
126 && $req->user_can(edit_add_child=>$article)) {
127 $html .= <<HTML;
128<td><form action="$CGI_URI/admin/add.pl" name="addchild">
129<input type=submit value="Add $level_names{1+$article->{level}}">
130<input type=hidden name=parentid value="$article->{id}">
131</form></td>
132HTML
133 }
134 if (generate_button() && $req->user_can(regen_article=>$article)) {
135 $html .= <<HTML;
136<td><form action="$CGI_URI/admin/generate.pl" name="regen">
137<input type=hidden name=id value="$article->{id}">
138<input type=submit value="Regenerate">
139</form></td>
140HTML
141 }
142 $html .= "<td>".$self->link_to_form($article->{admin}."&admin=0",
143 "Display", "_blank")."</td>";
144 my $parent = $article->parent;
145 if ($article->{link}) {
146 $html .= "<td>"
147 . $self->link_to_form($article->{link}, "On site", "_blank")
148 . "</td>";
149 } elsif ($parent && $parent->{link}) {
150 $html .= "<td>"
151 . $self->link_to_form($parent->{link}, "On site", "_blank")
152 . "</td>";
153 }
154 if ($parent && $parent->{admin} ne $article->{admin} && !$embedded) {
155 $html .= "<td>"
156 .$self->link_to_form($parent->{admin}, "Parent")."</td>";
157 }
158 $html .= <<HTML;
159</tr></table>
160HTML
161 return $html;
162}
163
62533efa
TC
164sub abs_urls {
165 my ($self, $article) = @_;
166
167 my $top = $self->{top} || $article;
168
169 $article->{link} =~ /^\w+:/ || $top->{link} =~ /^\w+:/;
170}
171
00dd8d82
TC
172sub tag_admin {
173 my ($self, $article, $default, $embedded, $arg) = @_;
174
175 $self->{admin} or return '';
176 $self->{request} or return '';
177 my $cfg = $self->{cfg};
178
179 my $name = $arg || $default;
180 my $template = "admin/adminmenu/$name";
181
182 unless (BSE::Template->find_source($template, $cfg)) {
183 return $self->_default_admin($article, $embedded);
184 }
185
186 my $parent = $article->parent;
187 my %acts;
188 %acts =
189 (
573f6f84 190 $self->{request}->admin_tags,
c76e86ea
TC
191 article => [ \&tag_article, $article, $cfg ],
192 parent => [ \&tag_article, $parent, $cfg ],
00dd8d82
TC
193 ifParent => $parent,
194 ifEmbedded => $embedded,
195 );
196
197 return BSE::Template->get_page($template, $cfg, \%acts);
198}
199
195977cd 200sub tag_thumbimage {
02d509ee 201 my ($self, $rcurrent, $images, $args, $acts, $funcname, $templater) = @_;
195977cd 202
02d509ee
TC
203 my ($geometry_id, $id, $field) =
204 DevHelp::Tags->get_parms($args, $acts, $templater);
195977cd
TC
205
206 return $self->do_thumbimage($geometry_id, $id, $field, $images, $$rcurrent);
207}
208
1a040ec1
TC
209sub iter_images {
210 my ($self, $images, $arg) = @_;
211
212 if ($arg eq 'all') {
213 return @$images;
214 }
215 elsif ($arg eq 'named') {
216 return grep $_->{name} ne '', @$images;
217 }
218 elsif ($arg =~ m!^named\s+/([^/]+)/$!) {
219 my $re = $1;
220 return grep $_->{name} =~ /$re/i, @$images;
221 }
222 else {
223 return grep $_->{name} eq '', @$images;
224 }
225}
226
bf909925
TC
227=item filen name
228
229=item filen name field
230
d745f64f
TC
231=item filen -
232
233=item filen - field
234
bf909925
TC
235Reference an article attached file by name.
236
237C<filen name> will display a link to the file.
238
239C<<filen name I<field> >> will display the given field from the file
240record. A I<field> of C<url> will be a URL to the file.
241
242If the file identifier given doesn't exist for the current article the
243empty string is returned, allowing use as ifFilen.
244
245The result is unspecified if the I<field> specified isn't one of the
246image record field names and isn't C<url>.
247
248=cut
249
250sub tag_filen {
d745f64f 251 my ($self, $files, $current, $arg, $acts, $funcname, $templater) = @_;
bf909925
TC
252
253 my ($name, $field, @rest) =
254 DevHelp::Tags->get_parms($arg, $acts, $templater);
255
256 length $name
257 or return '* name cannot be an empty string *';
258
d745f64f
TC
259 my $file;
260 if ($name eq '-') {
261 $$current
262 or return "* filen - can only be used inside a files iterator *";
263
264 $file = $$current;
265 }
266 else {
267 ($file) = grep $_->{name} eq $name, @$files
268 or return '';
269 }
270
271 return $self->_format_file($file, $field, "@rest");
272}
273
274=item iterator begin files
275
276=item iterator begin files named /foo/
277
278=item iterator begin files filter: FILE[file_handler] eq 'flv'
279
280=item file field
281
282Iterate over files attached to the current article.
283
284<:file field:> can only access simple attributes.
285
286<:filen - field:> can also access any inline representations.
287
288=cut
289
290sub iter_files {
291 my ($self, $files, $arg, $acts, $funcname, $templater) = @_;
292
293 $arg =~ /\S/
294 or return @$files;
295
296 if ($arg =~ m(^named\s+/([^/]+)/$)) {
297 my $re = $1;
298 return grep $_->{name} =~ /$re/i, @$files;
299 }
300 if ($arg =~ m(^filter: (.*)$)s) {
301 my $expr = $1;
302 $expr =~ s/FILE\[(\w+)\]/\$file->$1/g;
303 my $sub = eval 'sub { my $file = shift; ' . $expr . '; }';
304 $sub
305 or die "* Cannot compile sub from filter $expr: $@ *";
306 return grep $sub->($_), @$files;
307 }
bf909925 308
d745f64f 309 die "* Unknown type of file filter expression *";
bf909925
TC
310}
311
312=item iterator: crumbs/crumb
313
314Iterators over the ancestor tree from the article parent to the root.
315
316Parameters include:
317
318=over
319
320=item *
321
322showtop - the top level article is included even if unlisted
323
324=item *
325
326listedonly - only listed articles in the tree are included
327
328=back
329
330The default depends on the value of $Constants::UNLISTED_LEVEL1_IN_CRUMBS.
331
332=cut
333
334sub iter_crumbs {
335 my ($self, $crumbs, $args) = @_;
336
337 $args ||= $UNLISTED_LEVEL1_IN_CRUMBS ? 'showtop' : 'listedonly';
338 if ($args eq 'showtop') {
339 return @$crumbs;
340 }
341 else {
342 return grep $_->{listed}, @$crumbs;
343 }
344}
345
fd1b40b1
TC
346sub tag_ifUnderThreshold {
347 my ($self, $article, $args) = @_;
348
349 my $count;
350 my $what = $args || '';
351 if ($self->{kids}{$article->{id}}{$what}) {
352 $count = @{$self->{kids}{$article->{id}}{$what}};
353 }
354 else {
355 $count = @{$self->{kids}{$article->{id}}{children}};
356 }
357
358 return $count <= $article->{threshold};
359}
360
defa98aa
TC
361sub baseActs {
362 my ($self, $articles, $acts, $article, $embedded) = @_;
363
f00a461d 364 my $cfg = $self->{cfg} || BSE::Cfg->single;
2076966c 365
599fe373
TC
366 $self->set_variable(article => $article);
367 $self->set_variable(embedded => $embedded);
defa98aa
TC
368 # used to generate the list (or not) of children to this article
369 my $child_index = -1;
370 my @children = $articles->listedChildren($article->{id});
371
372 # used to generate a navigation list for the article
373 # generate a list of ancester articles/sections
4772671f 374 # Jason calls these breadcrumbs
defa98aa 375 my @crumbs;
7928764a 376 my @ancestors;
defa98aa
TC
377 my $temp = $article;
378 while ($temp->{parentid} > 0
379 and my $crumb = $articles->getByPkey($temp->{parentid})) {
7928764a 380 unshift(@ancestors, $crumb);
defa98aa
TC
381 unshift(@crumbs, $crumb) if $crumb->{listed} == 1 || $crumb->{level} == 1;
382 $temp = $crumb;
383 }
bf909925
TC
384 #my $crumb_index = -1;
385 #my @work_crumbs; # set by the crumbs iterator
386 my $current_crumb;
defa98aa
TC
387
388 my $parent = $articles->getByPkey($article->{parentid});
8029d4e5 389 my $section = @crumbs ? $crumbs[0] : $article;
defa98aa 390
f40af7e2 391 my @images = BSE::TB::Images->getBy('articleId', $article->{id});
dbe0477f 392 my @unnamed_images = grep $_->{name} eq '', @images;
57d988af 393 my @iter_images;
b6d08f24
TC
394 my $image_index = -1;
395 my $had_image_tags = 0;
b8e8b584 396 my @all_files = sort { $b->{displayOrder} <=> $a->{displayOrder} }
6430ee52 397 BSE::TB::ArticleFiles->getBy(articleId=>$article->{id});
b8e8b584 398 my @files = grep !$_->{hide_from_list}, @all_files;
721cd24c 399
d794b180 400 my $blank = qq!<img src="$IMAGES_URI/trans_pixel.gif" width="17" height="13" border="0" align="absbottom" alt="" />!;
aefcabcb 401
99b7cef0 402 my $top = $self->{top} || $article;
62533efa 403 my $abs_urls = $self->abs_urls($article);
99b7cef0 404
2076966c
TC
405 my $dynamic = $self->{force_dynamic}
406 || (UNIVERSAL::isa($top, 'Article') ? $top->is_dynamic : 0);
407
721cd24c
TC
408 my @stepkids;
409 my @allkids;
410 my @stepparents;
411 if (UNIVERSAL::isa($article, 'Article')) {
412 @stepkids = $article->visible_stepkids;
413 @allkids = $article->all_visible_kids;
3ffa8a72 414 @stepparents = $article->visible_step_parents;
721cd24c 415 }
fd1b40b1
TC
416 $self->{kids}{$article->{id}}{stepkids} = \@stepkids;
417 $self->{kids}{$article->{id}}{allkids} = \@allkids;
418 $self->{kids}{$article->{id}}{children} = \@children;
419
d2730773 420 my $allkids_index;
195977cd 421 my $current_image;
d745f64f 422 my $current_file;
75677b30 423 my $art_it = BSE::Util::Iterate::Article->new(cfg =>$cfg, admin => $self->{admin}, top => $self->{top});
1a040ec1 424 my $it = BSE::Util::Iterate->new;
defa98aa
TC
425 # separate these so the closures can see %acts
426 my %acts =
427 (
7b1351ea 428 $self->SUPER::baseActs($articles, $acts, $article, $embedded),
c76e86ea 429 article=>[ \&tag_article, $article, $cfg ],
defa98aa
TC
430 ifTitleImage =>
431 sub {
432 my $which = shift || 'article';
433 return $acts->{$which} && $acts->{$which}->('titleImage')
434 },
1da7ba81 435 title => [ \&tag_title, $cfg, $article, \@images ],
defa98aa
TC
436 thumbnail =>
437 sub {
a5e3fc4b
TC
438 my ($args, $acts, $name, $templater) = @_;
439 my ($which, $class) = split ' ', $args;
defa98aa 440 $which ||= 'article';
a5e3fc4b
TC
441 if ($acts->{$which} &&
442 (my $image = $templater->perform($acts, $which, 'thumbImage'))) {
443 my $width = $templater->perform($acts, $which, 'thumbWidth');
444 my $height = $templater->perform($acts, $which, 'thumbHeight');
445 my $result = '<img src="/images/'.$image
446 .'" width="'.$width
447 .'" height="'.$height.'"';
defa98aa 448 $result .= qq! class="$class"! if $class;
e3d242f7 449 $result .= ' border="0" alt="" />';
defa98aa
TC
450 return $result;
451 }
452 else {
453 return '';
454 }
455 },
456 ifThumbnail =>
457 sub {
a5e3fc4b
TC
458 my ($which, $acts, $name, $templater) = @_;
459 $which ||= 'article';
460 return $acts->{$which} &&
461 $templater->perform($acts, $which, 'thumbImage');
defa98aa 462 },
33bccea7 463 ifUnderThreshold =>
fd1b40b1 464 [ tag_ifUnderThreshold => $self, $article ],
defa98aa 465 ifChildren => sub { scalar @children },
defa98aa
TC
466 iterate_children_reset => sub { $child_index = -1; },
467 iterate_children =>
468 sub {
469 return ++$child_index < @children;
470 },
471 child =>
472 sub {
c76e86ea 473 return tag_article($children[$child_index], $cfg, $_[0]);
defa98aa
TC
474 },
475
c76e86ea 476 section=> [ \&tag_article, $section, $cfg ],
defa98aa
TC
477
478 # these are mostly obsolete, use moveUp and moveDown instead
479 # where possible
480 ifPrevChild => sub { $child_index > 0 },
481 ifNextChild => sub { $child_index < $#children },
482
483 # generate buttons for administration (only for admin generation)
00dd8d82 484 admin=> [ tag_admin=>$self, $article, 'article', $embedded ],
defa98aa
TC
485
486 # transform the article or response body (entities, images)
487 body=>sub {
5d88571c 488 my ($args, $acts, $funcname, $templater) = @_;
c5286ebe
TC
489 return $self->format_body(acts => $acts,
490 article => $articles,
491 text => $article->{body},
492 imagepos => $article->{imagePos},
493 abs_urls => $abs_urls,
494 auto_images => !$had_image_tags,
495 templater => $templater,
496 images => \@images,
b8e8b584 497 files => \@all_files,
fe4a482b 498 articles => $articles);
defa98aa
TC
499 },
500
501 # used to display a navigation path of parent sections
bf909925
TC
502 $art_it->make_iterator([ iter_crumbs => $self, \@crumbs ],
503 'crumb', 'crumbs', undef, undef,
504 'nocache', \$current_crumb),
defa98aa 505 crumbs =>
8b0b2f34 506 sub {
bf909925
TC
507 # this is obsolete
508 $cfg->entry('basic', 'warn_obsolete', 0)
509 and print STDERR "* crumbs tag obsolete *\n";
510 return tag_article($current_crumb, $cfg, $_[0]);
defa98aa 511 },
bf909925 512
defa98aa
TC
513 # access to parent
514 ifParent => sub { $parent },
515 parent =>
c76e86ea 516 sub { return $parent && tag_article($parent, $cfg, $_[0]) },
defa98aa
TC
517 # for rearranging order in admin mode
518 moveDown=>
519 sub {
aefcabcb 520 @children > 1 or return '';
defa98aa 521 if ($self->{admin} && $child_index < $#children) {
918735d1 522 my $html = <<HTML;
d09682dd 523<a href="$CGI_URI/admin/move.pl?id=$children[$child_index]{id}&amp;d=down"><img src="$IMAGES_URI/admin/move_down.gif" width="17" height="13" border="0" alt="Move Down" align="bottom" /></a>
defa98aa 524HTML
918735d1
TC
525 chop $html;
526 return $html;
defa98aa 527 } else {
aefcabcb 528 return $blank;
defa98aa
TC
529 }
530 },
531 moveUp=>
532 sub {
aefcabcb 533 @children > 1 or return '';
defa98aa 534 if ($self->{admin} && $child_index > 0) {
918735d1 535 my $html = <<HTML;
d09682dd 536<a href="$CGI_URI/admin/move.pl?id=$children[$child_index]{id}&amp;d=up"><img src="$IMAGES_URI/admin/move_up.gif" width="17" height="13" border="0" alt="Move Up" align="bottom" /></a>
defa98aa 537HTML
918735d1
TC
538 chop $html;
539 return $html;
defa98aa 540 } else {
aefcabcb 541 return $blank;
defa98aa
TC
542 }
543 },
d09682dd 544 movekid => [ \&tag_movekid, $self, \$child_index, \@children, $article ],
d2730773
TC
545 movestepkid =>
546 sub {
8b0b2f34 547 my ($arg, $acts, $funcname, $templater) = @_;
d2730773
TC
548 my $html = '';
549 return '' unless $self->{admin};
aefcabcb 550 return '' unless @allkids > 1;
f2e13a1a
TC
551 defined $allkids_index && $allkids_index >= 0 && $allkids_index < @allkids
552 or return '** movestepkid must be inside iterator allkids **';
8b0b2f34
TC
553 my ($img_prefix, $urladd) =
554 DevHelp::Tags->get_parms($arg, $acts, $templater);
555 $img_prefix = '' unless defined $img_prefix;
556 $urladd = '' unless defined $urladd;
d09682dd
TC
557 my $top = $self->{top} || $article;
558 my $refreshto = $ENV{SCRIPT_NAME} . "?id=$top->{id}$urladd";
559 my $down_url = "";
d2730773 560 if ($allkids_index < $#allkids) {
d09682dd 561 $down_url = "$CGI_URI/admin/move.pl?stepparent=$article->{id}&d=swap&id=$allkids[$allkids_index]{id}&other=$allkids[$allkids_index+1]{id}";
aefcabcb 562 }
d09682dd 563 my $up_url = "";
d2730773 564 if ($allkids_index > 0) {
d09682dd 565 $up_url = "$CGI_URI/admin/move.pl?stepparent=$article->{id}&d=swap&id=$allkids[$allkids_index]{id}&other=$allkids[$allkids_index-1]{id}";
aefcabcb 566 }
d09682dd
TC
567
568 return make_arrows($self->{cfg}, $down_url, $up_url, $refreshto, $img_prefix);
d2730773 569 },
a4d3bc0d
TC
570 ifCurrentPage=>
571 sub {
572 my $arg = shift;
573 $arg && $acts->{$arg} && $acts->{$arg}->('id') == $article->{id};
574 },
7928764a
TC
575 ifAncestor =>
576 sub {
577 my ($arg, $acts, $name, $templater) = @_;
578 unless ($arg =~ /^\d+$/) {
54c97cf6 579 $acts->{$arg} or die "ENOIMPL\n";
7928764a
TC
580 $arg = $acts->{$arg} && $templater->perform($acts, $arg, 'id')
581 or return;
582 }
583 scalar grep $_->{id} == $arg, @ancestors, $article;
584 },
201e926c 585 ifStepAncestor => [ \&tag_ifStepAncestor, $article ],
b6d08f24 586 # access to images, if any
ad792708 587 $it->make_iterator([ iter_images => $self, \@images ], 'image', 'images', \@iter_images, \$image_index, 'nocache', \$current_image),
1a040ec1 588 # override the generated image tag
b6d08f24
TC
589 image =>
590 sub {
84440b5b 591 my ($which, $align, $rest) = split ' ', $_[0], 3;
b6d08f24
TC
592
593 $had_image_tags = 1;
594 my $im;
595 if (defined $which && $which =~ /^\d+$/ && $which >=1
596 && $which <= @images) {
f8282904 597 $im = $images[$which-1];
b6d08f24
TC
598 }
599 else {
195977cd 600 $im = $current_image;
b6d08f24 601 }
4772671f 602
daee3409 603 return $self->_format_image($im, $align, $rest);
4772671f
TC
604 },
605 imagen =>
606 sub {
d22a0d7f
TC
607 my ($arg, $acts, $funcname, $templater) = @_;
608 my ($name, $align, @rest) =
609 DevHelp::Tags->get_parms($arg, $acts, $templater);
610 my $rest = "@rest";
4772671f 611
4772671f
TC
612 my ($im) = grep lc $name eq lc $_->{name}, @images
613 or return '';
614
daee3409 615 $self->_format_image($im, $align, $rest);
b6d08f24
TC
616 },
617 ifImage => sub { $_[0] >= 1 && $_[0] <= @images },
195977cd 618 thumbimage => [ tag_thumbimage => $self, \$current_image, \@images ],
d745f64f
TC
619 $it->make
620 (
621 plural => "files",
622 single => "file",
623 code => [ iter_files => $self, \@files ],
624 nocache => 1,
625 store => \$current_file,
626 ),
627 filen => [ tag_filen => $self, \@files, \$current_file ],
721cd24c 628 BSE::Util::Tags->make_iterator(\@stepkids, 'stepkid', 'stepkids'),
c76e86ea
TC
629 $art_it->make_iterator(undef, 'allkid', 'allkids', \@allkids, \$allkids_index),
630 $art_it->make_iterator(undef, 'stepparent', 'stepparents', \@stepparents),
706b12a2 631 top => [ \&tag_article, $self->{top} || $article, $cfg ],
2076966c 632 ifDynamic => $dynamic,
38239958 633 ifStatic => !$dynamic,
b873a8fa 634 ifAccessControlled => [ \&tag_ifAccessControlled, $article ],
defa98aa
TC
635 );
636
99b7cef0 637 if ($abs_urls) {
defa98aa 638 my $oldurl = $acts{url};
b19047a6 639 my $urlbase = $cfg->entryErr('site', 'url');
defa98aa
TC
640 $acts{url} =
641 sub {
642 my $value = $oldurl->(@_);
54c97cf6 643 return $value if $value =~ /^<:/; # handle "can't do it"
defa98aa
TC
644 unless ($value =~ /^\w+:/) {
645 # put in the base site url
b19047a6 646 $value = $urlbase . $value;
defa98aa
TC
647 }
648 return $value;
649 };
650 }
2076966c
TC
651 if ($dynamic && $cfg->entry('basic', 'ajax', 0)) {
652 # make sure the ajax tags are left until we do dynamic replacement
653 delete @acts{qw/ajax ifAjax/};
654 }
655
defa98aa
TC
656 return %acts;
657}
658
201e926c
TC
659sub tag_ifStepAncestor {
660 my ($article, $arg, $acts, $name, $templater) = @_;
661
662 unless ($arg =~ /^\d+$/) {
663 $acts->{$arg} or die "ENOIMPL\n";
664 $arg = $acts->{$arg} && $templater->perform($acts, $arg, 'id')
665 or return;
666 }
352883cc 667 return 0 if $article->{id} < 0;
201e926c
TC
668 return $article->{id} == $arg || $article->is_step_ancestor($arg);
669}
670
57d988af
TC
671sub tag_ifDynamic {
672 my ($self, $top) = @_;
673
674 # this is to support pregenerated pages being handled as dynamic pages
675 $self->{force_dynamic} and return 1;
676
677 UNIVERSAL::isa($top, 'Article') ? $top->is_dynamic : 0;
678}
679
b873a8fa 680sub tag_ifAccessControlled {
295a1e2b 681 my ($article, $arg, $acts, $funcname, $templater) = @_;
b873a8fa
TC
682
683 if ($arg) {
684 if ($acts->{$arg}) {
685 my $id = $templater->perform($acts, $arg, 'id');
686 $article = Articles->getByPkey($id);
687 unless ($article) {
688 print STDERR "** Unknown article $id from $arg in ifAccessControlled\n";
689 return 0;
690 }
691 }
692 else {
693 print STDERR "** Unknown article $arg in ifAccessControlled\n";
694 return 0;
695 }
696 }
697
698 return UNIVERSAL::isa($article, 'Article') ?
699 $article->is_access_controlled : 0;
700}
701
8a153d74
TC
702sub get_image {
703 my ($self, $image_id, $images) = @_;
704
705 my $im;
706 if ($image_id =~ /^\d+$/) {
707 $image_id >= 1 && $image_id <= @$images
708 or return ( undef, "* Out of range image index '$image_id' *" );
709
710 $im = $images->[$image_id-1];
711 }
712 elsif ($image_id =~ /^[^\W\d]\w*$/) {
713 ($im) = grep $_->{name} eq $image_id, @$images
714 or return ( undef, "* Unknown image identifier '$image_id' *" );
715 }
716 else {
717 return ( undef, "* Unrecognized image '$image_id' *" );
718 }
719
720 return $im;
721}
722
723sub do_popimage {
724 my ($self, $image_id, $class, $images) = @_;
725
726 my ($im, $msg) = $self->get_image($image_id, $images);
727 $im
728 or return $msg;
729
730 return $self->do_popimage_low($im, $class);
731}
732
195977cd
TC
733# note: this is called by BSE::Formatter::thumbimage(), update that if
734# this is changed
735sub do_thumbimage {
4ce93a85 736 my ($self, $geo_id, $image_id, $field, $images, $current) = @_;
195977cd
TC
737
738 my $im;
5888d9ca 739 if ($image_id eq '-' && $current) {
4ce93a85 740 $im = $current
195977cd
TC
741 or return "** No current image in images iterator **"
742 }
8a153d74
TC
743 else {
744 ($im, my $msg) = $self->get_image($image_id, $images);
745 $im
746 or return $msg;
195977cd
TC
747 }
748
b902f9de 749 return $self->_sthumbimage_low($geo_id, $im, $field);
195977cd
TC
750}
751
defa98aa
TC
752sub generate {
753 my ($self, $article, $articles) = @_;
754
aefcabcb 755 my $html = BSE::Template->get_source($article->{template}, $self->{cfg});
defa98aa
TC
756 $html =~ s/<:\s*embed\s+(?:start|end)\s*:>//g;
757
758 return $self->generate_low($html, $article, $articles, 0);
759}
760
d09682dd
TC
761sub tag_movekid {
762 my ($self, $rindex, $rchildren, $article, $args, $acts,
763 $funcname, $templater) = @_;
764
765 $self->{admin} or return '';
766 @$rchildren or return '';
767
768 my ($img_prefix, $urladd) =
769 DevHelp::Tags->get_parms($args, $acts, $templater);
770 defined $img_prefix or $img_prefix = '';
771 defined $urladd or $urladd = '';
772
773 my $top = $self->{top} || $article;
774 my $refreshto = $ENV{SCRIPT_NAME} . "?id=$top->{id}$urladd";
775 my $down_url = "";
776 if ($$rindex < $#$rchildren) {
777 $down_url = "$CGI_URI/admin/move.pl?id=$rchildren->[$$rindex]{id}&d=down";
778 }
779 my $up_url = "";
780 if ($$rindex > 0) {
781 $up_url = "$CGI_URI/admin/move.pl?id=$rchildren->[$$rindex]{id}&d=up";
782 }
783
784 return make_arrows($self->{cfg}, $down_url, $up_url, $refreshto, $img_prefix);
785}
786
47c75494
TC
787sub _find_articles {
788 my ($self, $article_id, $article, @rest) = @_;
789
790 if ($article_id eq 'article') {
791 return $article;
792 }
793 elsif ($article_id eq 'children') {
794 return $article->all_visible_kids;
795 }
796 elsif ($article_id eq 'parent') {
797 return $article->parent;
798 }
799 else {
800 return $self->SUPER::_find_articles($article_id, $article, @rest);
801 }
802}
803
defa98aa
TC
8041;
805
806__END__
807
808=head1 NAME
809
810 Generate::Article - generates articles.
811
812=head1 SYNOPSIS
813
814=head1 DESCRIPTION
815
816=head1 TAGS
817
818
819=head2 Tag notes
820
821In your HTML each tag will be preceded by <: and followed by :>
822
823Tags marked as conditional will require a little more. Conditional
824tags can be used in two ways:
825
826<:ifName args:>true text<:or:>false text<:eif:>
827
828or:
829
830<:if Name args:>true text<:or Name:>false text<:eif Name:>
831
832Tags starting iterator ... are used as iterators, like:
833
834<:iterator begin name:>
835repeated text
836<:iterator separator name:>
837separator text
838<:iterator end name:>
839
840In general, a parameter I<which> can be any one of 'article', 'parent'
841or 'section'. In a child iterator it can also be 'child'. In a
842crumbs iterator it can also be 'crumbs'. If I<which> is missing it
843means the current article.
844
845=head2 Normal tags
846
847=over 4
848
849=item article I<name>
850
851Access to fields of the article. See L<Article attributes>.
852
853=item parent I<name>
854
855Access to fields of the parent article. See L<Article attributes>.
856
857=item ifParent
858
859Conditional tag, true if there is a parent.
860
861=item section I<name>
862
863Access to the fields of the section containing the article. See
864L<Article attributes>.
865
866=item title I<which>
867
868The title of the article presented as an image if there is a
869titleImage or as text. See L<Tag notes> for values for which.
870
871=item ifTitleImage I<which>
872
873Conditional tag, true if the given article has a titleImage,
874
875=item thumbnail I<which> I<class>
876
877The thumbnail image as an <img> tag for object I<which> where I<which>
878is one of the article objects defined. The optional I<class> adds a
879class attribute to the tag with that class.
880
881=item ifThumbnail I<which>
882
883Conditional tag, true if the article specified by I<which> has a
884thumbnail.
885
886=item ifUnderThreshold
887
957a90ca
TC
888=item ifUnderThreshold stepkids
889
890=item ifUnderThreshold allkids
891
892Conditional tag, true if the number of children/stepkids/allkids is
893less or equal to the article's threshold.
defa98aa
TC
894
895=item body
896
897The formatted body of the article.
898
899=item keywords
900
901Ignore this one.
902
c8463fc9 903=item iterator ... crumbs [option]
defa98aa
TC
904
905Iterates over the ancestors of the article. See the L</item crumbs>.
906
c8463fc9
TC
907I<option> can be empty, "listedonly" or "showtop". If empty the
908display of an unlisted level1 ancestor is controlled by
909$UNLISTED_LEVEL1_IN_CRUMBS, if "listedonly" then an unlisted level1
910article isn't shown in the crumbs, and it is if "showtop" is the
911I<option>. This can be used in <: ifCrumbs :> too.
912
defa98aa
TC
913=item crumbs I<name>
914
d500a9af
TC
915Access to the fields of the specific ancestor. I<name> can be any of
916the L<Article attributes>.
defa98aa 917
c8463fc9 918=item ifCrumbs [options]
defa98aa
TC
919
920Conditional tag, true if there are any crumbs.
921
c8463fc9
TC
922See L</iterator ... crumbs [option]> for information on I<option>.
923
defa98aa
TC
924=item ifChildren
925
926Conditional tag, true if the article has any children.
927
928=item iterator ... children
929
930Iterates over the children of the article. See the L</item child>.
931
932=item child I<name>
933
934Access to the fields of the current child.
935
936=item summary
937
938Produces a processed summary of the current child's body.
939
940=item ifPrevChild
941
942Conditional tag, true if there is a previous child. Originally used
943for generating a move up link, but you can use the moveUp tag for
944that now.
945
946=item ifNextChild
947
948Conditional tag, true if there is a next child. Originally used to
949generating a move down link, but you can use the moveDown tag for that
950now.
951
a4d3bc0d
TC
952=item ifCurrentPage I<which>
953
954Conditional tag, true if the given I<which> is the page currently
d500a9af
TC
955being generated. This can be used to apply special formatting if a
956C<level1> or C<level2> article is the current page.
a4d3bc0d 957
b6d08f24
TC
958=item iterator ... images
959
57d988af
TC
960Iterates over the unnamed images for the given article.
961
962=item iterator ... images all
963
964Iterates over all images for the article.
965
966=item iterator ... images named
967
968Iterates over the named images for the article.
969
970=item iterator ... images named /regexp/
971
972Iterates over images with names matching the given regular expression.
973Note that if the expression matches an empty string then unnamed
974images will be included.
b6d08f24
TC
975
976=item image which field
977
978Extracts the given field from the specified image.
979
980I<which> in this can be either an image index to access a specific
981image, or "-" to access the current image in the images iterator.
982
983The image fields are:
984
985=over
986
987=item articleId
988
989The identifier of the article the image belongs to.
990
991=item image
992
993A partial url of the image, relative to /images/.
994
995=item alt
996
997Alternative text of the image.
998
999=item width
1000
1001=item height
1002
1003dimensions of the image.
1004
d500a9af
TC
1005=item url
1006
1007the url if any associated with the image
1008
b6d08f24
TC
1009=back
1010
1011=item image which align
1012
1013=item image which
1014
1015=item image
1016
1017Produces HTML to render the given image.
1018
1019I<which> can be an image index (1, 2, 3...) to select one of the
1020images from the current article, or '-' or omitted to select the
1021current image from the images iterator. If align is present then the
1022C<align> attribute of the image is set.
1023
d500a9af
TC
1024If the image has a URL that <a href="...">...</a> will also be
1025generated.
1026
b6d08f24
TC
1027=item ifImage imageindex
1028
1029Condition tag, true if an image exists at the given index.
1030
1031=item ifImages
1032
57d988af
TC
1033=item ifImages all
1034
b6d08f24
TC
1035Conditional tag, true if the article has any images.
1036
57d988af
TC
1037=item ifImages named
1038
1039Conditional tag, true if the article has any named images.
1040
1041=item ifImages named /regexp/
1042
1043Conditional tag, true if the article has any named images, where the
1044name matches the regular expression.
1045
1046=item ifImages unnamed
1047
1048Conditional tag, true if the article has any unnamed images.
1049
defa98aa
TC
1050=item embed child
1051
28c3a9c9 1052This has been made more general and been moved, see L<Generate/embed child>.
defa98aa 1053
57d988af
TC
1054=item ifDynamic
1055
1056Tests if the article is dynamically generated.
1057
1058=item top I<field>
1059
1060Toplevel article being generated. This is the page that any articles
1061are being embedded in.
1062
1063=item iterator ... files
1064
1065Iterates over the files attached to the article, setting the file tag.
1066
1067=item file I<field>
1068
1069Information from the current file in the files iterator.
1070
b8e8b584
TC
1071The file fields are:
1072
1073=over
1074
1075=item *
1076
1077id - identifier for this file
1078
1079=item *
1080
1081articleId - article this file belongs to
1082
1083=item *
1084
1085displayName - the filename of the file as displayed
1086
1087=item *
1088
1089filename - filename of the file as stored on disk,
1090
1091=item *
1092
1093sizeInBytes - size of the file in bytes.
1094
1095=item *
1096
1097description - the entered description of the file
1098
1099=item *
1100
1101contentType - the MIME content type of the file
1102
1103=item *
1104
1105displayOrder - number used to control the listing order.
1106
1107=item *
1108
1109forSale - non-zero if the file needs to be paid for to be downloaded.
1110
1111=item *
1112
1113download - if this is non-zero BSE will attempt to make the browser
1114download the file rather than display it.
1115
1116=item *
1117
1118whenUploaded - date/time when the file was uploaded.
1119
1120=item *
1121
1122requireUser - if non-zero the user must be logged on to download this
1123file.
1124
1125=item *
1126
1127notes - longer descriptive text.
1128
1129=item *
1130
1131name - identifier for the file for filelink[]
1132
1133=item *
1134
1135hide_from_list - if non-zero the file won't be listed by the files
1136iterator, but will still be available to filelink[].
1137
1138=back
1139
defa98aa
TC
1140=back
1141
1142=head2 Article attributes
1143
1144=over 4
1145
1146=item id
1147
1148Identifies the article.
1149
1150=item parentId
1151
1152The identifier of the parent article.
1153
1154=item title
1155
1156The title of the article. See the title tag
1157
1158=item titleImage
1159
1160The name of the title image for the article, if any. See the title
1161and ifTitleImage tags.
1162
1163=item body
1164
1165The body of the article. See the body tag.
1166
1167=item thumbImage
1168
1169=item thumbWidth
1170
1171=item thumbHeight
1172
1173The thumbnail image for the article, if any. See the thumbnail tag.
1174
1175=item release
1176
1177=item expire
1178
1179The release and expiry dates of the article.
1180
1181=item keyword
1182
1183Any keywords for the article. Indexed by the search engine.
1184
1185=item link
1186
1187=item admin
1188
1189Links to the normal and adminstrative versions of the article. The
1190url tag defined by Generate.pm will select the appropriate tag for the
1191current mode.
1192
1193=item threshold
1194
1195The maximum number of articles that should be embeded into the current
1196article for display. See the ifUnderThreshold tag.
1197
1198=item summaryLength
1199
1200The maximum amount of text displayed in the summary of an article.
1201See the summary tag.
1202
1203=item generator
1204
1205The class used to generate the article. Should be one of
1206Generate::Article, Generate::Catalog or Generate::Product.
1207
1208=item level
1209
1210The level of the article. Sections are level1, etc
1211
1212=item listed
1213
1214How the article is listed. If zero then the article can only be found
1215in a search. If 1 then the article is listed in menus and article
1216contents, if 2 then the article is only listed in article contents.
1217
1218=item lastModified
1219
1220When the article was last modified. Currently only used for display
1221in search results.
1222
8a7e4f97
TC
1223=item lastModifiedBy
1224
1225Set the the current admin user logon if access_control is enabled in the cfg.
1226
1227=item created
1228
1229Set to the current date time when a new article is created.
1230
1231=item createdBy
1232
7f05f584
TC
1233Set to the current admin user logon if access_control is enabled in
1234the cfg.
8a7e4f97
TC
1235
1236=item author
1237
1238A user definable field for attributing the article author.
1239
1240=item pageTitle
1241
7f05f584
TC
1242An alternate article title which can be used to make search engine
1243baited oage titles.
8a7e4f97
TC
1244
1245=item metaDescription
1246
7f05f584
TC
1247Article metadata description, used as metadata in the generated HTML
1248output.
8a7e4f97
TC
1249
1250=item metaKeywords
1251
7f05f584
TC
1252Article metadata keywords, used as metadata in the generated HTML
1253output.
8a7e4f97 1254
defa98aa
TC
1255=back
1256
1257The following attributes are unlikely to be used in a page:
1258
1259=over 4
1260
1261=item displayOrder
1262
1263Used internally to control the ordering of articles within a section.
1264
1265=item imagePos
1266
1267The position of the first image in the body. The body tag will format
1268images into the body as specified by this tag.
1269
1270=item template
1271
1272The template used to format the article.
1273
8a7e4f97
TC
1274=item flags
1275
1276Flags which can be checked by code or template tags to control behaviours
1277specific the the article.
1278
1279=item force_dynamic
1280
1281Forces a page to be displayed dynamically with page.pl regardless of
1282access control.
1283
1284=item inherit_siteuser_rights
1285
1286Controls whether the article inherits its parents access controls.
1287
defa98aa
TC
1288=back
1289
1290=head2 Admin tags
1291
1292=over 4
1293
1294The following tags produce output only in admin mode.
1295
1296=item admin
1297
1298Produces buttons and links used for administering the article.
1299
1300=item moveUp
1301
1302Generates a move up link if there is a previous child for the current
1303child.
1304
1305=item moveDown
1306
1307Generates a move down link if there is a next child for the current child.
1308
00dd8d82
TC
1309=item admin
1310
1311Produces buttons and links used for administering the article.
1312
1313This tag can use a specialized template if it's available. If you
1314call it with a parameter, <:admin I<template>:> then it will use
1315template C<< admin/adminmenu/I<template>.tmpl >>. When used in an
1316article template C<< <:admin:> >> behaves like C<< <:admin article:>
1317>>, when used in a catalog template C<< <:admin:> >> behaves like C<<
1318<:admin catalog:> >>, when used in a product template C<< <:admin:> >>
1319behaves like C<< <:admin product:> >>. See L<Admin Menu Templates>
1320for the tags available in admin menu templates.
1321
1322If the template doesn't exist then the old behaviour applies.
defa98aa
TC
1323
1324=back
1325
00dd8d82
TC
1326=head2 Admin Menu Templates
1327
1328These tags can be used in the templates included by the C<< <:admin:>
1329>> tag.
1330
1331The basic template tags and ifUserCan tag can be used in admin menu
1332templates.
1333
1334=over
1335
1336=item article field
defa98aa 1337
00dd8d82
TC
1338Retrieves a field from the current article.
1339
1340=item parent field
1341
1342Retrieves a field from the parent of the current article.
1343
1344=item ifParent
1345
1346Conditional tag, true if the current article has a parent.
1347
1348=item ifEmbedded
1349
1350Conditional tag, true if the current article is embedded in another
1351article, in this context.
1352
1353=back
1354
1355=cut