allow use of the new template system from static pages
[bse.git] / t / t20gen.t
CommitLineData
7928764a
TC
1#!perl -w
2use strict;
3use BSE::Test ();
0316d3da 4use Test::More tests=>147;
7928764a
TC
5use File::Spec;
6use FindBin;
810ce080
TC
7BEGIN {
8 my $cgidir = File::Spec->catdir(BSE::Test::base_dir, 'cgi-bin');
9 ok(chdir $cgidir, "switch to CGI directory");
10 push @INC, 'modules';
11}
12use BSE::API qw(bse_init bse_cfg bse_make_article);
13
14bse_init(".");
15
16my $cfg = bse_cfg();
17
18use BSE::Util::SQL qw/sql_datetime/;
19use DevHelp::Date qw(dh_strftime_sql_datetime);
20
7928764a 21sub template_test($$$$);
70789617 22sub dyn_template_test($$$$);
7928764a 23
fd1b40b1
TC
24my $parent = add_article
25 (
26 title=>'Parent',
27 body=>'parent article doclink[shop|foo]',
28 lastModified => '2004-09-23 06:00:00',
29 threshold => 2,
30 );
7928764a
TC
31ok($parent, "create section");
32my @kids;
33for my $name ('One', 'Two', 'Three') {
48c6e0b6
TC
34 my $kid = add_article
35 (
36 title => $name, parentid => $parent->{id},
37 body => "b[$name] - alpha, beta, gamma, delta, epsilon",
fd1b40b1 38 summaryLength => 35,
48c6e0b6 39 );
7928764a
TC
40 ok($kid, "creating kid $name");
41 push(@kids, $kid);
42}
43
1d9a2f7f
TC
44my $grandkid = add_article
45 (
46 parentid => $kids[1]{id},
47 title => "Grandkid",
48 body => "grandkid",
49 );
50
f40af7e2
TC
51my $base_securl = $cfg->entryVar("site", "secureurl");
52
aefcabcb
TC
53# make parent a step child of itself
54require BSE::Admin::StepParents;
55BSE::Admin::StepParents->add($parent, $parent);
56
ff9130e4
TC
57is($parent->section->{id}, $parent->{id}, "parent should be it's own section");
58is($kids[0]->section->{id}, $parent->{id}, "kids section should be the parent");
59
7928764a
TC
60my $top = Articles->getByPkey(1);
61ok($top, "grabbing Home page");
62
05c4170c
TC
63template_test "cfg", $top, <<TEMPLATE, <<EXPECTED;
64<:cfg "no such section" somekey "default / value":>
65TEMPLATE
66default / value
67EXPECTED
68
77bd0dba
TC
69template_test "formats", $top, <<TEMPLATE, <<EXPECTED;
70<:arithmetic 10 |%05d:>
71TEMPLATE
7200010
73EXPECTED
74
7928764a
TC
75template_test "children_of", $top, <<TEMPLATE, <<EXPECTED;
76<:iterator begin children_of $parent->{id}:><:
77ofchild title:>
78<:iterator end children_of:>
599fe373
TC
79<:-.set myart = articles.getByPkey($parent->{id}):>
80<:-.for a in [ myart.visible_kids ]:>
81<:-= a.title |html :>
82<:.end for-:>
7928764a
TC
83TEMPLATE
84Three
85Two
86One
599fe373
TC
87Three
88Two
89One
7928764a
TC
90EXPECTED
91
aefcabcb
TC
92template_test "allkids_of", $top, <<TEMPLATE, <<EXPECTED;
93<:iterator begin allkids_of $parent->{id}:><:
94ofallkid title:>
95<:iterator end allkids_of:>
96TEMPLATE
97Parent
98Three
99Two
100One
101
102EXPECTED
103
70789617
TC
104template_test "allkids_of filtered", $top, <<TEMPLATE, <<EXPECTED;
105<:iterator begin allkids_of $parent->{id} filter: [title] =~ /o/i :><:
106ofallkid title:>
107<:iterator end allkids_of:>
108TEMPLATE
109Two
110One
111
112EXPECTED
113
7928764a
TC
114my @kidids = map $_->{id}, @kids;
115template_test "inlines", $top, <<TEMPLATE, <<EXPECTED;
116<:iterator begin inlines @kidids:><:
117inline title:><:iterator end inlines:>
118TEMPLATE
119OneTwoThree
120EXPECTED
121
0316d3da
AO
122template_test "inlines filtered", $top, <<TEMPLATE, <<EXPECTED;
123<:iterator begin inlines @kidids filter: [title] =~ /^T/ :><:
124inline title:><:iterator end inlines:>
125TEMPLATE
126TwoThree
127EXPECTED
128
7928764a
TC
129template_test "ifancestor positive", $kids[0], <<TEMPLATE, <<EXPECTED;
130<:ifAncestor $parent->{id}:>Yes<:or:>No<:eif:>
131TEMPLATE
132Yes
133EXPECTED
134
135template_test "ifancestor equal", $kids[0], <<TEMPLATE, <<EXPECTED;
136<:ifAncestor $kids[0]{id}:>Yes<:or:>No<:eif:>
137TEMPLATE
138Yes
139EXPECTED
140
141template_test "ifancestor negative", $kids[0], <<TEMPLATE, <<EXPECTED;
142<:ifAncestor $kids[1]{id}:>Yes<:or:>No<:eif:>
143TEMPLATE
144No
145EXPECTED
146
ca9aa2bf
TC
147template_test "children", $parent, <<TEMPLATE, <<EXPECTED;
148<:iterator begin children:><:
149child title:>
150<:iterator end children:>
151TEMPLATE
152Three
153Two
154One
155
156EXPECTED
157
158template_test "embed children", $top, <<TEMPLATE, <<EXPECTED;
159<:embed $parent->{id} test/children.tmpl:>
160TEMPLATE
161Three
162Two
163One
164
165
166EXPECTED
167
650a6188
TC
168# test some of the newer basic tags
169template_test "add", $top, <<TEMPLATE, <<EXPECTED;
170<:add 3 4:>
171<:add 3 4 5:>
172<:add 3 [add 4 5]:>
173TEMPLATE
1747
17512
17612
177EXPECTED
178
179template_test "concatenate", $top, <<TEMPLATE, <<EXPECTED;
180<:concatenate one two:>
181<:concatenate one "two " three:>
182<:concatenate one [concatenate "two " three]:>
4772671f 183<:concatenate [concatenate "one" [concatenate "two" "three"]]:>
f1ccc6ff 184<:cat [cat "one" "two"] [concatenate "three" "four"]:>
650a6188
TC
185TEMPLATE
186onetwo
187onetwo three
188onetwo three
4772671f 189onetwothree
f1ccc6ff
TC
190onetwothreefour
191EXPECTED
192
193template_test "cond", $top, <<TEMPLATE, <<EXPECTED;
194<:cond 1 "true" false:>
195<:cond 0 true false:>
196<:cond "" true false:>
197TEMPLATE
198true
199false
200false
650a6188
TC
201EXPECTED
202
203template_test "match", $top, <<'TEMPLATE', <<EXPECTED;
204<:match "abc123" "(\d+)":>
205<:match "abc 123" "(\w+)\s+(\w+)" "$2$1":>
206<:match "abc 123" "(\w+)X(\w+)" "$2$1":>
207<:match "abc 123" "(\w+)X(\w+)" "$2$1" "default":>
208TEMPLATE
209123
210123abc
211
212default
213EXPECTED
214
215template_test "replace", $top, <<'TEMPLATE', <<EXPECTED;
216<:replace "abc123" "(\d+)" "XXX" :>
217<:replace "!!abc 123!!" "(\w+)\s+(\w+)" "$2$1":>
218<:replace "abc 123" "(\w+)" "XXX" g:>
219<:replace "abc 123" "X" "$1" :>
dc040d12
TC
220<:replace "abc
221123
222xyz" "\n" "\\n" g:>
650a6188
TC
223TEMPLATE
224abcXXX
225!!123abc!!
226XXX XXX
227abc 123
dc040d12 228abc\\n123\\nxyz
650a6188
TC
229EXPECTED
230
231template_test "cases", $top, <<'TEMPLATE', <<EXPECTED;
232<:lc "AbC123 XYZ":>
233<:uc "aBc123 xyz":>
234<:lcfirst "AbC123 XYZ":>
235<:ucfirst "aBc123 xyz":>
236<:capitalize "alpha beta gamma":>
50c3d7f9 237<:capitalize "'one day, but don't', 'we know'":>
1ce7ce31 238<:capitalize "IBM stock soars":>
650a6188
TC
239TEMPLATE
240abc123 xyz
241ABC123 XYZ
242abC123 XYZ
243ABc123 xyz
244Alpha Beta Gamma
50c3d7f9 245'One Day, But Don't', 'We Know'
1ce7ce31 246Ibm Stock Soars
650a6188
TC
247EXPECTED
248
6a8a6ac5
TC
249template_test "arithmetic", $top, <<'TEMPLATE', <<EXPECTED;
250<:arithmetic 2+2:>
251<:arithmetic 2+[add 1 1]:>
e3d242f7 252<:arithmetic d2:1.234+1.542:>
bad29ab2 253<:arithmetic 2+ [add 1 2] + [undefinedtag x] + [add 1 1] + [undefinedtag2]:>
6a8a6ac5
TC
254TEMPLATE
2554
2564
e3d242f7 2572.78
bad29ab2 258<:arithmetic 2+ [add 1 2] + [undefinedtag x] + [add 1 1] + [undefinedtag2]:>
6a8a6ac5
TC
259EXPECTED
260
ab3c22ff
TC
261template_test "nobodytext", $kids[0], <<'TEMPLATE', <<EXPECTED;
262<:nobodytext article body:>
263TEMPLATE
48c6e0b6 264One - alpha, beta, gamma, delta, epsilon
ab3c22ff
TC
265EXPECTED
266
810ce080
TC
267{
268 my $formatted = dh_strftime_sql_datetime("%a %d/%m/%Y", $parent->lastModified);
269 template_test "date", $parent, <<'TEMPLATE', <<EXPECTED;
ab3c22ff
TC
270<:date "%a %d/%m/%Y" article lastModified:>
271TEMPLATE
810ce080 272$formatted
ab3c22ff 273EXPECTED
810ce080 274}
ab3c22ff 275
f1e04d14
TC
276use POSIX;
277template_test "today", $parent, <<'TEMPLATE', strftime("%Y-%m-%d %d-%b-%Y\n", localtime);
278<:today "%Y-%m-%d":> <:today:>
279TEMPLATE
280
d2454f2c
TC
281SKIP:
282{
283 eval {
284 require Date::Format;
285 };
286
287 $@
288 and skip("No Date::Format", 3);
289
290 my $today = Date::Format::strftime("%a %o %B %Y", [ localtime ]);
810ce080 291 my $mod = dh_strftime_sql_datetime("%A %o %B %Y", $parent->lastModified);
d2454f2c
TC
292 template_test "date/today w/Date::Format", $parent, <<'TEMPLATE', <<EXPECTED;
293<:date "%A %o %B %Y" article lastModified:> <:today "%a %o %B %Y":>
294TEMPLATE
810ce080 295$mod $today
d2454f2c
TC
296EXPECTED
297}
298
e3d242f7
TC
299template_test "strepeats", $parent, <<'TEMPLATE', <<EXPECTED;
300<:iterator begin strepeats [arithmetic 1+1]:><:strepeat index:> <:strepeat value:>
301<:iterator end strepeats:>
302TEMPLATE
3030 1
3041 2
305
306EXPECTED
307
308template_test "strepeats2", $parent, <<'TEMPLATE', <<EXPECTED;
309<:iterator begin strepeats [arithmetic 1+1] 5:><:strepeat index:> <:strepeat value:>
310<:iterator end strepeats:>
311TEMPLATE
3120 2
3131 3
3142 4
3153 5
316
317EXPECTED
318
810ce080
TC
319{
320 my $mod_iso = dh_strftime_sql_datetime("%FT%T%z", $parent->lastModified);
321 (my $mod_iso2 = $mod_iso) =~ s/(\d\d)$/:$1/;
322 template_test "quotedreplace", $parent, <<'TEMPLATE', <<EXPECTED;
37dd20ad
TC
323<:date "%FT%T%z" article lastModified:>
324<meta name="DC.title" content="<:article title:>" />
325<meta name="DC.date" content="<:replace [date "%FT%T%z" article lastModified] "(\d\d)$" ":$1":>" />
326<meta name="DC.format" content="<:cfg site format "text/html":>" />
327TEMPLATE
810ce080 328$mod_iso
37dd20ad 329<meta name="DC.title" content="Parent" />
810ce080 330<meta name="DC.date" content="$mod_iso2" />
37dd20ad
TC
331<meta name="DC.format" content="text/html" />
332EXPECTED
810ce080 333}
37dd20ad 334
988d8721
TC
335template_test "report", $parent, <<'TEMPLATE', <<EXPECTED;
336<:report bse_test test/testrep 2:>
337<:report bse_test test/testrep [article id]:>
338TEMPLATE
339Report: Test report
340id title
3412 [index subsection]
342Report: Test report
343id title
344$parent->{id} Parent
345EXPECTED
346
fe4a482b
TC
347template_test "body", $parent, <<'TEMPLATE', <<EXPECTED;
348<:body:>
349TEMPLATE
aa1ea04b 350<p>parent article <a href="$base_securl/shop/" title="The Shop" class="doclink">foo</a></p>
fe4a482b
TC
351EXPECTED
352
201e926c
TC
353# not actually generation tests, but chekcs that the is_step_ancestor works
354ok($kids[0]->is_step_ancestor($parent->{id}),
355 "is_step_ancestor - check normal parent");
356ok($parent->is_step_ancestor($parent->{id}),
357 "is_step_ancestor - check step parent");
358ok(!$parent->is_step_ancestor($kids[0]),
359 "is_step_ancestor - failure check");
360
361# and test the static tag
362template_test "ifStepAncestor 1", $parent, <<'TEMPLATE', <<EXPECTED;
363<:ifStepAncestor article:>Good<:or:>bad<:eif:>
364<:ifStepAncestor 3:>Bad<:or:>Good<:eif:>
365TEMPLATE
366Good
367Good
368EXPECTED
369
370template_test "ifStepAncestor 2", $kids[0], <<TEMPLATE, <<EXPECTED;
371<:ifStepAncestor parent:>Good<:or:>bad<:eif:>
372<:ifStepAncestor article:>Good<:or:>Bad<:eif:>
373<:ifStepAncestor $kids[2]{id}:>Bad<:or:>Good<:eif:>
374TEMPLATE
375Good
376Good
377Good
378EXPECTED
379
11c35ec9
TC
380template_test "ifAnd dynamic cfg ajax", $parent, <<TEMPLATE, <<EXPECTED;
381<:ifAnd [ifDynamic] [cfg basic ajax]:>1<:or:>0<:eif:>
382TEMPLATE
3830
384EXPECTED
385
74b21f6d
TC
386template_test "replace complex re", $parent, <<'TEMPLATE', <<EXPECTED;
387<:replace "test&amp;test 01234567890123456789" ((?:&[^;]*;|[^&]){16}).* $1...:>
388TEMPLATE
389test&amp;test 012345...
390EXPECTED
391
48c6e0b6
TC
392template_test "summary", $kids[0], <<'TEMPLATE', <<EXPECTED;
393<:summary article:>
394<:summary article 14:>
395TEMPLATE
396One - alpha, beta, gamma, delta,...
397One - alpha,...
398EXPECTED
399
fd1b40b1
TC
400template_test "ifUnderThreshold parent children", $parent, <<'TEMPLATE', <<EXPECTED;
401<:ifUnderThreshold:>1<:or:>0<:eif:>
402<:ifUnderThreshold children:>1<:or:>0<:eif:>
403TEMPLATE
4040
4050
406EXPECTED
407
408template_test "ifUnderThreshold parent allkids", $parent, <<'TEMPLATE', <<EXPECTED;
409<:ifUnderThreshold allkids:>1<:or:>0<:eif:>
410TEMPLATE
4110
412EXPECTED
413
414template_test "ifUnderThreshold parent stepkids", $parent, <<'TEMPLATE', <<EXPECTED;
415<:ifUnderThreshold stepkids:>1<:or:>0<:eif:>
416TEMPLATE
4171
418EXPECTED
419
420template_test "ifUnderThreshold child children", $kids[0], <<'TEMPLATE', <<EXPECTED;
421<:ifUnderThreshold:>1<:or:>0<:eif:>
422<:ifUnderThreshold children:>1<:or:>0<:eif:>
423TEMPLATE
4241
4251
426EXPECTED
427
428template_test "ifUnderThreshold child allkids", $kids[0], <<'TEMPLATE', <<EXPECTED;
429<:ifUnderThreshold allkids:>1<:or:>0<:eif:>
430TEMPLATE
4311
432EXPECTED
433
434template_test "ifUnderThreshold child stepkids", $kids[0], <<'TEMPLATE', <<EXPECTED;
435<:ifUnderThreshold stepkids:>1<:or:>0<:eif:>
436TEMPLATE
4371
438EXPECTED
439
cb5faf24
TC
440template_test "noreplace undefined", $parent, <<'TEMPLATE', <<'EXPECTED';
441<:switch:><:case Dynallkids_of2 dynofallkid filter: [listed] != 2 :><:if Or [ifAncestor dynofallkid] [ifEq [cgi catid] [dynofallkid id]]:>
442contentA
443 <:iterator begin dynallkids_of2 dynofallkid filter: [listed] != 2 && [generator] =~ /Catalog/ :>
444contentB
445<:ifEq [dynarticle id] [dynofallkid2 id]:> focus<:or:><:eif:>
446<:ifAncestor dynofallkid2:> hilite<:or:><:eif:>
447<:ifFirstDynofallkid2:> first<:or:><:eif:>
448<:ifLastDynofallkid2:> last<:or:><:eif:>
449<:ifDynallkids_of3 dynofallkid2 filter: [listed] != 2 :> parent<:or:><:eif:>
450<:ifDynofallkid2 titleAlias:><:dynofallkid2 titleAlias:><:or:><:dynofallkid2 title:><:eif:>
451<:iterator separator dynallkids_of2:>
452<:iterator end dynallkids_of2:>
453<:or Or:><:eif Or:><:endswitch:>
454TEMPLATE
455<:switch:><:case Dynallkids_of2 dynofallkid filter: [listed] != 2 :><:if Or [ifAncestor dynofallkid] [ifEq [cgi catid] [dynofallkid id]]:>
456contentA
457 <:iterator begin dynallkids_of2 dynofallkid filter: [listed] != 2 && [generator] =~ /Catalog/ :>
458contentB
459<:ifEq [dynarticle id] [dynofallkid2 id]:> focus<:or:><:eif:>
460<:ifAncestor dynofallkid2:> hilite<:or:><:eif:>
461<:ifFirstDynofallkid2:> first<:or:><:eif:>
462<:ifLastDynofallkid2:> last<:or:><:eif:>
463<:ifDynallkids_of3 dynofallkid2 filter: [listed] != 2 :> parent<:or:><:eif:>
464<:ifDynofallkid2 titleAlias:><:dynofallkid2 titleAlias:><:or:><:dynofallkid2 title:><:eif:>
465<:iterator separator dynallkids_of2:>
466<:iterator end dynallkids_of2:>
467<:or Or:><:eif Or:><:endswitch:>
468EXPECTED
fd1b40b1 469
70789617
TC
470############################################################
471# dynamic stuff
472require BSE::Dynamic::Article;
473require BSE::Request::Test;
474my $req = BSE::Request::Test->new(cfg => $cfg);
475my $gen = BSE::Dynamic::Article->new($req);
476
477dyn_template_test "dynallkidsof", $parent, <<TEMPLATE, <<EXPECTED;
478<:iterator begin dynallkids_of $parent->{id} filter: [title] =~ /o/i :><:
57fd870e
TC
479dynofallkid title:> <:next_dynofallkid title:> <:previous_dynofallkid title:>
480 next: <:ifNextDynofallkid:>Y<:or:>N<:eif:>
481 previous: <:ifPreviousDynofallkid:>Y<:or:>N<:eif:>
70789617
TC
482<:iterator end dynallkids_of:>
483TEMPLATE
57fd870e
TC
484Two One
485 next: Y
486 previous: N
487One Two
488 next: N
489 previous: Y
70789617
TC
490
491EXPECTED
492
1d9a2f7f
TC
493dyn_template_test "dynallkidsof nested", $parent, <<TEMPLATE, <<EXPECTED;
494<:iterator begin dynallkids_of $parent->{id} filter: [title] =~ /o/i :><:
495dynofallkid title:><:iterator begin dynallkids_of2 dynofallkid:>
496 <:dynofallkid2 title:><:iterator end dynallkids_of2:>
497<:iterator end dynallkids_of:>
498TEMPLATE
499Two
500 Grandkid
501One
502
503EXPECTED
504
b19b6cc8 505dyn_template_test "dynallkidsof nested filtered cond", $parent, <<TEMPLATE, <<EXPECTED;
773c9c63 506<:iterator begin dynallkids_of dynarticle:><:dynofallkid title:><:if Dynallkids_of2 dynofallkid filter: [title] =~ /G/:><:iterator begin dynallkids_of2 dynofallkid filter: [title] =~ /G/:>
b19b6cc8
TC
507 <:dynofallkid2 title:><:iterator end dynallkids_of2:><:or Dynallkids_of2:>
508 No G kids<:eif Dynallkids_of2:>
509<:iterator end dynallkids_of:>
510TEMPLATE
511Parent
512 No G kids
513Three
514 No G kids
515Two
516 Grandkid
517One
518 No G kids
519
520EXPECTED
521
773c9c63
TC
522dyn_template_test "dynallkids_of move_dynofallkid", $parent, <<TEMPLATE, <<EXPECTED;
523<:iterator begin dynallkids_of dynarticle:><:dynofallkid title:><:move_dynofallkid:>
524<:iterator end dynallkids_of:>
525TEMPLATE
526Parent
527Three
528Two
529One
530
531EXPECTED
532
201e926c
TC
533############################################################
534# Cleanup
535
aefcabcb 536BSE::Admin::StepParents->del($parent, $parent);
1d9a2f7f 537$grandkid->remove($cfg);
7928764a
TC
538for my $kid (reverse @kids) {
539 my $name = $kid->{title};
57d988af 540 my $kidid = $kid->{id};
16901a2a 541 $kid->remove($cfg);
57d988af 542 ok(1, "removing kid $name ($kidid)");
7928764a 543}
16901a2a 544$parent->remove($cfg);
7928764a
TC
545ok(1, "removed parent");
546
7928764a
TC
547sub add_article {
548 my (%parms) = @_;
810ce080
TC
549
550 my $article = bse_make_article(cfg => $cfg, parentid => -1, %parms);
7928764a
TC
551
552 $article;
553}
554
555sub template_test($$$$) {
556 my ($tag, $article, $template, $expected) = @_;
557
558 #diag "Template >$template<";
559 my $gen =
560 eval {
561 (my $filename = $article->{generator}) =~ s!::!/!g;
562 $filename .= ".pm";
563 require $filename;
d09682dd 564 $article->{generator}->new(cfg => $cfg, top => $article);
7928764a
TC
565 };
566 ok($gen, "$tag: created generator $article->{generator}");
567 diag $@ unless $gen;
568 my $content;
569 SKIP: {
570 skip "$tag: couldn't make generator", 1 unless $gen;
571 eval {
572 $content =
573 $gen->generate_low($template, $article, 'Articles', 0);
574 };
575 ok($content, "$tag: generate content");
576 diag $@ unless $content;
577 }
578 SKIP: {
579 skip "$tag: couldn't gen content", 1 unless $content;
580 is($content, $expected, "$tag: comparing");
581 }
582}
70789617
TC
583
584sub dyn_template_test($$$$) {
585 my ($tag, $article, $template, $expected) = @_;
586
587 #diag "Template >$template<";
588 my $gen =
589 eval {
590 my $gen_class = $article->{generator};
591 $gen_class =~ s/.*\W//;
592 $gen_class = "BSE::Dynamic::".$gen_class;
593 (my $filename = $gen_class) =~ s!::!/!g;
594 $filename .= ".pm";
595 require $filename;
596 $gen_class->new($req);
597 };
598 ok($gen, "$tag: created generator $article->{generator}");
599 diag $@ unless $gen;
600
601 # get the template - always regen it
602 my $work_template = _generate_dyn_template($article, $template);
603
604 my $result;
605 SKIP: {
606 skip "$tag: couldn't make generator", 1 unless $gen;
607 eval {
608 $result =
609 $gen->generate($article, $work_template);
610 };
611 ok($result, "$tag: generate content");
612 diag $@ unless $result;
613 }
614 SKIP: {
615 skip "$tag: couldn't gen content", 1 unless $result;
616 is($result->{content}, $expected, "$tag: comparing");
617 }
618}
619
620sub _generate_dyn_template {
621 my ($article, $template) = @_;
622
623 my $articles = 'Articles';
624 my $genname = $article->{generator};
625 eval "use $genname";
626 $@ && die $@;
627 my $gen = $genname->new(articles=>$articles, cfg=>$cfg, top=>$article);
628
629 return $gen->generate_low($template, $article, $articles, 0);
630}