]> git.imager.perl.org - bse.git/blame - site/cgi-bin/modules/BSE/Regen.pm
support for new templating for product options
[bse.git] / site / cgi-bin / modules / BSE / Regen.pm
CommitLineData
2990391c 1package BSE::Regen;
1953ce1c
TC
2use strict;
3use vars qw(@ISA @EXPORT_OK);
4require Exporter;
5@ISA = qw(Exporter);
bd7cf9d4 6@EXPORT_OK = qw(generate_article generate_all generate_button
f00a461d 7 regen_and_refresh generate_extras pregenerate_list generate_one_extra generate_base content_one_extra response_one_extra);
5abe2da5 8use Constants qw($GENERATE_BUTTON $SHOPID $AUTO_GENERATE);
aefcabcb 9use Carp qw(confess);
41f10371 10use BSE::WebUtil qw(refresh_to_admin);
c4f0aab9 11use BSE::Util::HTML;
6c8fac02 12use BSE::DummyArticle;
1953ce1c 13
e0ed81d7 14our $VERSION = "1.015";
cb7fd78d 15
37ca1e13
TC
16# returns non-zero if the Regenerate button should work
17sub generate_button {
18 if ($GENERATE_BUTTON) {
19 if (my $ref = ref $GENERATE_BUTTON) {
20 if ($ref eq 'CODE') {
21 return $GENERATE_BUTTON->();
22 }
23 else {
24 # assumed to be an object
25 return $GENERATE_BUTTON->want_button();
26 }
27 }
28 else {
29 return 1;
30 }
31 }
32 return 0;
33}
34
1953ce1c
TC
35# regenerate an individual article
36sub generate_low {
531fb3bc 37 my ($articles, $article, $cfg) = @_;
1953ce1c 38
28c82e2d 39 $cfg ||= BSE::Cfg->single;
efcc5a30
TC
40
41 my $outname;
42 if ($article->is_dynamic) {
43 my $debug_jit = $cfg->entry('debug', 'jit_dynamic_regen');
16901a2a 44 $outname = $article->cached_filename($cfg);
efcc5a30
TC
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
49 unlink $outname;
50 return;
51 }
52 }
53 else {
8f84f3f1
TC
54 $outname = $article->link_to_filename($cfg)
55 or return; # no output for this article
efcc5a30
TC
56 }
57
538b7aee
TC
58 if ($article->flags =~ /P/ && $article->{parentid} != -1) {
59 # link to parent, remove the file
60 unlink $outname;
61 return;
62 }
63
52fe5612 64 unless ($article->should_generate) {
971b8203
TC
65 # don't generate unlisted pages and remove any old content
66 unlink $outname;
67 return;
68 }
69
1953ce1c 70 my $genname = $article->{generator};
1953ce1c
TC
71 eval "use $genname";
72 $@ && die $@;
d09682dd 73 my $gen = $genname->new(articles=>$articles, cfg=>$cfg, top=>$article);
1953ce1c 74
d09682dd 75 my $content = $gen->generate($article, $articles);
1953ce1c
TC
76 my $tempname = $outname . ".work";
77 unlink $tempname;
3f9c8a96 78 _write_text($tempname, $content, $cfg);
bd7cf9d4 79 unlink $outname;
1953ce1c
TC
80 rename($tempname, $outname)
81 or die "Cannot rename $tempname to $outname: $!";
82}
83
84sub generate_article {
531fb3bc 85 my ($articles, $article, $cfg) = @_;
1953ce1c
TC
86
87 while ($article) {
531fb3bc 88 generate_low($articles, $article, $cfg)
1953ce1c
TC
89 if $article->{link} && $article->{template};
90
91 if ($article->{parentid} != -1) {
92 $article = $articles->getByPkey($article->{parentid});
93 }
94 else {
95 undef $article;
96 }
97 }
98}
99
f00a461d
TC
100sub _cfg_presets {
101 my ($cfg, $article, $type) = @_;
aefcabcb 102
f00a461d
TC
103 my $section = "$type settings";
104
e0ed81d7
AO
105 require BSE::TB::Articles;
106 for my $field (BSE::TB::Article->columns) {
f00a461d
TC
107 if ($cfg->entry($section, $field)) {
108 $article->{$field} = $cfg->entryVar($section, $field);
109 }
110 }
111}
112
113sub _search_presets {
114 my ($cfg) = @_;
e70f7e4b 115
e70f7e4b
TC
116 # build a dummy article
117 use Constants qw($SEARCH_TITLE $SEARCH_TITLE_IMAGE $CGI_URI);
e0ed81d7
AO
118 require BSE::TB::Articles;
119 my %article = map { $_, '' } BSE::TB::Article->columns;
507a9ac3 120 @article{qw(id parentid title titleImage displayOrder link level listed force_dynamic)} =
b1f66a92 121 (-4, -1, $SEARCH_TITLE, $SEARCH_TITLE_IMAGE, 0, $CGI_URI."/search.pl", 0, 1, 1);
e70f7e4b 122
2417a52b 123 $article{link} = $cfg->entryErr('site', 'url') . $article{link};
d09682dd 124
f00a461d
TC
125 _cfg_presets($cfg, \%article, "search");
126
3b263562 127 return _dummy_article(\%article);
1953ce1c
TC
128}
129
f00a461d
TC
130sub _shop_presets {
131 my ($cfg) = @_;
132
e0ed81d7
AO
133 require BSE::TB::Articles;
134 my $shop_base = BSE::TB::Articles->getByPkey($SHOPID);
6866b8dd
TC
135 my $shop = { map { $_ => $shop_base->{$_} } $shop_base->columns };
136 $shop->{link} =~ /^\w+:/
137 or $shop->{link} = $cfg->entryErr('site', 'url') . $shop->{link};
b1f66a92 138 $shop->{id} = -3; # some random negative number
f00a461d
TC
139
140 _cfg_presets($cfg, $shop, "shop");
141
3b263562 142 return _dummy_article($shop);
f00a461d
TC
143}
144
145sub _extras_presets {
146 my ($cfg, $presets) = @_;
147
e0ed81d7
AO
148 require BSE::TB::Articles;
149 my %article = map { $_, '' } BSE::TB::Article->columns;
f00a461d
TC
150 $article{displayOrder} = 1;
151 $article{id} = -5;
152 $article{parentid} = -1;
153 $article{link} = $cfg->entryErr('site', 'url');
154 _cfg_presets($cfg, \%article, $presets);
155
3b263562 156 return _dummy_article(\%article);
f00a461d
TC
157}
158
159my %builtin_extras =
160 (
161 search => [ "search" ],
162 shop =>
163 [
c24a8a7b 164 'cart', 'checkoutnew', 'checkoutfinal',
f00a461d
TC
165 'checkoutpay',
166 ],
167 );
168
169my %abs_urls =
170 (
171 shop => 1,
172 search => 0,
173 );
174
175my %builtin_lookup;
176
177{
178 for my $type (keys %builtin_extras) {
179 for my $name (@{$builtin_extras{$type}}) {
180 $builtin_lookup{$name} = $type;
181 }
182 }
183}
184
185sub _extras_cfg {
186 my ($cfg, $extra) = @_;
187
188 my %result;
189 if ($builtin_extras{$extra->{set}}) {
190 $result{abs_urls} = $abs_urls{$extra->{set}};
191 }
192 else {
193 $result{abs_urls} = $cfg->entry("$extra->{type} settings", "abs_urls", 0);
194 }
195
196 return \%result;
197}
198
199sub pregenerate_list {
200 my ($cfg) = @_;
201
202 my $template_dir = $cfg->entryVar('paths', 'templates');
203
204 # this will change to a directory that can be safely blown away
205 my $pregen_path = $template_dir;
206
207 my @result =
208 (
209 (
210 map
211 +{
212 name => "$_.tmpl",
213 base => $_ . "_base.tmpl",
214 type => $builtin_lookup{$_},
215 set => $builtin_lookup{$_},
216 sort => 0,
217 outpath => $pregen_path,
218 abs_urls => $abs_urls{$builtin_lookup{$_}},
219 dynamic => 1,
220 }, keys %builtin_lookup
221 ),
222 );
223
224 # cfg pregen
225 my %pregen = $cfg->entries('pregenerate');
226 for my $out (keys %pregen) {
227 my ($type, $input) = split ',', $pregen{$out}, 2;
228 push @result,
229 +{
230 name => $out,
231 base => $input,
232 type => $type,
233 set => "pregen",
234 sort => 1,
235 outpath => $pregen_path,
236 dynamic => 1,
237 };
238 }
239
240 # extras file
241 if (open my $extras, "$template_dir/extras.txt") {
242 while (<$extras>) {
243 chomp;
244 next if /^\s*#/;
245 if (/^(\S+)\s+(\S+)/) {
246 push @result,
247 {
248 name => $2,
249 base => $1,
250 type => "extras",
251 set => "extras",
252 sort => 2,
253 outpath => $cfg->content_base_path,
254 dynamic => 0,
255 };
256 }
257 }
258 close $extras;
259 }
260
261 return sort {
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}
266 } @result;
267}
268
269sub _make_extra_art {
270 my ($cfg, $extra) = @_;
271
272 if ($extra->{set} eq "shop") {
273 return _shop_presets($cfg);
274 }
275 elsif ($extra->{set} eq "search") {
276 return _search_presets($cfg);
277 }
278 elsif ($extra->{set} eq "pregen"
279 || $extra->{set} eq "extras") {
280 return _extras_presets($cfg, $extra->{type});
281 }
282 else {
283 confess "Unknown extras set $extra->{set}";
284 }
285}
286
287sub _make_extra_gen {
288 my ($cfg, $extra) = @_;
289
290 my $article = _make_extra_art($cfg, $extra);
39e87dbd 291 require BSE::Generate::Article;
f00a461d
TC
292 my %opts =
293 (
294 cfg => $cfg,
295 top => $article,
296 );
89ffbadb 297 if ($extra->{dynamic}) {
f00a461d
TC
298 $opts{force_dynamic} = 1;
299 }
39e87dbd
AO
300 require BSE::Generate::Article;
301 my $gen = BSE::Generate::Article->new(%opts);
f00a461d
TC
302
303 return ($article, $gen);
304}
305
306sub _common_one_extra {
307 my ($articles, $extra, $cfg) = @_;
308
309 my ($article, $gen) = _make_extra_gen($cfg, $extra);
310 my %acts;
311 %acts = $gen->baseActs($articles, \%acts, $article);
312 if (_extras_cfg($cfg, $extra)->{abs_urls}) {
1953ce1c
TC
313 my $oldurl = $acts{url};
314 $acts{url} =
315 sub {
316 my $value = $oldurl->(@_);
54c97cf6 317 $value =~ /^<:/ and return $value;
1953ce1c
TC
318 unless ($value =~ /^\w+:/) {
319 # put in the base site url
b19047a6 320 $value = $cfg->entryErr('site', 'url').$value;
1953ce1c
TC
321 }
322 return $value;
323 };
1953ce1c 324 }
f00a461d 325
599fe373
TC
326 my $content = BSE::Template->get_page($extra->{base}, $cfg, \%acts,
327 undef, undef, $gen->variables);
328
f00a461d 329 return wantarray ? ( $content, $article ) : $content;
1953ce1c
TC
330}
331
f00a461d
TC
332sub response_one_extra {
333 my ($articles, $extra) = @_;
1953ce1c 334
f00a461d
TC
335 my $cfg = BSE::Cfg->single;
336 my $content = _common_one_extra($articles, $extra, $cfg);
edc5d096 337
f00a461d
TC
338 return BSE::Template->make_response($content, BSE::Template->get_type($cfg, $extra->{template}));
339}
edc5d096 340
f00a461d
TC
341sub content_one_extra {
342 my ($articles, $extra) = @_;
343
344 my $cfg = BSE::Cfg->single;
345 return _common_one_extra($articles, $extra, $cfg);
346}
347
348sub generate_one_extra {
349 my ($articles, $extra) = @_;
350
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);
356 unlink $outname;
357 rename $workname, $outname
358 or die "Cannot rename $workname to $outname: $!";
359}
360
361sub generate_base {
362 my %opts = @_;
363
364 my $cfg = delete $opts{cfg} || BSE::Cfg->single;
365
e0ed81d7 366 my $articles = delete $opts{articles} || "BSE::TB::Articles";
f00a461d
TC
367 my $extras = delete $opts{extras} || [ pregenerate_list($cfg) ];
368
369 my $progress = delete $opts{progress} || sub {};
370
371 my @extras = sort
372 {
373 $a->{sort} <=> $b->{sort}
374 || $a->{type} cmp $b->{type}
375 || lc $a->{name} cmp lc $b->{name}
376 } @$extras;
377
378 my $count = @extras;
379 $progress->({ type => "extras", count => $count, info => "count" }, "$count base pages");
380 my $set = "";
381 my $type = "";
382 my ($gen, $article);
383 my %acts;
e4793ddd 384 for my $extra (@extras) {
f00a461d
TC
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};
390 $acts{url} =
391 sub {
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;
397 }
398 return $value;
399 };
edc5d096 400 }
f00a461d 401 $progress->($extra, "Generating $extra->{name}");
599fe373
TC
402 my $content = BSE::Template->get_page($extra->{base}, $cfg, \%acts,
403 undef, undef, $gen->variables);
f00a461d
TC
404 my $outname = $extra->{outpath} . "/". $extra->{name};
405 my $workname = $outname . ".work";
406 _write_text($workname, $content, $cfg);
407 unlink $outname;
408 rename $workname, $outname
409 or die "Cannot rename $workname to $outname: $!";
edc5d096
TC
410 }
411 }
1953ce1c
TC
412}
413
414sub generate_all {
531fb3bc 415 my ($articles, $cfg, $callback) = @_;
1953ce1c 416
531fb3bc
TC
417 my @articleids = $articles->allids;
418 my $pc = 0;
419 $callback->("Generating articles (".scalar(@articleids)." to do)")
420 if $callback;
421 my $index;
422 my $total = 0;
531fb3bc 423 Squirrel::Table->caching(1);
c5493735 424 my $allstart = time;
531fb3bc 425 for my $articleid (@articleids) {
531fb3bc
TC
426 my $article = $articles->getByPkey($articleid);
427 ++$index;
531fb3bc 428 my $newpc = $index / @articleids * 100;
c5493735 429 my $now = time;
531fb3bc 430 if ($callback && $newpc >= $pc + 1 || abs($newpc-100) < 0.01) {
c5493735 431 $callback->(sprintf("%5d: %.1f%% done - elapsed: %.1f", $articleid, $newpc, $now - $allstart)) if $callback;
531fb3bc
TC
432 $pc = int $newpc;
433 }
7797165e
TC
434 if ($article->{link} && $article->{template}) {
435 #$callback->("Article $articleid");
436 generate_low($articles, $article, $cfg);
437 }
1953ce1c 438 }
1953ce1c 439
f00a461d
TC
440 my $last_section = "";
441 my $progress = $callback ? sub {
442 my $data = shift;
443 if (!$data->{count} && $data->{set} ne $last_section) {
444 $callback->("Regenerating $data->{set} pages");
445 $last_section = $data->{set};
bd7cf9d4 446 }
f00a461d
TC
447 $callback->(" @_")
448 } : undef;
449 generate_base(cfg => $cfg, articles => $articles, progress => $progress);
bd7cf9d4 450
f00a461d 451 $callback->("Total of ".(time()-$allstart)." seconds") if $callback;
bd7cf9d4
TC
452}
453
3f9c8a96
TC
454sub _write_text {
455 my ($filename, $data, $cfg) = @_;
456
457 open my $fh, ">", $filename
458 or die "Cannot create $filename: $!";
459 if ($cfg->utf8) {
460 my $charset = $cfg->charset;
461 binmode $fh, ":encoding($charset)";
462 }
463 print $fh $data
464 or die "Cannot write $filename: $!";
465 close $fh
466 or die "Cannot close $filename: $!";
467}
468
3b263562
TC
469sub _dummy_article {
470 my ($data) = @_;
471
6c8fac02 472 return bless $data, "BSE::DummyArticle";
139d9a14
TC
473}
474
1953ce1c 4751;