10 sub linkify ($$) { #{{{
14 $content =~ s{(\\?)$config{wiki_link_regexp}}{
15 $2 ? ( $1 ? "[[$2|$3]]" : htmllink($page, titlepage($3), 0, 0, pagetitle($2)))
16 : ( $1 ? "[[$3]]" : htmllink($page, titlepage($3)))
24 return $_scrubber if defined $_scrubber;
26 eval q{use HTML::Scrubber};
27 # Lists based on http://feedparser.org/docs/html-sanitization.html
28 $_scrubber = HTML::Scrubber->new(
30 a abbr acronym address area b big blockquote br
31 button caption center cite code col colgroup dd del
32 dfn dir div dl dt em fieldset font form h1 h2 h3 h4
33 h5 h6 hr i img input ins kbd label legend li map
34 menu ol optgroup option p pre q s samp select small
35 span strike strong sub sup table tbody td textarea
36 tfoot th thead tr tt u ul var
38 default => [undef, { map { $_ => 1 } qw{
39 abbr accept accept-charset accesskey action
40 align alt axis border cellpadding cellspacing
41 char charoff charset checked cite class
42 clear cols colspan color compact coords
43 datetime dir disabled enctype for frame
44 headers height href hreflang hspace id ismap
45 label lang longdesc maxlength media method
46 multiple name nohref noshade nowrap prompt
47 readonly rel rev rows rowspan rules scope
48 selected shape size span src start summary
49 tabindex target title type usemap valign
56 sub htmlize ($$) { #{{{
60 if (! $INC{"/usr/bin/markdown"}) {
62 $blosxom::version="is a proper perl module too much to ask?";
64 do "/usr/bin/markdown";
67 if ($type eq '.mdwn') {
68 $content=Markdown::Markdown($content);
71 error("htmlization of $type not supported");
74 if ($config{sanitize}) {
75 $content=scrubber()->scrub($content);
81 sub backlinks ($) { #{{{
85 foreach my $p (keys %links) {
86 next if bestlink($page, $p) eq $page;
87 if (grep { length $_ && bestlink($p, $_) eq $page } @{$links{$p}}) {
88 my $href=File::Spec->abs2rel(htmlpage($p), dirname($page));
90 # Trim common dir prefixes from both pages.
92 my $page_trimmed=$page;
94 1 while (($dir)=$page_trimmed=~m!^([^/]+/)!) &&
96 $p_trimmed=~s/^\Q$dir\E// &&
97 $page_trimmed=~s/^\Q$dir\E//;
99 push @links, { url => $href, page => $p_trimmed };
103 return sort { $a->{page} cmp $b->{page} } @links;
106 sub parentlinks ($) { #{{{
113 foreach my $dir (reverse split("/", $page)) {
116 unshift @ret, { url => "$path$dir.html", page => $dir };
122 unshift @ret, { url => length $path ? $path : ".", page => $config{wikiname} };
126 sub preprocess ($$) { #{{{
134 if (length $escape) {
135 return "[[$command $params]]";
137 elsif (exists $hooks{preprocess}{$command}) {
139 while ($params =~ /(\w+)=\"([^"]+)"(\s+|$)/g) {
142 return $hooks{preprocess}{$command}{call}->(page => $page, %params);
145 return "[[$command not processed]]";
149 $content =~ s{(\\?)$config{wiki_processor_regexp}}{$handle->($1, $2, $3)}eg;
153 sub add_depends ($$) { #{{{
157 if (! exists $depends{$page}) {
158 $depends{$page}=$globlist;
161 $depends{$page}=globlist_merge($depends{$page}, $globlist);
165 sub globlist_merge ($$) { #{{{
170 # Only add negated globs if they are not matched by the other globlist.
171 foreach my $i ((map { [ $a, $_ ] } split(" ", $b)),
172 (map { [ $b, $_ ] } split(" ", $a))) {
173 if ($i->[1]=~/^!(.*)/) {
174 if (! globlist_match($1, $i->[0])) {
186 sub genpage ($$$) { #{{{
191 my $title=pagetitle(basename($page));
193 my $template=HTML::Template->new(blind_cache => 1,
194 filename => "$config{templatedir}/page.tmpl");
196 if (length $config{cgiurl}) {
197 $template->param(editurl => cgiurl(do => "edit", page => $page));
198 $template->param(prefsurl => cgiurl(do => "prefs"));
200 $template->param(recentchangesurl => cgiurl(do => "recentchanges"));
204 if (length $config{historyurl}) {
205 my $u=$config{historyurl};
206 $u=~s/\[\[file\]\]/$pagesources{$page}/g;
207 $template->param(historyurl => $u);
209 if ($config{hyperestraier}) {
210 $template->param(hyperestraierurl => cgiurl());
215 wikiname => $config{wikiname},
216 parentlinks => [parentlinks($page)],
218 backlinks => [backlinks($page)],
219 discussionlink => htmllink($page, "Discussion", 1, 1),
220 mtime => scalar(gmtime($mtime)),
221 styleurl => styleurl($page),
224 return $template->output;
227 sub check_overwrite ($$) { #{{{
228 # Important security check. Make sure to call this before saving
229 # any files to the source directory.
233 if (! exists $renderedfiles{$src} && -e $dest && ! $config{rebuild}) {
234 error("$dest already exists and was rendered from ".
235 join(" ",(grep { $renderedfiles{$_} eq $dest } keys
237 ", before, so not rendering from $src");
244 return (stat($file))[9];
247 sub findlinks ($$) { #{{{
252 while ($content =~ /(?<!\\)$config{wiki_link_regexp}/g) {
253 push @links, titlepage($2);
255 # Discussion links are a special case since they're not in the text
256 # of the page, but on its template.
257 return @links, "$page/discussion";
260 sub render ($) { #{{{
263 my $type=pagetype($file);
264 my $srcfile=srcfile($file);
265 if ($type ne 'unknown') {
266 my $content=readfile($srcfile);
267 my $page=pagename($file);
269 $links{$page}=[findlinks($content, $page)];
270 delete $depends{$page};
272 $content=linkify($content, $page);
273 $content=preprocess($page, $content);
274 $content=htmlize($type, $content);
276 check_overwrite("$config{destdir}/".htmlpage($page), $page);
277 writefile(htmlpage($page), $config{destdir},
278 genpage($content, $page, mtime($srcfile)));
279 $oldpagemtime{$page}=time;
280 $renderedfiles{$page}=htmlpage($page);
283 my $content=readfile($srcfile, 1);
285 delete $depends{$file};
286 check_overwrite("$config{destdir}/$file", $file);
287 writefile($file, $config{destdir}, $content, 1);
288 $oldpagemtime{$file}=time;
289 $renderedfiles{$file}=$file;
297 my $dir=dirname($file);
298 while (rmdir($dir)) {
304 my $estdir="$config{wikistatedir}/hyperestraier";
305 my $cgi=basename($config{cgiurl});
307 open(TEMPLATE, ">$estdir/$cgi.tmpl") ||
308 error("write $estdir/$cgi.tmpl: $!");
309 print TEMPLATE misctemplate("search",
310 "<!--ESTFORM-->\n\n<!--ESTRESULT-->\n\n<!--ESTINFO-->\n\n");
312 open(TEMPLATE, ">$estdir/$cgi.conf") ||
313 error("write $estdir/$cgi.conf: $!");
314 my $template=HTML::Template->new(
315 filename => "$config{templatedir}/estseek.conf"
317 eval q{use Cwd 'abs_path'};
320 tmplfile => "$estdir/$cgi.tmpl",
321 destdir => abs_path($config{destdir}),
324 print TEMPLATE $template->output;
326 $cgi="$estdir/".basename($config{cgiurl});
328 symlink("/usr/lib/estraier/estseek.cgi", $cgi) ||
329 error("symlink $cgi: $!");
332 sub estcmd ($;@) { #{{{
333 my @params=split(' ', shift);
334 push @params, "-cl", "$config{wikistatedir}/hyperestraier";
339 my $pid=open(CHILD, "|-");
345 close(CHILD) || error("estcmd @params exited nonzero: $?");
349 open(STDOUT, "/dev/null"); # shut it up (closing won't work)
350 exec("estcmd", @params) || error("can't run estcmd");
354 sub refresh () { #{{{
355 # find existing pages
358 eval q{use File::Find};
362 if (/$config{wiki_file_prune_regexp}/) {
363 $File::Find::prune=1;
365 elsif (! -d $_ && ! -l $_) {
366 my ($f)=/$config{wiki_file_regexp}/; # untaint
368 warn("skipping bad filename $_\n");
371 $f=~s/^\Q$config{srcdir}\E\/?//;
373 $exists{pagename($f)}=1;
381 if (/$config{wiki_file_prune_regexp}/) {
382 $File::Find::prune=1;
384 elsif (! -d $_ && ! -l $_) {
385 my ($f)=/$config{wiki_file_regexp}/; # untaint
387 warn("skipping bad filename $_\n");
390 # Don't add files that are in the
392 $f=~s/^\Q$config{underlaydir}\E\/?//;
393 if (! -e "$config{srcdir}/$f" &&
394 ! -l "$config{srcdir}/$f") {
396 $exists{pagename($f)}=1;
401 }, $config{underlaydir});
405 # check for added or removed pages
407 foreach my $file (@files) {
408 my $page=pagename($file);
409 if (! $oldpagemtime{$page}) {
410 debug("new page $page") unless exists $pagectime{$page};
413 $pagesources{$page}=$file;
414 $pagectime{$page}=mtime(srcfile($file))
415 unless exists $pagectime{$page};
419 foreach my $page (keys %oldpagemtime) {
420 if (! $exists{$page}) {
421 debug("removing old page $page");
422 push @del, $pagesources{$page};
423 prune($config{destdir}."/".$renderedfiles{$page});
424 delete $renderedfiles{$page};
425 $oldpagemtime{$page}=0;
426 delete $pagesources{$page};
430 # render any updated files
431 foreach my $file (@files) {
432 my $page=pagename($file);
434 if (! exists $oldpagemtime{$page} ||
435 mtime(srcfile($file)) > $oldpagemtime{$page}) {
436 debug("rendering changed file $file");
442 # if any files were added or removed, check to see if each page
443 # needs an update due to linking to them or inlining them.
444 # TODO: inefficient; pages may get rendered above and again here;
445 # problem is the bestlink may have changed and we won't know until
448 FILE: foreach my $file (@files) {
449 my $page=pagename($file);
450 foreach my $f (@add, @del) {
452 foreach my $link (@{$links{$page}}) {
453 if (bestlink($page, $link) eq $p) {
454 debug("rendering $file, which links to $p");
464 # Handle backlinks; if a page has added/removed links, update the
465 # pages it links to. Also handles rebuilding dependat pages.
466 # TODO: inefficient; pages may get rendered above and again here;
467 # problem is the backlinks could be wrong in the first pass render
469 if (%rendered || @del) {
470 foreach my $f (@files) {
472 if (exists $depends{$p}) {
473 foreach my $file (keys %rendered, @del) {
475 my $page=pagename($file);
476 if (globlist_match($page, $depends{$p})) {
477 debug("rendering $f, which depends on $page");
487 foreach my $file (keys %rendered, @del) {
488 my $page=pagename($file);
490 if (exists $links{$page}) {
491 foreach my $link (map { bestlink($page, $_) } @{$links{$page}}) {
493 (! exists $oldlinks{$page} ||
494 ! grep { bestlink($page, $_) eq $link } @{$oldlinks{$page}})) {
495 $linkchanged{$link}=1;
499 if (exists $oldlinks{$page}) {
500 foreach my $link (map { bestlink($page, $_) } @{$oldlinks{$page}}) {
502 (! exists $links{$page} ||
503 ! grep { bestlink($page, $_) eq $link } @{$links{$page}})) {
504 $linkchanged{$link}=1;
509 foreach my $link (keys %linkchanged) {
510 my $linkfile=$pagesources{$link};
511 if (defined $linkfile) {
512 debug("rendering $linkfile, to update its backlinks");
514 $rendered{$linkfile}=1;
519 if ($config{hyperestraier} && (%rendered || @del)) {
520 debug("updating hyperestraier search index");
522 estcmd("gather -cm -bc -cl -sd",
523 map { $config{destdir}."/".$renderedfiles{pagename($_)} }
530 debug("generating hyperestraier cgi config");