1 package Generate::Article;
4 use Constants qw(%LEVEL_DEFAULTS $CGI_URI $ADMIN_URI $IMAGES_URI
5 $UNLISTED_LEVEL1_IN_CRUMBS);
9 use Util qw(generate_button);
10 use BSE::Util::Tags qw(tag_article);
16 use BSE::Util::Iterate;
18 my $excerptSize = 300;
20 my %level_names = map { $_, $LEVEL_DEFAULTS{$_}{display} }
21 grep { $LEVEL_DEFAULTS{$_}{display} } keys %LEVEL_DEFAULTS;
24 my ($class, %opts) = @_;
26 $opts{top} or confess "Please supply 'top' to $class->new";
28 return $class->SUPER::new(%opts);
33 return "$CGI_URI/admin/add.pl?id=$id";
37 my ($self, $link, $text, $target) = @_;
39 my ($url, $query) = split /\?/, $link;
40 my $form = qq!<form action="$url"!;
41 $form .= qq! target="$target"! if $target;
43 if (defined $query && length $query) {
44 for my $attr (split /&/, $query) {
45 my ($name, $value) = split /=/, $attr, 2;
46 # I'm assuming none of the values are uri escaped
47 $value = escape_html($value);
48 $form .= qq!<input type=hidden name="$name" value="$value" />!
51 $form .= qq!<input type=submit value="!.escape_html($text).'" />';
58 my ($self, $template, $article, $articles, $embedded) = @_;
60 %acts = $self -> baseActs($articles, \%acts, $article, $embedded);
62 my $page = BSE::Template->replace($template, $self->{cfg}, \%acts);
64 %acts = (); # try to destroy any circular refs
70 my ($cfg, $article, $images, $args, $acts, $funcname, $templater) = @_;
72 my $which = $args || 'article';
74 exists $acts->{$which}
75 or return "** no such object $which **";
77 my $title = $templater->perform($acts, $which, 'title');
78 my $imagename = $which eq 'article' ? $article->{titleImage} :
79 $templater->perform($acts, $which, 'titleImage');
80 my $xhtml = $cfg->entry("basic", "xhtml", 1);
82 my $html = qq!<img src="/images/titles/$imagename"!;
83 $html .= ' border="0"' unless $xhtml;
84 $html .= qq! class="bse_image_title" alt="$title" />!;
87 if ($which eq 'article') {
88 ($im) = grep lc $_->{name} eq 'bse_title', @$images;
91 my $id = $templater->perform($acts, $which, 'id');
93 my @images = Images->getBy(articleId=>$id);
94 ($im) = grep lc $_->{name} eq 'bse_title', @$images;
98 my $src = $im->{src} || "/images/$im->{image}";
99 $src = escape_html($src);
100 return qq!<img src="$src" width="$im->{width}"!
101 . qq! height="$im->{height}" alt="$title" class="bse_image_title" />!;
109 my ($self, $article, $embedded) = @_;
111 my $req = $self->{request};
114 <td><form action="$CGI_URI/admin/add.pl" name="edit">
115 <input type=submit value="Edit $level_names{$article->{level}}">
116 <input type=hidden name=id value="$article->{id}">
118 <td><form action="$ADMIN_URI">
119 <input type=submit value="Admin menu">
122 if (exists $level_names{1+$article->{level}}
123 && $req->user_can(edit_add_child=>$article)) {
125 <td><form action="$CGI_URI/admin/add.pl" name="addchild">
126 <input type=submit value="Add $level_names{1+$article->{level}}">
127 <input type=hidden name=parentid value="$article->{id}">
131 if (generate_button() && $req->user_can(regen_article=>$article)) {
133 <td><form action="$CGI_URI/admin/generate.pl" name="regen">
134 <input type=hidden name=id value="$article->{id}">
135 <input type=submit value="Regenerate">
139 $html .= "<td>".$self->link_to_form($article->{admin}."&admin=0",
140 "Display", "_blank")."</td>";
141 my $parent = $article->parent;
142 if ($article->{link}) {
144 . $self->link_to_form($article->{link}, "On site", "_blank")
146 } elsif ($parent && $parent->{link}) {
148 . $self->link_to_form($parent->{link}, "On site", "_blank")
151 if ($parent && $parent->{admin} ne $article->{admin} && !$embedded) {
153 .$self->link_to_form($parent->{admin}, "Parent")."</td>";
162 my ($self, $article) = @_;
164 my $top = $self->{top} || $article;
166 $article->{link} =~ /^\w+:/ || $top->{link} =~ /^\w+:/;
170 my ($self, $article, $default, $embedded, $arg) = @_;
172 $self->{admin} or return '';
173 $self->{request} or return '';
174 my $cfg = $self->{cfg};
176 my $name = $arg || $default;
177 my $template = "admin/adminmenu/$name";
179 unless (BSE::Template->find_source($template, $cfg)) {
180 return $self->_default_admin($article, $embedded);
183 my $parent = $article->parent;
187 BSE::Util::Tags->static(\%acts, $cfg),
188 BSE::Util::Tags->admin(\%acts, $cfg),
189 BSE::Util::Tags->secure($self->{request}),
190 article => [ \&tag_article, $article, $cfg ],
191 parent => [ \&tag_article, $parent, $cfg ],
193 ifEmbedded => $embedded,
196 return BSE::Template->get_page($template, $cfg, \%acts);
200 my ($self, $rcurrent, $images, $args, $acts, $funcname, $templater) = @_;
202 my ($geometry_id, $id, $field) =
203 DevHelp::Tags->get_parms($args, $acts, $templater);
205 return $self->do_thumbimage($geometry_id, $id, $field, $images, $$rcurrent);
209 my ($self, $images, $arg) = @_;
214 elsif ($arg eq 'named') {
215 return grep $_->{name} ne '', @$images;
217 elsif ($arg =~ m!^named\s+/([^/]+)/$!) {
219 return grep $_->{name} =~ /$re/i, @$images;
222 return grep $_->{name} eq '', @$images;
228 =item filen name field
230 Reference an article attached file by name.
232 C<filen name> will display a link to the file.
234 C<<filen name I<field> >> will display the given field from the file
235 record. A I<field> of C<url> will be a URL to the file.
237 If the file identifier given doesn't exist for the current article the
238 empty string is returned, allowing use as ifFilen.
240 The result is unspecified if the I<field> specified isn't one of the
241 image record field names and isn't C<url>.
246 my ($self, $files, $arg, $acts, $funcname, $templater) = @_;
248 my ($name, $field, @rest) =
249 DevHelp::Tags->get_parms($arg, $acts, $templater);
252 or return '* name cannot be an empty string *';
254 my ($file) = grep $_->{name} eq $name, @$files
257 return $self->_format_file($file, $field);
260 =item iterator: crumbs/crumb
262 Iterators over the ancestor tree from the article parent to the root.
270 showtop - the top level article is included even if unlisted
274 listedonly - only listed articles in the tree are included
278 The default depends on the value of $Constants::UNLISTED_LEVEL1_IN_CRUMBS.
283 my ($self, $crumbs, $args) = @_;
285 $args ||= $UNLISTED_LEVEL1_IN_CRUMBS ? 'showtop' : 'listedonly';
286 if ($args eq 'showtop') {
290 return grep $_->{listed}, @$crumbs;
295 my ($self, $articles, $acts, $article, $embedded) = @_;
297 my $cfg = $self->{cfg} || BSE::Cfg->new;
299 # used to generate the list (or not) of children to this article
300 my $child_index = -1;
301 my @children = $articles->listedChildren($article->{id});
303 # used to generate a navigation list for the article
304 # generate a list of ancester articles/sections
305 # Jason calls these breadcrumbs
309 while ($temp->{parentid} > 0
310 and my $crumb = $articles->getByPkey($temp->{parentid})) {
311 unshift(@ancestors, $crumb);
312 unshift(@crumbs, $crumb) if $crumb->{listed} == 1 || $crumb->{level} == 1;
315 #my $crumb_index = -1;
316 #my @work_crumbs; # set by the crumbs iterator
319 my $parent = $articles->getByPkey($article->{parentid});
320 my $section = @crumbs ? $crumbs[0] : $article;
322 my @images = Images->getBy('articleId', $article->{id});
323 my @unnamed_images = grep $_->{name} eq '', @images;
325 my $image_index = -1;
326 my $had_image_tags = 0;
327 my @all_files = sort { $b->{displayOrder} <=> $a->{displayOrder} }
328 ArticleFiles->getBy(articleId=>$article->{id});
329 my @files = grep !$_->{hide_from_list}, @all_files;
331 my $blank = qq!<img src="$IMAGES_URI/trans_pixel.gif" width="17" height="13" border="0" align="absbottom" alt="" />!;
333 my $top = $self->{top} || $article;
334 my $abs_urls = $self->abs_urls($article);
336 my $dynamic = $self->{force_dynamic}
337 || (UNIVERSAL::isa($top, 'Article') ? $top->is_dynamic : 0);
342 if (UNIVERSAL::isa($article, 'Article')) {
343 @stepkids = $article->visible_stepkids;
344 @allkids = $article->all_visible_kids;
345 @stepparents = $article->visible_step_parents;
349 my $art_it = BSE::Util::Iterate::Article->new(cfg =>$cfg, admin => $self->{admin}, top => $self->{top});
350 my $it = BSE::Util::Iterate->new;
351 # separate these so the closures can see %acts
354 $self->SUPER::baseActs($articles, $acts, $article, $embedded),
355 article=>[ \&tag_article, $article, $cfg ],
358 my $which = shift || 'article';
359 return $acts->{$which} && $acts->{$which}->('titleImage')
361 title => [ \&tag_title, $cfg, $article, \@images ],
364 my ($args, $acts, $name, $templater) = @_;
365 my ($which, $class) = split ' ', $args;
366 $which ||= 'article';
367 if ($acts->{$which} &&
368 (my $image = $templater->perform($acts, $which, 'thumbImage'))) {
369 my $width = $templater->perform($acts, $which, 'thumbWidth');
370 my $height = $templater->perform($acts, $which, 'thumbHeight');
371 my $result = '<img src="/images/'.$image
373 .'" height="'.$height.'"';
374 $result .= qq! class="$class"! if $class;
375 $result .= ' border="0" alt="" />';
384 my ($which, $acts, $name, $templater) = @_;
385 $which ||= 'article';
386 return $acts->{$which} &&
387 $templater->perform($acts, $which, 'thumbImage');
392 my $what = $_[0] || '';
393 if ($what eq 'stepkids') {
396 elsif ($what eq 'allkids') {
402 $count <= $article->{threshold};
404 ifChildren => sub { scalar @children },
405 iterate_children_reset => sub { $child_index = -1; },
408 return ++$child_index < @children;
412 return tag_article($children[$child_index], $cfg, $_[0]);
415 section=> [ \&tag_article, $section, $cfg ],
417 # these are mostly obsolete, use moveUp and moveDown instead
419 ifPrevChild => sub { $child_index > 0 },
420 ifNextChild => sub { $child_index < $#children },
422 # generate buttons for administration (only for admin generation)
423 admin=> [ tag_admin=>$self, $article, 'article', $embedded ],
425 # transform the article or response body (entities, images)
427 my ($args, $acts, $funcname, $templater) = @_;
428 return $self->format_body(acts => $acts,
429 article => $articles,
430 text => $article->{body},
431 imagepos => $article->{imagePos},
432 abs_urls => $abs_urls,
433 auto_images => !$had_image_tags,
434 templater => $templater,
436 files => \@all_files,
437 articles => $articles);
440 # used to display a navigation path of parent sections
441 $art_it->make_iterator([ iter_crumbs => $self, \@crumbs ],
442 'crumb', 'crumbs', undef, undef,
443 'nocache', \$current_crumb),
447 $cfg->entry('basic', 'warn_obsolete', 0)
448 and print STDERR "* crumbs tag obsolete *\n";
449 return tag_article($current_crumb, $cfg, $_[0]);
453 ifParent => sub { $parent },
455 sub { return $parent && tag_article($parent, $cfg, $_[0]) },
456 # for rearranging order in admin mode
459 @children > 1 or return '';
460 if ($self->{admin} && $child_index < $#children) {
462 <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>
472 @children > 1 or return '';
473 if ($self->{admin} && $child_index > 0) {
475 <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>
483 movekid => [ \&tag_movekid, $self, \$child_index, \@children, $article ],
486 my ($arg, $acts, $funcname, $templater) = @_;
488 return '' unless $self->{admin};
489 return '' unless @allkids > 1;
490 defined $allkids_index && $allkids_index >= 0 && $allkids_index < @allkids
491 or return '** movestepkid must be inside iterator allkids **';
492 my ($img_prefix, $urladd) =
493 DevHelp::Tags->get_parms($arg, $acts, $templater);
494 $img_prefix = '' unless defined $img_prefix;
495 $urladd = '' unless defined $urladd;
496 my $top = $self->{top} || $article;
497 my $refreshto = $ENV{SCRIPT_NAME} . "?id=$top->{id}$urladd";
499 if ($allkids_index < $#allkids) {
500 $down_url = "$CGI_URI/admin/move.pl?stepparent=$article->{id}&d=swap&id=$allkids[$allkids_index]{id}&other=$allkids[$allkids_index+1]{id}";
503 if ($allkids_index > 0) {
504 $up_url = "$CGI_URI/admin/move.pl?stepparent=$article->{id}&d=swap&id=$allkids[$allkids_index]{id}&other=$allkids[$allkids_index-1]{id}";
507 return make_arrows($self->{cfg}, $down_url, $up_url, $refreshto, $img_prefix);
512 $arg && $acts->{$arg} && $acts->{$arg}->('id') == $article->{id};
516 my ($arg, $acts, $name, $templater) = @_;
517 unless ($arg =~ /^\d+$/) {
518 $acts->{$arg} or die "ENOIMPL\n";
519 $arg = $acts->{$arg} && $templater->perform($acts, $arg, 'id')
522 scalar grep $_->{id} == $arg, @ancestors, $article;
524 ifStepAncestor => [ \&tag_ifStepAncestor, $article ],
525 # access to images, if any
526 $it->make_iterator([ iter_images => $self, \@images ], 'image', 'images', \@iter_images, \$image_index, 'nocache', \$current_image),
527 # override the generated image tag
530 my ($which, $align, $rest) = split ' ', $_[0], 3;
534 if (defined $which && $which =~ /^\d+$/ && $which >=1
535 && $which <= @images) {
536 $im = $images[$which-1];
539 $im = $current_image;
542 return $self->_format_image($im, $align, $rest);
546 my ($arg, $acts, $funcname, $templater) = @_;
547 my ($name, $align, @rest) =
548 DevHelp::Tags->get_parms($arg, $acts, $templater);
551 my ($im) = grep lc $name eq lc $_->{name}, @images
554 $self->_format_image($im, $align, $rest);
556 ifImage => sub { $_[0] >= 1 && $_[0] <= @images },
557 thumbimage => [ tag_thumbimage => $self, \$current_image, \@images ],
558 BSE::Util::Tags->make_iterator(\@files, 'file', 'files'),
559 filen => [ tag_filen => $self, \@files ],
560 BSE::Util::Tags->make_iterator(\@stepkids, 'stepkid', 'stepkids'),
561 $art_it->make_iterator(undef, 'allkid', 'allkids', \@allkids, \$allkids_index),
562 $art_it->make_iterator(undef, 'stepparent', 'stepparents', \@stepparents),
563 top => [ \&tag_article, $self->{top} || $article, $cfg ],
564 ifDynamic => $dynamic,
565 ifStatic => !$dynamic,
566 ifAccessControlled => [ \&tag_ifAccessControlled, $article ],
570 my $oldurl = $acts{url};
571 my $urlbase = $cfg->entryErr('site', 'url');
574 my $value = $oldurl->(@_);
575 return $value if $value =~ /^<:/; # handle "can't do it"
576 unless ($value =~ /^\w+:/) {
577 # put in the base site url
578 $value = $urlbase . $value;
583 if ($dynamic && $cfg->entry('basic', 'ajax', 0)) {
584 # make sure the ajax tags are left until we do dynamic replacement
585 delete @acts{qw/ajax ifAjax/};
591 sub tag_ifStepAncestor {
592 my ($article, $arg, $acts, $name, $templater) = @_;
594 unless ($arg =~ /^\d+$/) {
595 $acts->{$arg} or die "ENOIMPL\n";
596 $arg = $acts->{$arg} && $templater->perform($acts, $arg, 'id')
599 return 0 if $article->{id} < 0;
600 return $article->{id} == $arg || $article->is_step_ancestor($arg);
604 my ($self, $top) = @_;
606 # this is to support pregenerated pages being handled as dynamic pages
607 $self->{force_dynamic} and return 1;
609 UNIVERSAL::isa($top, 'Article') ? $top->is_dynamic : 0;
612 sub tag_ifAccessControlled {
613 my ($article, $arg, $acts, $funcname, $templater) = @_;
617 my $id = $templater->perform($acts, $arg, 'id');
618 $article = Articles->getByPkey($id);
620 print STDERR "** Unknown article $id from $arg in ifAccessControlled\n";
625 print STDERR "** Unknown article $arg in ifAccessControlled\n";
630 return UNIVERSAL::isa($article, 'Article') ?
631 $article->is_access_controlled : 0;
635 my ($self, $image_id, $images) = @_;
638 if ($image_id =~ /^\d+$/) {
639 $image_id >= 1 && $image_id <= @$images
640 or return ( undef, "* Out of range image index '$image_id' *" );
642 $im = $images->[$image_id-1];
644 elsif ($image_id =~ /^[^\W\d]\w*$/) {
645 ($im) = grep $_->{name} eq $image_id, @$images
646 or return ( undef, "* Unknown image identifier '$image_id' *" );
649 return ( undef, "* Unrecognized image '$image_id' *" );
656 my ($self, $image_id, $class, $images) = @_;
658 my ($im, $msg) = $self->get_image($image_id, $images);
662 return $self->do_popimage_low($im, $class);
665 # note: this is called by BSE::Formatter::thumbimage(), update that if
668 my ($self, $geo_id, $image_id, $field, $images, $current) = @_;
671 if ($image_id eq '-' && $current) {
673 or return "** No current image in images iterator **"
676 ($im, my $msg) = $self->get_image($image_id, $images);
681 return $self->_sthumbimage_low($geo_id, $im, $field);
685 my ($self, $article, $articles) = @_;
687 my $html = BSE::Template->get_source($article->{template}, $self->{cfg});
688 $html =~ s/<:\s*embed\s+(?:start|end)\s*:>//g;
690 return $self->generate_low($html, $article, $articles, 0);
694 my ($self, $rindex, $rchildren, $article, $args, $acts,
695 $funcname, $templater) = @_;
697 $self->{admin} or return '';
698 @$rchildren or return '';
700 my ($img_prefix, $urladd) =
701 DevHelp::Tags->get_parms($args, $acts, $templater);
702 defined $img_prefix or $img_prefix = '';
703 defined $urladd or $urladd = '';
705 my $top = $self->{top} || $article;
706 my $refreshto = $ENV{SCRIPT_NAME} . "?id=$top->{id}$urladd";
708 if ($$rindex < $#$rchildren) {
709 $down_url = "$CGI_URI/admin/move.pl?id=$rchildren->[$$rindex]{id}&d=down";
713 $up_url = "$CGI_URI/admin/move.pl?id=$rchildren->[$$rindex]{id}&d=up";
716 return make_arrows($self->{cfg}, $down_url, $up_url, $refreshto, $img_prefix);
720 my ($self, $article_id, $article, @rest) = @_;
722 if ($article_id eq 'article') {
725 elsif ($article_id eq 'children') {
726 return $article->all_visible_kids;
728 elsif ($article_id eq 'parent') {
729 return $article->parent;
732 return $self->SUPER::_find_articles($article_id, $article, @rest);
742 Generate::Article - generates articles.
753 In your HTML each tag will be preceded by <: and followed by :>
755 Tags marked as conditional will require a little more. Conditional
756 tags can be used in two ways:
758 <:ifName args:>true text<:or:>false text<:eif:>
762 <:if Name args:>true text<:or Name:>false text<:eif Name:>
764 Tags starting iterator ... are used as iterators, like:
766 <:iterator begin name:>
768 <:iterator separator name:>
770 <:iterator end name:>
772 In general, a parameter I<which> can be any one of 'article', 'parent'
773 or 'section'. In a child iterator it can also be 'child'. In a
774 crumbs iterator it can also be 'crumbs'. If I<which> is missing it
775 means the current article.
781 =item article I<name>
783 Access to fields of the article. See L<Article attributes>.
787 Access to fields of the parent article. See L<Article attributes>.
791 Conditional tag, true if there is a parent.
793 =item section I<name>
795 Access to the fields of the section containing the article. See
796 L<Article attributes>.
800 The title of the article presented as an image if there is a
801 titleImage or as text. See L<Tag notes> for values for which.
803 =item ifTitleImage I<which>
805 Conditional tag, true if the given article has a titleImage,
807 =item thumbnail I<which> I<class>
809 The thumbnail image as an <img> tag for object I<which> where I<which>
810 is one of the article objects defined. The optional I<class> adds a
811 class attribute to the tag with that class.
813 =item ifThumbnail I<which>
815 Conditional tag, true if the article specified by I<which> has a
818 =item ifUnderThreshold
820 =item ifUnderThreshold stepkids
822 =item ifUnderThreshold allkids
824 Conditional tag, true if the number of children/stepkids/allkids is
825 less or equal to the article's threshold.
829 The formatted body of the article.
835 =item iterator ... crumbs [option]
837 Iterates over the ancestors of the article. See the L</item crumbs>.
839 I<option> can be empty, "listedonly" or "showtop". If empty the
840 display of an unlisted level1 ancestor is controlled by
841 $UNLISTED_LEVEL1_IN_CRUMBS, if "listedonly" then an unlisted level1
842 article isn't shown in the crumbs, and it is if "showtop" is the
843 I<option>. This can be used in <: ifCrumbs :> too.
847 Access to the fields of the specific ancestor. I<name> can be any of
848 the L<Article attributes>.
850 =item ifCrumbs [options]
852 Conditional tag, true if there are any crumbs.
854 See L</iterator ... crumbs [option]> for information on I<option>.
858 Conditional tag, true if the article has any children.
860 =item iterator ... children
862 Iterates over the children of the article. See the L</item child>.
866 Access to the fields of the current child.
870 Produces a processed summary of the current child's body.
874 Conditional tag, true if there is a previous child. Originally used
875 for generating a move up link, but you can use the moveUp tag for
880 Conditional tag, true if there is a next child. Originally used to
881 generating a move down link, but you can use the moveDown tag for that
884 =item ifCurrentPage I<which>
886 Conditional tag, true if the given I<which> is the page currently
887 being generated. This can be used to apply special formatting if a
888 C<level1> or C<level2> article is the current page.
890 =item iterator ... images
892 Iterates over the unnamed images for the given article.
894 =item iterator ... images all
896 Iterates over all images for the article.
898 =item iterator ... images named
900 Iterates over the named images for the article.
902 =item iterator ... images named /regexp/
904 Iterates over images with names matching the given regular expression.
905 Note that if the expression matches an empty string then unnamed
906 images will be included.
908 =item image which field
910 Extracts the given field from the specified image.
912 I<which> in this can be either an image index to access a specific
913 image, or "-" to access the current image in the images iterator.
915 The image fields are:
921 The identifier of the article the image belongs to.
925 A partial url of the image, relative to /images/.
929 Alternative text of the image.
935 dimensions of the image.
939 the url if any associated with the image
943 =item image which align
949 Produces HTML to render the given image.
951 I<which> can be an image index (1, 2, 3...) to select one of the
952 images from the current article, or '-' or omitted to select the
953 current image from the images iterator. If align is present then the
954 C<align> attribute of the image is set.
956 If the image has a URL that <a href="...">...</a> will also be
959 =item ifImage imageindex
961 Condition tag, true if an image exists at the given index.
967 Conditional tag, true if the article has any images.
971 Conditional tag, true if the article has any named images.
973 =item ifImages named /regexp/
975 Conditional tag, true if the article has any named images, where the
976 name matches the regular expression.
978 =item ifImages unnamed
980 Conditional tag, true if the article has any unnamed images.
984 This has been made more general and been moved, see L<Generate/embed child>.
988 Tests if the article is dynamically generated.
992 Toplevel article being generated. This is the page that any articles
993 are being embedded in.
995 =item iterator ... files
997 Iterates over the files attached to the article, setting the file tag.
1001 Information from the current file in the files iterator.
1003 The file fields are:
1009 id - identifier for this file
1013 articleId - article this file belongs to
1017 displayName - the filename of the file as displayed
1021 filename - filename of the file as stored on disk,
1025 sizeInBytes - size of the file in bytes.
1029 description - the entered description of the file
1033 contentType - the MIME content type of the file
1037 displayOrder - number used to control the listing order.
1041 forSale - non-zero if the file needs to be paid for to be downloaded.
1045 download - if this is non-zero BSE will attempt to make the browser
1046 download the file rather than display it.
1050 whenUploaded - date/time when the file was uploaded.
1054 requireUser - if non-zero the user must be logged on to download this
1059 notes - longer descriptive text.
1063 name - identifier for the file for filelink[]
1067 hide_from_list - if non-zero the file won't be listed by the files
1068 iterator, but will still be available to filelink[].
1074 =head2 Article attributes
1080 Identifies the article.
1084 The identifier of the parent article.
1088 The title of the article. See the title tag
1092 The name of the title image for the article, if any. See the title
1093 and ifTitleImage tags.
1097 The body of the article. See the body tag.
1105 The thumbnail image for the article, if any. See the thumbnail tag.
1111 The release and expiry dates of the article.
1115 Any keywords for the article. Indexed by the search engine.
1121 Links to the normal and adminstrative versions of the article. The
1122 url tag defined by Generate.pm will select the appropriate tag for the
1127 The maximum number of articles that should be embeded into the current
1128 article for display. See the ifUnderThreshold tag.
1132 The maximum amount of text displayed in the summary of an article.
1133 See the summary tag.
1137 The class used to generate the article. Should be one of
1138 Generate::Article, Generate::Catalog or Generate::Product.
1142 The level of the article. Sections are level1, etc
1146 How the article is listed. If zero then the article can only be found
1147 in a search. If 1 then the article is listed in menus and article
1148 contents, if 2 then the article is only listed in article contents.
1152 When the article was last modified. Currently only used for display
1155 =item lastModifiedBy
1157 Set the the current admin user logon if access_control is enabled in the cfg.
1161 Set to the current date time when a new article is created.
1165 Set to the current admin user logon if access_control is enabled in
1170 A user definable field for attributing the article author.
1174 An alternate article title which can be used to make search engine
1177 =item metaDescription
1179 Article metadata description, used as metadata in the generated HTML
1184 Article metadata keywords, used as metadata in the generated HTML
1189 The following attributes are unlikely to be used in a page:
1195 Used internally to control the ordering of articles within a section.
1199 The position of the first image in the body. The body tag will format
1200 images into the body as specified by this tag.
1204 The template used to format the article.
1208 Flags which can be checked by code or template tags to control behaviours
1209 specific the the article.
1213 Forces a page to be displayed dynamically with page.pl regardless of
1216 =item inherit_siteuser_rights
1218 Controls whether the article inherits its parents access controls.
1226 The following tags produce output only in admin mode.
1230 Produces buttons and links used for administering the article.
1234 Generates a move up link if there is a previous child for the current
1239 Generates a move down link if there is a next child for the current child.
1243 Produces buttons and links used for administering the article.
1245 This tag can use a specialized template if it's available. If you
1246 call it with a parameter, <:admin I<template>:> then it will use
1247 template C<< admin/adminmenu/I<template>.tmpl >>. When used in an
1248 article template C<< <:admin:> >> behaves like C<< <:admin article:>
1249 >>, when used in a catalog template C<< <:admin:> >> behaves like C<<
1250 <:admin catalog:> >>, when used in a product template C<< <:admin:> >>
1251 behaves like C<< <:admin product:> >>. See L<Admin Menu Templates>
1252 for the tags available in admin menu templates.
1254 If the template doesn't exist then the old behaviour applies.
1258 =head2 Admin Menu Templates
1260 These tags can be used in the templates included by the C<< <:admin:>
1263 The basic template tags and ifUserCan tag can be used in admin menu
1270 Retrieves a field from the current article.
1274 Retrieves a field from the parent of the current article.
1278 Conditional tag, true if the current article has a parent.
1282 Conditional tag, true if the current article is embedded in another
1283 article, in this context.