3 use vars qw(@ISA @EXPORT_OK);
6 @EXPORT_OK = qw(generate_article generate_all generate_button
7 regen_and_refresh generate_extras pregenerate_list generate_one_extra generate_base content_one_extra response_one_extra);
8 use Constants qw($GENERATE_BUTTON $SHOPID $AUTO_GENERATE);
10 use BSE::WebUtil qw(refresh_to_admin);
12 use BSE::DummyArticle;
14 our $VERSION = "1.015";
16 # returns non-zero if the Regenerate button should work
18 if ($GENERATE_BUTTON) {
19 if (my $ref = ref $GENERATE_BUTTON) {
21 return $GENERATE_BUTTON->();
24 # assumed to be an object
25 return $GENERATE_BUTTON->want_button();
35 # regenerate an individual article
37 my ($articles, $article, $cfg) = @_;
39 $cfg ||= BSE::Cfg->single;
42 if ($article->is_dynamic) {
43 my $debug_jit = $cfg->entry('debug', 'jit_dynamic_regen');
44 $outname = $article->cached_filename($cfg);
45 if ($article->{flags} !~ /R/ &&
46 $cfg->entry('basic', 'jit_dynamic_pregen')) {
47 $debug_jit and print STDERR "JIT: $article->{id} - deleting $outname\n";
48 # just delete the file, page.pl will make it if needed
54 $outname = $article->link_to_filename($cfg)
55 or return; # no output for this article
58 if ($article->flags =~ /P/ && $article->{parentid} != -1) {
59 # link to parent, remove the file
64 unless ($article->should_generate) {
65 # don't generate unlisted pages and remove any old content
70 my $genname = $article->{generator};
73 my $gen = $genname->new(articles=>$articles, cfg=>$cfg, top=>$article);
75 my $content = $gen->generate($article, $articles);
76 my $tempname = $outname . ".work";
78 _write_text($tempname, $content, $cfg);
80 rename($tempname, $outname)
81 or die "Cannot rename $tempname to $outname: $!";
84 sub generate_article {
85 my ($articles, $article, $cfg) = @_;
88 generate_low($articles, $article, $cfg)
89 if $article->{link} && $article->{template};
91 if ($article->{parentid} != -1) {
92 $article = $articles->getByPkey($article->{parentid});
101 my ($cfg, $article, $type) = @_;
103 my $section = "$type settings";
105 require BSE::TB::Articles;
106 for my $field (BSE::TB::Article->columns) {
107 if ($cfg->entry($section, $field)) {
108 $article->{$field} = $cfg->entryVar($section, $field);
113 sub _search_presets {
116 # build a dummy article
117 use Constants qw($SEARCH_TITLE $SEARCH_TITLE_IMAGE $CGI_URI);
118 require BSE::TB::Articles;
119 my %article = map { $_, '' } BSE::TB::Article->columns;
120 @article{qw(id parentid title titleImage displayOrder link level listed force_dynamic)} =
121 (-4, -1, $SEARCH_TITLE, $SEARCH_TITLE_IMAGE, 0, $CGI_URI."/search.pl", 0, 1, 1);
123 $article{link} = $cfg->entryErr('site', 'url') . $article{link};
125 _cfg_presets($cfg, \%article, "search");
127 return _dummy_article(\%article);
133 require BSE::TB::Articles;
134 my $shop_base = BSE::TB::Articles->getByPkey($SHOPID);
135 my $shop = { map { $_ => $shop_base->{$_} } $shop_base->columns };
136 $shop->{link} =~ /^\w+:/
137 or $shop->{link} = $cfg->entryErr('site', 'url') . $shop->{link};
138 $shop->{id} = -3; # some random negative number
140 _cfg_presets($cfg, $shop, "shop");
142 return _dummy_article($shop);
145 sub _extras_presets {
146 my ($cfg, $presets) = @_;
148 require BSE::TB::Articles;
149 my %article = map { $_, '' } BSE::TB::Article->columns;
150 $article{displayOrder} = 1;
152 $article{parentid} = -1;
153 $article{link} = $cfg->entryErr('site', 'url');
154 _cfg_presets($cfg, \%article, $presets);
156 return _dummy_article(\%article);
161 search => [ "search" ],
164 'cart', 'checkoutnew', 'checkoutfinal',
178 for my $type (keys %builtin_extras) {
179 for my $name (@{$builtin_extras{$type}}) {
180 $builtin_lookup{$name} = $type;
186 my ($cfg, $extra) = @_;
189 if ($builtin_extras{$extra->{set}}) {
190 $result{abs_urls} = $abs_urls{$extra->{set}};
193 $result{abs_urls} = $cfg->entry("$extra->{type} settings", "abs_urls", 0);
199 sub pregenerate_list {
202 my $template_dir = $cfg->entryVar('paths', 'templates');
204 # this will change to a directory that can be safely blown away
205 my $pregen_path = $template_dir;
213 base => $_ . "_base.tmpl",
214 type => $builtin_lookup{$_},
215 set => $builtin_lookup{$_},
217 outpath => $pregen_path,
218 abs_urls => $abs_urls{$builtin_lookup{$_}},
220 }, keys %builtin_lookup
225 my %pregen = $cfg->entries('pregenerate');
226 for my $out (keys %pregen) {
227 my ($type, $input) = split ',', $pregen{$out}, 2;
235 outpath => $pregen_path,
241 if (open my $extras, "$template_dir/extras.txt") {
245 if (/^(\S+)\s+(\S+)/) {
253 outpath => $cfg->content_base_path,
262 $a->{sort} <=> $b->{sort}
263 || $a->{set} cmp $b->{set}
264 || $a->{type} cmp $b->{type}
265 || lc $a->{name} cmp lc $b->{name}
269 sub _make_extra_art {
270 my ($cfg, $extra) = @_;
272 if ($extra->{set} eq "shop") {
273 return _shop_presets($cfg);
275 elsif ($extra->{set} eq "search") {
276 return _search_presets($cfg);
278 elsif ($extra->{set} eq "pregen"
279 || $extra->{set} eq "extras") {
280 return _extras_presets($cfg, $extra->{type});
283 confess "Unknown extras set $extra->{set}";
287 sub _make_extra_gen {
288 my ($cfg, $extra) = @_;
290 my $article = _make_extra_art($cfg, $extra);
291 require BSE::Generate::Article;
297 if ($extra->{dynamic}) {
298 $opts{force_dynamic} = 1;
300 require BSE::Generate::Article;
301 my $gen = BSE::Generate::Article->new(%opts);
303 return ($article, $gen);
306 sub _common_one_extra {
307 my ($articles, $extra, $cfg) = @_;
309 my ($article, $gen) = _make_extra_gen($cfg, $extra);
311 %acts = $gen->baseActs($articles, \%acts, $article);
312 if (_extras_cfg($cfg, $extra)->{abs_urls}) {
313 my $oldurl = $acts{url};
316 my $value = $oldurl->(@_);
317 $value =~ /^<:/ and return $value;
318 unless ($value =~ /^\w+:/) {
319 # put in the base site url
320 $value = $cfg->entryErr('site', 'url').$value;
326 my $content = BSE::Template->get_page($extra->{base}, $cfg, \%acts,
327 undef, undef, $gen->variables);
329 return wantarray ? ( $content, $article ) : $content;
332 sub response_one_extra {
333 my ($articles, $extra) = @_;
335 my $cfg = BSE::Cfg->single;
336 my $content = _common_one_extra($articles, $extra, $cfg);
338 return BSE::Template->make_response($content, BSE::Template->get_type($cfg, $extra->{template}));
341 sub content_one_extra {
342 my ($articles, $extra) = @_;
344 my $cfg = BSE::Cfg->single;
345 return _common_one_extra($articles, $extra, $cfg);
348 sub generate_one_extra {
349 my ($articles, $extra) = @_;
351 my $cfg = BSE::Cfg->single;
352 my $content = _common_one_extra($articles, $extra, $cfg);
353 my $outname = $extra->{outpath} . "/". $extra->{name};
354 my $workname = $outname . ".work";
355 _write_text($workname, $content, $cfg);
357 rename $workname, $outname
358 or die "Cannot rename $workname to $outname: $!";
364 my $cfg = delete $opts{cfg} || BSE::Cfg->single;
366 my $articles = delete $opts{articles} || "BSE::TB::Articles";
367 my $extras = delete $opts{extras} || [ pregenerate_list($cfg) ];
369 my $progress = delete $opts{progress} || sub {};
373 $a->{sort} <=> $b->{sort}
374 || $a->{type} cmp $b->{type}
375 || lc $a->{name} cmp lc $b->{name}
379 $progress->({ type => "extras", count => $count, info => "count" }, "$count base pages");
384 for my $extra (@extras) {
385 if ($extra->{set} ne $set || $extra->{type} ne $type) {
386 ($article, $gen) = _make_extra_gen($cfg, $extra);
387 %acts = $gen->baseActs($articles, \%acts, $article);
388 if (_extras_cfg($cfg, $extra)->{abs_urls}) {
389 my $oldurl = $acts{url};
392 my $value = $oldurl->(@_);
393 $value =~ /^<:/ and return $value;
394 unless ($value =~ /^\w+:/) {
395 # put in the base site url
396 $value = $cfg->entryErr('site', 'url').$value;
401 $progress->($extra, "Generating $extra->{name}");
402 my $content = BSE::Template->get_page($extra->{base}, $cfg, \%acts,
403 undef, undef, $gen->variables);
404 my $outname = $extra->{outpath} . "/". $extra->{name};
405 my $workname = $outname . ".work";
406 _write_text($workname, $content, $cfg);
408 rename $workname, $outname
409 or die "Cannot rename $workname to $outname: $!";
415 my ($articles, $cfg, $callback) = @_;
417 my @articleids = $articles->allids;
419 $callback->("Generating articles (".scalar(@articleids)." to do)")
423 Squirrel::Table->caching(1);
425 for my $articleid (@articleids) {
426 my $article = $articles->getByPkey($articleid);
428 my $newpc = $index / @articleids * 100;
430 if ($callback && $newpc >= $pc + 1 || abs($newpc-100) < 0.01) {
431 $callback->(sprintf("%5d: %.1f%% done - elapsed: %.1f", $articleid, $newpc, $now - $allstart)) if $callback;
434 if ($article->{link} && $article->{template}) {
435 #$callback->("Article $articleid");
436 generate_low($articles, $article, $cfg);
440 my $last_section = "";
441 my $progress = $callback ? sub {
443 if (!$data->{count} && $data->{set} ne $last_section) {
444 $callback->("Regenerating $data->{set} pages");
445 $last_section = $data->{set};
449 generate_base(cfg => $cfg, articles => $articles, progress => $progress);
451 $callback->("Total of ".(time()-$allstart)." seconds") if $callback;
455 my ($filename, $data, $cfg) = @_;
457 open my $fh, ">", $filename
458 or die "Cannot create $filename: $!";
460 my $charset = $cfg->charset;
461 binmode $fh, ":encoding($charset)";
464 or die "Cannot write $filename: $!";
466 or die "Cannot close $filename: $!";
472 return bless $data, "BSE::DummyArticle";