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 rsspage ($) { #{{{
131 sub preprocess ($$) { #{{{
135 my %commands=(inline => \&preprocess_inline);
141 if (length $escape) {
142 return "[[$command $params]]";
144 elsif (exists $commands{$command}) {
146 while ($params =~ /(\w+)=\"([^"]+)"(\s+|$)/g) {
149 return $commands{$command}->(page => $page, %params);
152 return "[[bad directive $command]]";
156 $content =~ s{(\\?)$config{wiki_processor_regexp}}{$handle->($1, $2, $3)}eg;
160 sub blog_list ($$) { #{{{
165 foreach my $page (keys %pagesources) {
166 if (globlist_match($page, $globlist)) {
171 @list=sort { $pagectime{$b} <=> $pagectime{$a} } @list;
172 return @list if ! $maxitems || @list <= $maxitems;
173 return @list[0..$maxitems - 1];
176 sub get_inline_content ($$) { #{{{
177 my $parentpage=shift;
180 my $file=$pagesources{$page};
181 my $type=pagetype($file);
182 if ($type ne 'unknown') {
183 return htmlize($type, linkify(readfile(srcfile($file)), $parentpage));
190 sub preprocess_inline ($@) { #{{{
193 if (! exists $params{pages}) {
196 if (! exists $params{archive}) {
197 $params{archive}="no";
199 if (! exists $params{show} && $params{archive} eq "no") {
202 if (! exists $depends{$params{page}}) {
203 $depends{$params{page}}=$params{pages};
206 $depends{$params{page}}.=" ".$params{pages};
211 if (exists $params{rootpage}) {
212 # Add a blog post form, with a rss link button.
213 my $formtemplate=HTML::Template->new(blind_cache => 1,
214 filename => "$config{templatedir}/blogpost.tmpl");
215 $formtemplate->param(cgiurl => $config{cgiurl});
216 $formtemplate->param(rootpage => $params{rootpage});
218 $formtemplate->param(rssurl => rsspage(basename($params{page})));
220 $ret.=$formtemplate->output;
222 elsif ($config{rss}) {
223 # Add a rss link button.
224 my $linktemplate=HTML::Template->new(blind_cache => 1,
225 filename => "$config{templatedir}/rsslink.tmpl");
226 $linktemplate->param(rssurl => rsspage(basename($params{page})));
227 $ret.=$linktemplate->output;
230 my $template=HTML::Template->new(blind_cache => 1,
231 filename => (($params{archive} eq "no")
232 ? "$config{templatedir}/inlinepage.tmpl"
233 : "$config{templatedir}/inlinepagetitle.tmpl"));
236 foreach my $page (blog_list($params{pages}, $params{show})) {
237 next if $page eq $params{page};
239 $template->param(pagelink => htmllink($params{page}, $page));
240 $template->param(content => get_inline_content($params{page}, $page))
241 if $params{archive} eq "no";
242 $template->param(ctime => scalar(gmtime($pagectime{$page})));
243 $ret.=$template->output;
246 # TODO: should really add this to renderedfiles and call
247 # check_overwrite, but currently renderedfiles
248 # only supports listing one file per page.
250 writefile(rsspage($params{page}), $config{destdir},
251 genrss($params{page}, @pages));
257 sub genpage ($$$) { #{{{
262 my $title=pagetitle(basename($page));
264 my $template=HTML::Template->new(blind_cache => 1,
265 filename => "$config{templatedir}/page.tmpl");
267 if (length $config{cgiurl}) {
268 $template->param(editurl => cgiurl(do => "edit", page => $page));
269 $template->param(prefsurl => cgiurl(do => "prefs"));
271 $template->param(recentchangesurl => cgiurl(do => "recentchanges"));
275 if (length $config{historyurl}) {
276 my $u=$config{historyurl};
277 $u=~s/\[\[file\]\]/$pagesources{$page}/g;
278 $template->param(historyurl => $u);
280 if ($config{hyperestraier}) {
281 $template->param(hyperestraierurl => cgiurl());
286 wikiname => $config{wikiname},
287 parentlinks => [parentlinks($page)],
289 backlinks => [backlinks($page)],
290 discussionlink => htmllink($page, "Discussion", 1, 1),
291 mtime => scalar(gmtime($mtime)),
292 styleurl => styleurl($page),
295 return $template->output;
298 sub date_822 ($) { #{{{
302 return POSIX::strftime("%a, %d %b %Y %H:%M:%S %z", localtime($time));
305 sub absolute_urls ($$) { #{{{
306 # sucky sub because rss sucks
312 $content=~s/<a\s+href="(?!http:\/\/)([^"]+)"/<a href="$url$1"/ig;
313 $content=~s/<img\s+src="(?!http:\/\/)([^"]+)"/<img src="$url$1"/ig;
317 sub genrss ($@) { #{{{
321 my $url="$config{url}/".htmlpage($page);
323 my $template=HTML::Template->new(blind_cache => 1,
324 filename => "$config{templatedir}/rsspage.tmpl");
327 foreach my $p (@pages) {
329 itemtitle => pagetitle(basename($p)),
330 itemurl => "$config{url}/$renderedfiles{$p}",
331 itempubdate => date_822($pagectime{$p}),
332 itemcontent => absolute_urls(get_inline_content($page, $p), $url),
333 } if exists $renderedfiles{$p};
337 title => $config{wikiname},
342 return $template->output;
345 sub check_overwrite ($$) { #{{{
346 # Important security check. Make sure to call this before saving
347 # any files to the source directory.
351 if (! exists $renderedfiles{$src} && -e $dest && ! $config{rebuild}) {
352 error("$dest already exists and was rendered from ".
353 join(" ",(grep { $renderedfiles{$_} eq $dest } keys
355 ", before, so not rendering from $src");
362 return (stat($file))[9];
365 sub findlinks ($$) { #{{{
370 while ($content =~ /(?<!\\)$config{wiki_link_regexp}/g) {
371 push @links, titlepage($2);
373 # Discussion links are a special case since they're not in the text
374 # of the page, but on its template.
375 return @links, "$page/discussion";
378 sub render ($) { #{{{
381 my $type=pagetype($file);
382 my $srcfile=srcfile($file);
383 if ($type ne 'unknown') {
384 my $content=readfile($srcfile);
385 my $page=pagename($file);
387 $links{$page}=[findlinks($content, $page)];
388 delete $depends{$page};
390 $content=linkify($content, $page);
391 $content=preprocess($page, $content);
392 $content=htmlize($type, $content);
394 check_overwrite("$config{destdir}/".htmlpage($page), $page);
395 writefile(htmlpage($page), $config{destdir},
396 genpage($content, $page, mtime($srcfile)));
397 $oldpagemtime{$page}=time;
398 $renderedfiles{$page}=htmlpage($page);
401 my $content=readfile($srcfile, 1);
403 check_overwrite("$config{destdir}/$file", $file);
404 writefile($file, $config{destdir}, $content, 1);
405 $oldpagemtime{$file}=time;
406 $renderedfiles{$file}=$file;
414 my $dir=dirname($file);
415 while (rmdir($dir)) {
421 my $estdir="$config{wikistatedir}/hyperestraier";
422 my $cgi=basename($config{cgiurl});
424 open(TEMPLATE, ">$estdir/$cgi.tmpl") ||
425 error("write $estdir/$cgi.tmpl: $!");
426 print TEMPLATE misctemplate("search",
427 "<!--ESTFORM-->\n\n<!--ESTRESULT-->\n\n<!--ESTINFO-->\n\n");
429 open(TEMPLATE, ">$estdir/$cgi.conf") ||
430 error("write $estdir/$cgi.conf: $!");
431 my $template=HTML::Template->new(
432 filename => "$config{templatedir}/estseek.conf"
434 eval q{use Cwd 'abs_path'};
437 tmplfile => "$estdir/$cgi.tmpl",
438 destdir => abs_path($config{destdir}),
441 print TEMPLATE $template->output;
443 $cgi="$estdir/".basename($config{cgiurl});
445 symlink("/usr/lib/estraier/estseek.cgi", $cgi) ||
446 error("symlink $cgi: $!");
449 sub estcmd ($;@) { #{{{
450 my @params=split(' ', shift);
451 push @params, "-cl", "$config{wikistatedir}/hyperestraier";
456 my $pid=open(CHILD, "|-");
462 close(CHILD) || error("estcmd @params exited nonzero: $?");
466 open(STDOUT, "/dev/null"); # shut it up (closing won't work)
467 exec("estcmd", @params) || error("can't run estcmd");
471 sub refresh () { #{{{
472 # find existing pages
475 eval q{use File::Find};
479 if (/$config{wiki_file_prune_regexp}/) {
480 $File::Find::prune=1;
482 elsif (! -d $_ && ! -l $_) {
483 my ($f)=/$config{wiki_file_regexp}/; # untaint
485 warn("skipping bad filename $_\n");
488 $f=~s/^\Q$config{srcdir}\E\/?//;
490 $exists{pagename($f)}=1;
498 if (/$config{wiki_file_prune_regexp}/) {
499 $File::Find::prune=1;
501 elsif (! -d $_ && ! -l $_) {
502 my ($f)=/$config{wiki_file_regexp}/; # untaint
504 warn("skipping bad filename $_\n");
507 # Don't add files that are in the
509 $f=~s/^\Q$config{underlaydir}\E\/?//;
510 if (! -e "$config{srcdir}/$f" &&
511 ! -l "$config{srcdir}/$f") {
513 $exists{pagename($f)}=1;
518 }, $config{underlaydir});
522 # check for added or removed pages
524 foreach my $file (@files) {
525 my $page=pagename($file);
526 if (! $oldpagemtime{$page}) {
527 debug("new page $page") unless exists $pagectime{$page};
530 $pagesources{$page}=$file;
531 $pagectime{$page}=mtime(srcfile($file))
532 unless exists $pagectime{$page};
536 foreach my $page (keys %oldpagemtime) {
537 if (! $exists{$page}) {
538 debug("removing old page $page");
539 push @del, $pagesources{$page};
540 prune($config{destdir}."/".$renderedfiles{$page});
541 delete $renderedfiles{$page};
542 $oldpagemtime{$page}=0;
543 delete $pagesources{$page};
547 # render any updated files
548 foreach my $file (@files) {
549 my $page=pagename($file);
551 if (! exists $oldpagemtime{$page} ||
552 mtime(srcfile($file)) > $oldpagemtime{$page}) {
553 debug("rendering changed file $file");
559 # if any files were added or removed, check to see if each page
560 # needs an update due to linking to them or inlining them.
561 # TODO: inefficient; pages may get rendered above and again here;
562 # problem is the bestlink may have changed and we won't know until
565 FILE: foreach my $file (@files) {
566 my $page=pagename($file);
567 foreach my $f (@add, @del) {
569 foreach my $link (@{$links{$page}}) {
570 if (bestlink($page, $link) eq $p) {
571 debug("rendering $file, which links to $p");
581 # Handle backlinks; if a page has added/removed links, update the
582 # pages it links to. Also handles rebuilding dependat pages.
583 # TODO: inefficient; pages may get rendered above and again here;
584 # problem is the backlinks could be wrong in the first pass render
586 if (%rendered || @del) {
587 foreach my $f (@files) {
589 if (exists $depends{$p}) {
590 foreach my $file (keys %rendered, @del) {
591 my $page=pagename($file);
592 if (globlist_match($page, $depends{$p})) {
593 debug("rendering $f, which depends on $page");
603 foreach my $file (keys %rendered, @del) {
604 my $page=pagename($file);
606 if (exists $links{$page}) {
607 foreach my $link (map { bestlink($page, $_) } @{$links{$page}}) {
609 ! exists $oldlinks{$page} ||
610 ! grep { $_ eq $link } @{$oldlinks{$page}}) {
611 $linkchanged{$link}=1;
615 if (exists $oldlinks{$page}) {
616 foreach my $link (map { bestlink($page, $_) } @{$oldlinks{$page}}) {
618 ! exists $links{$page} ||
619 ! grep { $_ eq $link } @{$links{$page}}) {
620 $linkchanged{$link}=1;
625 foreach my $link (keys %linkchanged) {
626 my $linkfile=$pagesources{$link};
627 if (defined $linkfile) {
628 debug("rendering $linkfile, to update its backlinks");
630 $rendered{$linkfile}=1;
635 if ($config{hyperestraier} && (%rendered || @del)) {
636 debug("updating hyperestraier search index");
638 estcmd("gather -cm -bc -cl -sd",
639 map { $config{destdir}."/".$renderedfiles{pagename($_)} }
646 debug("generating hyperestraier cgi config");