1 package Generate::Article;
4 use Constants qw(%LEVEL_DEFAULTS $CGI_URI $ADMIN_URI $IMAGES_URI
5 $UNLISTED_LEVEL1_IN_CRUMBS);
9 use BSE::Regen qw(generate_button);
10 use BSE::Util::Tags qw(tag_article);
11 use BSE::TB::ArticleFiles;
16 use BSE::Util::Iterate;
18 our $VERSION = "1.004";
20 my $excerptSize = 300;
22 my %level_names = map { $_, $LEVEL_DEFAULTS{$_}{display} }
23 grep { $LEVEL_DEFAULTS{$_}{display} } keys %LEVEL_DEFAULTS;
26 my ($class, %opts) = @_;
28 $opts{top} or confess "Please supply 'top' to $class->new";
30 return $class->SUPER::new(%opts);
35 return "$CGI_URI/admin/add.pl?id=$id";
39 my ($self, $link, $text, $target) = @_;
41 my ($url, $query) = split /\?/, $link;
42 my $form = qq!<form action="$url"!;
43 $form .= qq! target="$target"! if $target;
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
49 $value = escape_html($value);
50 $form .= qq!<input type=hidden name="$name" value="$value" />!
53 $form .= qq!<input type=submit value="!.escape_html($text).'" />';
60 my ($self, $template, $article, $articles, $embedded) = @_;
62 %acts = $self -> baseActs($articles, \%acts, $article, $embedded);
64 my $page = BSE::Template->replace($template, $self->{cfg}, \%acts,
67 %acts = (); # try to destroy any circular refs
73 my ($cfg, $article, $images, $args, $acts, $funcname, $templater) = @_;
75 my $which = $args || 'article';
77 exists $acts->{$which}
78 or return "** no such object $which **";
80 my $title = $templater->perform($acts, $which, 'title');
81 my $imagename = $which eq 'article' ? $article->{titleImage} :
82 $templater->perform($acts, $which, 'titleImage');
83 my $xhtml = $cfg->entry("basic", "xhtml", 1);
85 my $html = qq!<img src="/images/titles/$imagename"!;
86 $html .= ' border="0"' unless $xhtml;
87 $html .= qq! class="bse_image_title" alt="$title" />!;
90 if ($which eq 'article') {
91 ($im) = grep lc $_->{name} eq 'bse_title', @$images;
94 my $id = $templater->perform($acts, $which, 'id');
95 require BSE::TB::Images;
96 my @images = BSE::TB::Images->getBy(articleId=>$id);
97 ($im) = grep lc $_->{name} eq 'bse_title', @$images;
101 my $src = $im->{src} || "/images/$im->{image}";
102 $src = escape_html($src);
103 return qq!<img src="$src" width="$im->{width}"!
104 . qq! height="$im->{height}" alt="$title" class="bse_image_title" />!;
112 my ($self, $article, $embedded) = @_;
114 my $req = $self->{request};
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}">
121 <td><form action="$ADMIN_URI">
122 <input type=submit value="Admin menu">
125 if (exists $level_names{1+$article->{level}}
126 && $req->user_can(edit_add_child=>$article)) {
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}">
134 if (generate_button() && $req->user_can(regen_article=>$article)) {
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">
142 $html .= "<td>".$self->link_to_form($article->{admin}."&admin=0",
143 "Display", "_blank")."</td>";
144 my $parent = $article->parent;
145 if ($article->{link}) {
147 . $self->link_to_form($article->{link}, "On site", "_blank")
149 } elsif ($parent && $parent->{link}) {
151 . $self->link_to_form($parent->{link}, "On site", "_blank")
154 if ($parent && $parent->{admin} ne $article->{admin} && !$embedded) {
156 .$self->link_to_form($parent->{admin}, "Parent")."</td>";
165 my ($self, $article) = @_;
167 my $top = $self->{top} || $article;
169 $article->{link} =~ /^\w+:/ || $top->{link} =~ /^\w+:/;
173 my ($self, $article, $default, $embedded, $arg) = @_;
175 $self->{admin} or return '';
176 $self->{request} or return '';
177 my $cfg = $self->{cfg};
179 my $name = $arg || $default;
180 my $template = "admin/adminmenu/$name";
182 unless (BSE::Template->find_source($template, $cfg)) {
183 return $self->_default_admin($article, $embedded);
186 my $parent = $article->parent;
190 $self->{request}->admin_tags,
191 article => [ \&tag_article, $article, $cfg ],
192 parent => [ \&tag_article, $parent, $cfg ],
194 ifEmbedded => $embedded,
197 return BSE::Template->get_page($template, $cfg, \%acts);
201 my ($self, $rcurrent, $images, $args, $acts, $funcname, $templater) = @_;
203 my ($geometry_id, $id, $field) =
204 DevHelp::Tags->get_parms($args, $acts, $templater);
206 return $self->do_thumbimage($geometry_id, $id, $field, $images, $$rcurrent);
210 my ($self, $images, $arg) = @_;
215 elsif ($arg eq 'named') {
216 return grep $_->{name} ne '', @$images;
218 elsif ($arg =~ m!^named\s+/([^/]+)/$!) {
220 return grep $_->{name} =~ /$re/i, @$images;
223 return grep $_->{name} eq '', @$images;
229 =item filen name field
235 Reference an article attached file by name.
237 C<filen name> will display a link to the file.
239 C<<filen name I<field> >> will display the given field from the file
240 record. A I<field> of C<url> will be a URL to the file.
242 If the file identifier given doesn't exist for the current article the
243 empty string is returned, allowing use as ifFilen.
245 The result is unspecified if the I<field> specified isn't one of the
246 image record field names and isn't C<url>.
251 my ($self, $files, $current, $arg, $acts, $funcname, $templater) = @_;
253 my ($name, $field, @rest) =
254 DevHelp::Tags->get_parms($arg, $acts, $templater);
257 or return '* name cannot be an empty string *';
262 or return "* filen - can only be used inside a files iterator *";
267 ($file) = grep $_->{name} eq $name, @$files
271 return $self->_format_file($file, $field, "@rest");
274 =item iterator begin files
276 =item iterator begin files named /foo/
278 =item iterator begin files filter: FILE[file_handler] eq 'flv'
282 Iterate over files attached to the current article.
284 <:file field:> can only access simple attributes.
286 <:filen - field:> can also access any inline representations.
291 my ($self, $files, $arg, $acts, $funcname, $templater) = @_;
296 if ($arg =~ m(^named\s+/([^/]+)/$)) {
298 return grep $_->{name} =~ /$re/i, @$files;
300 if ($arg =~ m(^filter: (.*)$)s) {
302 $expr =~ s/FILE\[(\w+)\]/\$file->$1/g;
303 my $sub = eval 'sub { my $file = shift; ' . $expr . '; }';
305 or die "* Cannot compile sub from filter $expr: $@ *";
306 return grep $sub->($_), @$files;
309 die "* Unknown type of file filter expression *";
312 =item iterator: crumbs/crumb
314 Iterators over the ancestor tree from the article parent to the root.
322 showtop - the top level article is included even if unlisted
326 listedonly - only listed articles in the tree are included
330 The default depends on the value of $Constants::UNLISTED_LEVEL1_IN_CRUMBS.
335 my ($self, $crumbs, $args) = @_;
337 $args ||= $UNLISTED_LEVEL1_IN_CRUMBS ? 'showtop' : 'listedonly';
338 if ($args eq 'showtop') {
342 return grep $_->{listed}, @$crumbs;
346 sub tag_ifUnderThreshold {
347 my ($self, $article, $args) = @_;
350 my $what = $args || '';
351 if ($self->{kids}{$article->{id}}{$what}) {
352 $count = @{$self->{kids}{$article->{id}}{$what}};
355 $count = @{$self->{kids}{$article->{id}}{children}};
358 return $count <= $article->{threshold};
362 my ($self, $articles, $acts, $article, $embedded) = @_;
364 my $cfg = $self->{cfg} || BSE::Cfg->single;
366 $self->set_variable(article => $article);
367 $self->set_variable(embedded => $embedded);
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});
372 # used to generate a navigation list for the article
373 # generate a list of ancester articles/sections
374 # Jason calls these breadcrumbs
378 while ($temp->{parentid} > 0
379 and my $crumb = $articles->getByPkey($temp->{parentid})) {
380 unshift(@ancestors, $crumb);
381 unshift(@crumbs, $crumb) if $crumb->{listed} == 1 || $crumb->{level} == 1;
384 #my $crumb_index = -1;
385 #my @work_crumbs; # set by the crumbs iterator
388 my $parent = $articles->getByPkey($article->{parentid});
389 my $section = @crumbs ? $crumbs[0] : $article;
391 my @images = BSE::TB::Images->getBy('articleId', $article->{id});
392 my @unnamed_images = grep $_->{name} eq '', @images;
394 my $image_index = -1;
395 my $had_image_tags = 0;
396 my @all_files = sort { $b->{displayOrder} <=> $a->{displayOrder} }
397 BSE::TB::ArticleFiles->getBy(articleId=>$article->{id});
398 my @files = grep !$_->{hide_from_list}, @all_files;
400 my $blank = qq!<img src="$IMAGES_URI/trans_pixel.gif" width="17" height="13" border="0" align="absbottom" alt="" />!;
402 my $top = $self->{top} || $article;
403 my $abs_urls = $self->abs_urls($article);
405 my $dynamic = $self->{force_dynamic}
406 || (UNIVERSAL::isa($top, 'Article') ? $top->is_dynamic : 0);
411 if (UNIVERSAL::isa($article, 'Article')) {
412 @stepkids = $article->visible_stepkids;
413 @allkids = $article->all_visible_kids;
414 @stepparents = $article->visible_step_parents;
416 $self->{kids}{$article->{id}}{stepkids} = \@stepkids;
417 $self->{kids}{$article->{id}}{allkids} = \@allkids;
418 $self->{kids}{$article->{id}}{children} = \@children;
423 my $art_it = BSE::Util::Iterate::Article->new(cfg =>$cfg, admin => $self->{admin}, top => $self->{top});
424 my $it = BSE::Util::Iterate->new;
425 # separate these so the closures can see %acts
428 $self->SUPER::baseActs($articles, $acts, $article, $embedded),
429 article=>[ \&tag_article, $article, $cfg ],
432 my $which = shift || 'article';
433 return $acts->{$which} && $acts->{$which}->('titleImage')
435 title => [ \&tag_title, $cfg, $article, \@images ],
438 my ($args, $acts, $name, $templater) = @_;
439 my ($which, $class) = split ' ', $args;
440 $which ||= 'article';
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
447 .'" height="'.$height.'"';
448 $result .= qq! class="$class"! if $class;
449 $result .= ' border="0" alt="" />';
458 my ($which, $acts, $name, $templater) = @_;
459 $which ||= 'article';
460 return $acts->{$which} &&
461 $templater->perform($acts, $which, 'thumbImage');
464 [ tag_ifUnderThreshold => $self, $article ],
465 ifChildren => sub { scalar @children },
466 iterate_children_reset => sub { $child_index = -1; },
469 return ++$child_index < @children;
473 return tag_article($children[$child_index], $cfg, $_[0]);
476 section=> [ \&tag_article, $section, $cfg ],
478 # these are mostly obsolete, use moveUp and moveDown instead
480 ifPrevChild => sub { $child_index > 0 },
481 ifNextChild => sub { $child_index < $#children },
483 # generate buttons for administration (only for admin generation)
484 admin=> [ tag_admin=>$self, $article, 'article', $embedded ],
486 # transform the article or response body (entities, images)
488 my ($args, $acts, $funcname, $templater) = @_;
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,
497 files => \@all_files,
498 articles => $articles);
501 # used to display a navigation path of parent sections
502 $art_it->make_iterator([ iter_crumbs => $self, \@crumbs ],
503 'crumb', 'crumbs', undef, undef,
504 'nocache', \$current_crumb),
508 $cfg->entry('basic', 'warn_obsolete', 0)
509 and print STDERR "* crumbs tag obsolete *\n";
510 return tag_article($current_crumb, $cfg, $_[0]);
514 ifParent => sub { $parent },
516 sub { return $parent && tag_article($parent, $cfg, $_[0]) },
517 # for rearranging order in admin mode
520 @children > 1 or return '';
521 if ($self->{admin} && $child_index < $#children) {
523 <a href="$CGI_URI/admin/move.pl?id=$children[$child_index]{id}&d=down"><img src="$IMAGES_URI/admin/move_down.gif" width="17" height="13" border="0" alt="Move Down" align="bottom" /></a>
533 @children > 1 or return '';
534 if ($self->{admin} && $child_index > 0) {
536 <a href="$CGI_URI/admin/move.pl?id=$children[$child_index]{id}&d=up"><img src="$IMAGES_URI/admin/move_up.gif" width="17" height="13" border="0" alt="Move Up" align="bottom" /></a>
544 movekid => [ \&tag_movekid, $self, \$child_index, \@children, $article ],
547 my ($arg, $acts, $funcname, $templater) = @_;
549 return '' unless $self->{admin};
550 return '' unless @allkids > 1;
551 defined $allkids_index && $allkids_index >= 0 && $allkids_index < @allkids
552 or return '** movestepkid must be inside iterator allkids **';
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;
557 my $top = $self->{top} || $article;
558 my $refreshto = $ENV{SCRIPT_NAME} . "?id=$top->{id}$urladd";
560 if ($allkids_index < $#allkids) {
561 $down_url = "$CGI_URI/admin/move.pl?stepparent=$article->{id}&d=swap&id=$allkids[$allkids_index]{id}&other=$allkids[$allkids_index+1]{id}";
564 if ($allkids_index > 0) {
565 $up_url = "$CGI_URI/admin/move.pl?stepparent=$article->{id}&d=swap&id=$allkids[$allkids_index]{id}&other=$allkids[$allkids_index-1]{id}";
568 return make_arrows($self->{cfg}, $down_url, $up_url, $refreshto, $img_prefix);
573 $arg && $acts->{$arg} && $acts->{$arg}->('id') == $article->{id};
577 my ($arg, $acts, $name, $templater) = @_;
578 unless ($arg =~ /^\d+$/) {
579 $acts->{$arg} or die "ENOIMPL\n";
580 $arg = $acts->{$arg} && $templater->perform($acts, $arg, 'id')
583 scalar grep $_->{id} == $arg, @ancestors, $article;
585 ifStepAncestor => [ \&tag_ifStepAncestor, $article ],
586 # access to images, if any
587 $it->make_iterator([ iter_images => $self, \@images ], 'image', 'images', \@iter_images, \$image_index, 'nocache', \$current_image),
588 # override the generated image tag
591 my ($which, $align, $rest) = split ' ', $_[0], 3;
595 if (defined $which && $which =~ /^\d+$/ && $which >=1
596 && $which <= @images) {
597 $im = $images[$which-1];
600 $im = $current_image;
603 return $self->_format_image($im, $align, $rest);
607 my ($arg, $acts, $funcname, $templater) = @_;
608 my ($name, $align, @rest) =
609 DevHelp::Tags->get_parms($arg, $acts, $templater);
612 my ($im) = grep lc $name eq lc $_->{name}, @images
615 $self->_format_image($im, $align, $rest);
617 ifImage => sub { $_[0] >= 1 && $_[0] <= @images },
618 thumbimage => [ tag_thumbimage => $self, \$current_image, \@images ],
623 code => [ iter_files => $self, \@files ],
625 store => \$current_file,
627 filen => [ tag_filen => $self, \@files, \$current_file ],
628 BSE::Util::Tags->make_iterator(\@stepkids, 'stepkid', 'stepkids'),
629 $art_it->make_iterator(undef, 'allkid', 'allkids', \@allkids, \$allkids_index),
630 $art_it->make_iterator(undef, 'stepparent', 'stepparents', \@stepparents),
631 top => [ \&tag_article, $self->{top} || $article, $cfg ],
632 ifDynamic => $dynamic,
633 ifStatic => !$dynamic,
634 ifAccessControlled => [ \&tag_ifAccessControlled, $article ],
638 my $oldurl = $acts{url};
639 my $urlbase = $cfg->entryErr('site', 'url');
642 my $value = $oldurl->(@_);
643 return $value if $value =~ /^<:/; # handle "can't do it"
644 unless ($value =~ /^\w+:/) {
645 # put in the base site url
646 $value = $urlbase . $value;
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/};
659 sub tag_ifStepAncestor {
660 my ($article, $arg, $acts, $name, $templater) = @_;
662 unless ($arg =~ /^\d+$/) {
663 $acts->{$arg} or die "ENOIMPL\n";
664 $arg = $acts->{$arg} && $templater->perform($acts, $arg, 'id')
667 return 0 if $article->{id} < 0;
668 return $article->{id} == $arg || $article->is_step_ancestor($arg);
672 my ($self, $top) = @_;
674 # this is to support pregenerated pages being handled as dynamic pages
675 $self->{force_dynamic} and return 1;
677 UNIVERSAL::isa($top, 'Article') ? $top->is_dynamic : 0;
680 sub tag_ifAccessControlled {
681 my ($article, $arg, $acts, $funcname, $templater) = @_;
685 my $id = $templater->perform($acts, $arg, 'id');
686 $article = Articles->getByPkey($id);
688 print STDERR "** Unknown article $id from $arg in ifAccessControlled\n";
693 print STDERR "** Unknown article $arg in ifAccessControlled\n";
698 return UNIVERSAL::isa($article, 'Article') ?
699 $article->is_access_controlled : 0;
703 my ($self, $image_id, $images) = @_;
706 if ($image_id =~ /^\d+$/) {
707 $image_id >= 1 && $image_id <= @$images
708 or return ( undef, "* Out of range image index '$image_id' *" );
710 $im = $images->[$image_id-1];
712 elsif ($image_id =~ /^[^\W\d]\w*$/) {
713 ($im) = grep $_->{name} eq $image_id, @$images
714 or return ( undef, "* Unknown image identifier '$image_id' *" );
717 return ( undef, "* Unrecognized image '$image_id' *" );
724 my ($self, $image_id, $class, $images) = @_;
726 my ($im, $msg) = $self->get_image($image_id, $images);
730 return $self->do_popimage_low($im, $class);
733 # note: this is called by BSE::Formatter::thumbimage(), update that if
736 my ($self, $geo_id, $image_id, $field, $images, $current) = @_;
739 if ($image_id eq '-' && $current) {
741 or return "** No current image in images iterator **"
744 ($im, my $msg) = $self->get_image($image_id, $images);
749 return $self->_sthumbimage_low($geo_id, $im, $field);
753 my ($self, $article, $articles) = @_;
755 my $html = BSE::Template->get_source($article->{template}, $self->{cfg});
756 $html =~ s/<:\s*embed\s+(?:start|end)\s*:>//g;
758 return $self->generate_low($html, $article, $articles, 0);
762 my ($self, $rindex, $rchildren, $article, $args, $acts,
763 $funcname, $templater) = @_;
765 $self->{admin} or return '';
766 @$rchildren or return '';
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 = '';
773 my $top = $self->{top} || $article;
774 my $refreshto = $ENV{SCRIPT_NAME} . "?id=$top->{id}$urladd";
776 if ($$rindex < $#$rchildren) {
777 $down_url = "$CGI_URI/admin/move.pl?id=$rchildren->[$$rindex]{id}&d=down";
781 $up_url = "$CGI_URI/admin/move.pl?id=$rchildren->[$$rindex]{id}&d=up";
784 return make_arrows($self->{cfg}, $down_url, $up_url, $refreshto, $img_prefix);
788 my ($self, $article_id, $article, @rest) = @_;
790 if ($article_id eq 'article') {
793 elsif ($article_id eq 'children') {
794 return $article->all_visible_kids;
796 elsif ($article_id eq 'parent') {
797 return $article->parent;
800 return $self->SUPER::_find_articles($article_id, $article, @rest);
810 Generate::Article - generates articles.
821 In your HTML each tag will be preceded by <: and followed by :>
823 Tags marked as conditional will require a little more. Conditional
824 tags can be used in two ways:
826 <:ifName args:>true text<:or:>false text<:eif:>
830 <:if Name args:>true text<:or Name:>false text<:eif Name:>
832 Tags starting iterator ... are used as iterators, like:
834 <:iterator begin name:>
836 <:iterator separator name:>
838 <:iterator end name:>
840 In general, a parameter I<which> can be any one of 'article', 'parent'
841 or 'section'. In a child iterator it can also be 'child'. In a
842 crumbs iterator it can also be 'crumbs'. If I<which> is missing it
843 means the current article.
849 =item article I<name>
851 Access to fields of the article. See L<Article attributes>.
855 Access to fields of the parent article. See L<Article attributes>.
859 Conditional tag, true if there is a parent.
861 =item section I<name>
863 Access to the fields of the section containing the article. See
864 L<Article attributes>.
868 The title of the article presented as an image if there is a
869 titleImage or as text. See L<Tag notes> for values for which.
871 =item ifTitleImage I<which>
873 Conditional tag, true if the given article has a titleImage,
875 =item thumbnail I<which> I<class>
877 The thumbnail image as an <img> tag for object I<which> where I<which>
878 is one of the article objects defined. The optional I<class> adds a
879 class attribute to the tag with that class.
881 =item ifThumbnail I<which>
883 Conditional tag, true if the article specified by I<which> has a
886 =item ifUnderThreshold
888 =item ifUnderThreshold stepkids
890 =item ifUnderThreshold allkids
892 Conditional tag, true if the number of children/stepkids/allkids is
893 less or equal to the article's threshold.
897 The formatted body of the article.
903 =item iterator ... crumbs [option]
905 Iterates over the ancestors of the article. See the L</item crumbs>.
907 I<option> can be empty, "listedonly" or "showtop". If empty the
908 display of an unlisted level1 ancestor is controlled by
909 $UNLISTED_LEVEL1_IN_CRUMBS, if "listedonly" then an unlisted level1
910 article isn't shown in the crumbs, and it is if "showtop" is the
911 I<option>. This can be used in <: ifCrumbs :> too.
915 Access to the fields of the specific ancestor. I<name> can be any of
916 the L<Article attributes>.
918 =item ifCrumbs [options]
920 Conditional tag, true if there are any crumbs.
922 See L</iterator ... crumbs [option]> for information on I<option>.
926 Conditional tag, true if the article has any children.
928 =item iterator ... children
930 Iterates over the children of the article. See the L</item child>.
934 Access to the fields of the current child.
938 Produces a processed summary of the current child's body.
942 Conditional tag, true if there is a previous child. Originally used
943 for generating a move up link, but you can use the moveUp tag for
948 Conditional tag, true if there is a next child. Originally used to
949 generating a move down link, but you can use the moveDown tag for that
952 =item ifCurrentPage I<which>
954 Conditional tag, true if the given I<which> is the page currently
955 being generated. This can be used to apply special formatting if a
956 C<level1> or C<level2> article is the current page.
958 =item iterator ... images
960 Iterates over the unnamed images for the given article.
962 =item iterator ... images all
964 Iterates over all images for the article.
966 =item iterator ... images named
968 Iterates over the named images for the article.
970 =item iterator ... images named /regexp/
972 Iterates over images with names matching the given regular expression.
973 Note that if the expression matches an empty string then unnamed
974 images will be included.
976 =item image which field
978 Extracts the given field from the specified image.
980 I<which> in this can be either an image index to access a specific
981 image, or "-" to access the current image in the images iterator.
983 The image fields are:
989 The identifier of the article the image belongs to.
993 A partial url of the image, relative to /images/.
997 Alternative text of the image.
1003 dimensions of the image.
1007 the url if any associated with the image
1011 =item image which align
1017 Produces HTML to render the given image.
1019 I<which> can be an image index (1, 2, 3...) to select one of the
1020 images from the current article, or '-' or omitted to select the
1021 current image from the images iterator. If align is present then the
1022 C<align> attribute of the image is set.
1024 If the image has a URL that <a href="...">...</a> will also be
1027 =item ifImage imageindex
1029 Condition tag, true if an image exists at the given index.
1035 Conditional tag, true if the article has any images.
1037 =item ifImages named
1039 Conditional tag, true if the article has any named images.
1041 =item ifImages named /regexp/
1043 Conditional tag, true if the article has any named images, where the
1044 name matches the regular expression.
1046 =item ifImages unnamed
1048 Conditional tag, true if the article has any unnamed images.
1052 This has been made more general and been moved, see L<Generate/embed child>.
1056 Tests if the article is dynamically generated.
1060 Toplevel article being generated. This is the page that any articles
1061 are being embedded in.
1063 =item iterator ... files
1065 Iterates over the files attached to the article, setting the file tag.
1069 Information from the current file in the files iterator.
1071 The file fields are:
1077 id - identifier for this file
1081 articleId - article this file belongs to
1085 displayName - the filename of the file as displayed
1089 filename - filename of the file as stored on disk,
1093 sizeInBytes - size of the file in bytes.
1097 description - the entered description of the file
1101 contentType - the MIME content type of the file
1105 displayOrder - number used to control the listing order.
1109 forSale - non-zero if the file needs to be paid for to be downloaded.
1113 download - if this is non-zero BSE will attempt to make the browser
1114 download the file rather than display it.
1118 whenUploaded - date/time when the file was uploaded.
1122 requireUser - if non-zero the user must be logged on to download this
1127 notes - longer descriptive text.
1131 name - identifier for the file for filelink[]
1135 hide_from_list - if non-zero the file won't be listed by the files
1136 iterator, but will still be available to filelink[].
1142 =head2 Article attributes
1148 Identifies the article.
1152 The identifier of the parent article.
1156 The title of the article. See the title tag
1160 The name of the title image for the article, if any. See the title
1161 and ifTitleImage tags.
1165 The body of the article. See the body tag.
1173 The thumbnail image for the article, if any. See the thumbnail tag.
1179 The release and expiry dates of the article.
1183 Any keywords for the article. Indexed by the search engine.
1189 Links to the normal and adminstrative versions of the article. The
1190 url tag defined by Generate.pm will select the appropriate tag for the
1195 The maximum number of articles that should be embeded into the current
1196 article for display. See the ifUnderThreshold tag.
1200 The maximum amount of text displayed in the summary of an article.
1201 See the summary tag.
1205 The class used to generate the article. Should be one of
1206 Generate::Article, Generate::Catalog or Generate::Product.
1210 The level of the article. Sections are level1, etc
1214 How the article is listed. If zero then the article can only be found
1215 in a search. If 1 then the article is listed in menus and article
1216 contents, if 2 then the article is only listed in article contents.
1220 When the article was last modified. Currently only used for display
1223 =item lastModifiedBy
1225 Set the the current admin user logon if access_control is enabled in the cfg.
1229 Set to the current date time when a new article is created.
1233 Set to the current admin user logon if access_control is enabled in
1238 A user definable field for attributing the article author.
1242 An alternate article title which can be used to make search engine
1245 =item metaDescription
1247 Article metadata description, used as metadata in the generated HTML
1252 Article metadata keywords, used as metadata in the generated HTML
1257 The following attributes are unlikely to be used in a page:
1263 Used internally to control the ordering of articles within a section.
1267 The position of the first image in the body. The body tag will format
1268 images into the body as specified by this tag.
1272 The template used to format the article.
1276 Flags which can be checked by code or template tags to control behaviours
1277 specific the the article.
1281 Forces a page to be displayed dynamically with page.pl regardless of
1284 =item inherit_siteuser_rights
1286 Controls whether the article inherits its parents access controls.
1294 The following tags produce output only in admin mode.
1298 Produces buttons and links used for administering the article.
1302 Generates a move up link if there is a previous child for the current
1307 Generates a move down link if there is a next child for the current child.
1311 Produces buttons and links used for administering the article.
1313 This tag can use a specialized template if it's available. If you
1314 call it with a parameter, <:admin I<template>:> then it will use
1315 template C<< admin/adminmenu/I<template>.tmpl >>. When used in an
1316 article 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:> >>
1319 behaves like C<< <:admin product:> >>. See L<Admin Menu Templates>
1320 for the tags available in admin menu templates.
1322 If the template doesn't exist then the old behaviour applies.
1326 =head2 Admin Menu Templates
1328 These tags can be used in the templates included by the C<< <:admin:>
1331 The basic template tags and ifUserCan tag can be used in admin menu
1338 Retrieves a field from the current article.
1342 Retrieves a field from the parent of the current article.
1346 Conditional tag, true if the current article has a parent.
1350 Conditional tag, true if the current article is embedded in another
1351 article, in this context.