9 sub linkify ($$) { #{{{
13 $content =~ s{(\\?)$config{wiki_link_regexp}}{
14 $2 ? ( $1 ? "[[$2|$3]]" : htmllink($page, titlepage($3), 0, 0, pagetitle($2)))
15 : ( $1 ? "[[$3]]" : htmllink($page, titlepage($3)))
23 return $_scrubber if defined $_scrubber;
25 eval q{use HTML::Scrubber};
26 # Lists based on http://feedparser.org/docs/html-sanitization.html
27 $_scrubber = HTML::Scrubber->new(
29 a abbr acronym address area b big blockquote br
30 button caption center cite code col colgroup dd del
31 dfn dir div dl dt em fieldset font form h1 h2 h3 h4
32 h5 h6 hr i img input ins kbd label legend li map
33 menu ol optgroup option p pre q s samp select small
34 span strike strong sub sup table tbody td textarea
35 tfoot th thead tr tt u ul var
37 default => [undef, { map { $_ => 1 } qw{
38 abbr accept accept-charset accesskey action
39 align alt axis border cellpadding cellspacing
40 char charoff charset checked cite class
41 clear cols colspan color compact coords
42 datetime dir disabled enctype for frame
43 headers height href hreflang hspace id ismap
44 label lang longdesc maxlength media method
45 multiple name nohref noshade nowrap prompt
46 readonly rel rev rows rowspan rules scope
47 selected shape size span src start summary
48 tabindex target title type usemap valign
55 sub htmlize ($$) { #{{{
59 if (! $INC{"/usr/bin/markdown"}) {
61 $blosxom::version="is a proper perl module too much to ask?";
63 do "/usr/bin/markdown";
66 if ($type eq '.mdwn') {
67 $content=Markdown::Markdown($content);
70 error("htmlization of $type not supported");
73 if ($config{sanitize}) {
74 $content=scrubber()->scrub($content);
80 sub backlinks ($) { #{{{
84 foreach my $p (keys %links) {
85 next if bestlink($page, $p) eq $page;
86 if (grep { length $_ && bestlink($p, $_) eq $page } @{$links{$p}}) {
87 my $href=File::Spec->abs2rel(htmlpage($p), dirname($page));
89 # Trim common dir prefixes from both pages.
91 my $page_trimmed=$page;
93 1 while (($dir)=$page_trimmed=~m!^([^/]+/)!) &&
95 $p_trimmed=~s/^\Q$dir\E// &&
96 $page_trimmed=~s/^\Q$dir\E//;
98 push @links, { url => $href, page => $p_trimmed };
102 return sort { $a->{page} cmp $b->{page} } @links;
105 sub parentlinks ($) { #{{{
112 foreach my $dir (reverse split("/", $page)) {
115 unshift @ret, { url => "$path$dir.html", page => $dir };
121 unshift @ret, { url => length $path ? $path : ".", page => $config{wikiname} };
125 sub preprocess ($$) { #{{{
133 if (length $escape) {
134 return "[[$command $params]]";
136 elsif (exists $plugins{preprocess}{$command}) {
138 while ($params =~ /(\w+)=\"([^"]+)"(\s+|$)/g) {
141 return $plugins{preprocess}{$command}->(page => $page, %params);
144 return "[[$command not processed]]";
148 $content =~ s{(\\?)$config{wiki_processor_regexp}}{$handle->($1, $2, $3)}eg;
152 sub add_depends ($$) { #{{{
156 if (! exists $depends{$page}) {
157 $depends{$page}=$globlist;
160 $depends{$page}.=" ".$globlist;
164 sub genpage ($$$) { #{{{
169 my $title=pagetitle(basename($page));
171 my $template=HTML::Template->new(blind_cache => 1,
172 filename => "$config{templatedir}/page.tmpl");
174 if (length $config{cgiurl}) {
175 $template->param(editurl => cgiurl(do => "edit", page => $page));
176 $template->param(prefsurl => cgiurl(do => "prefs"));
178 $template->param(recentchangesurl => cgiurl(do => "recentchanges"));
182 if (length $config{historyurl}) {
183 my $u=$config{historyurl};
184 $u=~s/\[\[file\]\]/$pagesources{$page}/g;
185 $template->param(historyurl => $u);
187 if ($config{hyperestraier}) {
188 $template->param(hyperestraierurl => cgiurl());
193 wikiname => $config{wikiname},
194 parentlinks => [parentlinks($page)],
196 backlinks => [backlinks($page)],
197 discussionlink => htmllink($page, "Discussion", 1, 1),
198 mtime => scalar(gmtime($mtime)),
199 styleurl => styleurl($page),
202 return $template->output;
205 sub check_overwrite ($$) { #{{{
206 # Important security check. Make sure to call this before saving
207 # any files to the source directory.
211 if (! exists $renderedfiles{$src} && -e $dest && ! $config{rebuild}) {
212 error("$dest already exists and was rendered from ".
213 join(" ",(grep { $renderedfiles{$_} eq $dest } keys
215 ", before, so not rendering from $src");
222 return (stat($file))[9];
225 sub findlinks ($$) { #{{{
230 while ($content =~ /(?<!\\)$config{wiki_link_regexp}/g) {
231 push @links, titlepage($2);
233 # Discussion links are a special case since they're not in the text
234 # of the page, but on its template.
235 return @links, "$page/discussion";
238 sub render ($) { #{{{
241 my $type=pagetype($file);
242 my $srcfile=srcfile($file);
243 if ($type ne 'unknown') {
244 my $content=readfile($srcfile);
245 my $page=pagename($file);
247 $links{$page}=[findlinks($content, $page)];
248 delete $depends{$page};
250 $content=linkify($content, $page);
251 $content=preprocess($page, $content);
252 $content=htmlize($type, $content);
254 check_overwrite("$config{destdir}/".htmlpage($page), $page);
255 writefile(htmlpage($page), $config{destdir},
256 genpage($content, $page, mtime($srcfile)));
257 $oldpagemtime{$page}=time;
258 $renderedfiles{$page}=htmlpage($page);
261 my $content=readfile($srcfile, 1);
263 delete $depends{$file};
264 check_overwrite("$config{destdir}/$file", $file);
265 writefile($file, $config{destdir}, $content, 1);
266 $oldpagemtime{$file}=time;
267 $renderedfiles{$file}=$file;
275 my $dir=dirname($file);
276 while (rmdir($dir)) {
282 my $estdir="$config{wikistatedir}/hyperestraier";
283 my $cgi=basename($config{cgiurl});
285 open(TEMPLATE, ">$estdir/$cgi.tmpl") ||
286 error("write $estdir/$cgi.tmpl: $!");
287 print TEMPLATE misctemplate("search",
288 "<!--ESTFORM-->\n\n<!--ESTRESULT-->\n\n<!--ESTINFO-->\n\n");
290 open(TEMPLATE, ">$estdir/$cgi.conf") ||
291 error("write $estdir/$cgi.conf: $!");
292 my $template=HTML::Template->new(
293 filename => "$config{templatedir}/estseek.conf"
295 eval q{use Cwd 'abs_path'};
298 tmplfile => "$estdir/$cgi.tmpl",
299 destdir => abs_path($config{destdir}),
302 print TEMPLATE $template->output;
304 $cgi="$estdir/".basename($config{cgiurl});
306 symlink("/usr/lib/estraier/estseek.cgi", $cgi) ||
307 error("symlink $cgi: $!");
310 sub estcmd ($;@) { #{{{
311 my @params=split(' ', shift);
312 push @params, "-cl", "$config{wikistatedir}/hyperestraier";
317 my $pid=open(CHILD, "|-");
323 close(CHILD) || error("estcmd @params exited nonzero: $?");
327 open(STDOUT, "/dev/null"); # shut it up (closing won't work)
328 exec("estcmd", @params) || error("can't run estcmd");
332 sub refresh () { #{{{
333 # find existing pages
336 eval q{use File::Find};
340 if (/$config{wiki_file_prune_regexp}/) {
341 $File::Find::prune=1;
343 elsif (! -d $_ && ! -l $_) {
344 my ($f)=/$config{wiki_file_regexp}/; # untaint
346 warn("skipping bad filename $_\n");
349 $f=~s/^\Q$config{srcdir}\E\/?//;
351 $exists{pagename($f)}=1;
359 if (/$config{wiki_file_prune_regexp}/) {
360 $File::Find::prune=1;
362 elsif (! -d $_ && ! -l $_) {
363 my ($f)=/$config{wiki_file_regexp}/; # untaint
365 warn("skipping bad filename $_\n");
368 # Don't add files that are in the
370 $f=~s/^\Q$config{underlaydir}\E\/?//;
371 if (! -e "$config{srcdir}/$f" &&
372 ! -l "$config{srcdir}/$f") {
374 $exists{pagename($f)}=1;
379 }, $config{underlaydir});
383 # check for added or removed pages
385 foreach my $file (@files) {
386 my $page=pagename($file);
387 if (! $oldpagemtime{$page}) {
388 debug("new page $page") unless exists $pagectime{$page};
391 $pagesources{$page}=$file;
392 $pagectime{$page}=mtime(srcfile($file))
393 unless exists $pagectime{$page};
397 foreach my $page (keys %oldpagemtime) {
398 if (! $exists{$page}) {
399 debug("removing old page $page");
400 push @del, $pagesources{$page};
401 prune($config{destdir}."/".$renderedfiles{$page});
402 delete $renderedfiles{$page};
403 $oldpagemtime{$page}=0;
404 delete $pagesources{$page};
408 # render any updated files
409 foreach my $file (@files) {
410 my $page=pagename($file);
412 if (! exists $oldpagemtime{$page} ||
413 mtime(srcfile($file)) > $oldpagemtime{$page}) {
414 debug("rendering changed file $file");
420 # if any files were added or removed, check to see if each page
421 # needs an update due to linking to them or inlining them.
422 # TODO: inefficient; pages may get rendered above and again here;
423 # problem is the bestlink may have changed and we won't know until
426 FILE: foreach my $file (@files) {
427 my $page=pagename($file);
428 foreach my $f (@add, @del) {
430 foreach my $link (@{$links{$page}}) {
431 if (bestlink($page, $link) eq $p) {
432 debug("rendering $file, which links to $p");
442 # Handle backlinks; if a page has added/removed links, update the
443 # pages it links to. Also handles rebuilding dependat pages.
444 # TODO: inefficient; pages may get rendered above and again here;
445 # problem is the backlinks could be wrong in the first pass render
447 if (%rendered || @del) {
448 foreach my $f (@files) {
450 if (exists $depends{$p}) {
451 foreach my $file (keys %rendered, @del) {
453 my $page=pagename($file);
454 if (globlist_match($page, $depends{$p})) {
455 debug("rendering $f, which depends on $page");
465 foreach my $file (keys %rendered, @del) {
466 my $page=pagename($file);
468 if (exists $links{$page}) {
469 foreach my $link (map { bestlink($page, $_) } @{$links{$page}}) {
471 (! exists $oldlinks{$page} ||
472 ! grep { bestlink($page, $_) eq $link } @{$oldlinks{$page}})) {
473 $linkchanged{$link}=1;
477 if (exists $oldlinks{$page}) {
478 foreach my $link (map { bestlink($page, $_) } @{$oldlinks{$page}}) {
480 (! exists $links{$page} ||
481 ! grep { bestlink($page, $_) eq $link } @{$links{$page}})) {
482 $linkchanged{$link}=1;
487 foreach my $link (keys %linkchanged) {
488 my $linkfile=$pagesources{$link};
489 if (defined $linkfile) {
490 debug("rendering $linkfile, to update its backlinks");
492 $rendered{$linkfile}=1;
497 if ($config{hyperestraier} && (%rendered || @del)) {
498 debug("updating hyperestraier search index");
500 estcmd("gather -cm -bc -cl -sd",
501 map { $config{destdir}."/".$renderedfiles{pagename($_)} }
508 debug("generating hyperestraier cgi config");