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 $plugins{preprocess}{$command}) {
139 while ($params =~ /(\w+)=\"([^"]+)"(\s+|$)/g) {
142 return $plugins{preprocess}{$command}->(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;
165 sub genpage ($$$) { #{{{
170 my $title=pagetitle(basename($page));
172 my $template=HTML::Template->new(blind_cache => 1,
173 filename => "$config{templatedir}/page.tmpl");
175 if (length $config{cgiurl}) {
176 $template->param(editurl => cgiurl(do => "edit", page => $page));
177 $template->param(prefsurl => cgiurl(do => "prefs"));
179 $template->param(recentchangesurl => cgiurl(do => "recentchanges"));
183 if (length $config{historyurl}) {
184 my $u=$config{historyurl};
185 $u=~s/\[\[file\]\]/$pagesources{$page}/g;
186 $template->param(historyurl => $u);
188 if ($config{hyperestraier}) {
189 $template->param(hyperestraierurl => cgiurl());
194 wikiname => $config{wikiname},
195 parentlinks => [parentlinks($page)],
197 backlinks => [backlinks($page)],
198 discussionlink => htmllink($page, "Discussion", 1, 1),
199 mtime => scalar(gmtime($mtime)),
200 styleurl => styleurl($page),
203 return $template->output;
206 sub check_overwrite ($$) { #{{{
207 # Important security check. Make sure to call this before saving
208 # any files to the source directory.
212 if (! exists $renderedfiles{$src} && -e $dest && ! $config{rebuild}) {
213 error("$dest already exists and was rendered from ".
214 join(" ",(grep { $renderedfiles{$_} eq $dest } keys
216 ", before, so not rendering from $src");
223 return (stat($file))[9];
226 sub findlinks ($$) { #{{{
231 while ($content =~ /(?<!\\)$config{wiki_link_regexp}/g) {
232 push @links, titlepage($2);
234 # Discussion links are a special case since they're not in the text
235 # of the page, but on its template.
236 return @links, "$page/discussion";
239 sub render ($) { #{{{
242 my $type=pagetype($file);
243 my $srcfile=srcfile($file);
244 if ($type ne 'unknown') {
245 my $content=readfile($srcfile);
246 my $page=pagename($file);
248 $links{$page}=[findlinks($content, $page)];
249 delete $depends{$page};
251 $content=linkify($content, $page);
252 $content=preprocess($page, $content);
253 $content=htmlize($type, $content);
255 check_overwrite("$config{destdir}/".htmlpage($page), $page);
256 writefile(htmlpage($page), $config{destdir},
257 genpage($content, $page, mtime($srcfile)));
258 $oldpagemtime{$page}=time;
259 $renderedfiles{$page}=htmlpage($page);
262 my $content=readfile($srcfile, 1);
264 delete $depends{$file};
265 check_overwrite("$config{destdir}/$file", $file);
266 writefile($file, $config{destdir}, $content, 1);
267 $oldpagemtime{$file}=time;
268 $renderedfiles{$file}=$file;
276 my $dir=dirname($file);
277 while (rmdir($dir)) {
283 my $estdir="$config{wikistatedir}/hyperestraier";
284 my $cgi=basename($config{cgiurl});
286 open(TEMPLATE, ">$estdir/$cgi.tmpl") ||
287 error("write $estdir/$cgi.tmpl: $!");
288 print TEMPLATE misctemplate("search",
289 "<!--ESTFORM-->\n\n<!--ESTRESULT-->\n\n<!--ESTINFO-->\n\n");
291 open(TEMPLATE, ">$estdir/$cgi.conf") ||
292 error("write $estdir/$cgi.conf: $!");
293 my $template=HTML::Template->new(
294 filename => "$config{templatedir}/estseek.conf"
296 eval q{use Cwd 'abs_path'};
299 tmplfile => "$estdir/$cgi.tmpl",
300 destdir => abs_path($config{destdir}),
303 print TEMPLATE $template->output;
305 $cgi="$estdir/".basename($config{cgiurl});
307 symlink("/usr/lib/estraier/estseek.cgi", $cgi) ||
308 error("symlink $cgi: $!");
311 sub estcmd ($;@) { #{{{
312 my @params=split(' ', shift);
313 push @params, "-cl", "$config{wikistatedir}/hyperestraier";
318 my $pid=open(CHILD, "|-");
324 close(CHILD) || error("estcmd @params exited nonzero: $?");
328 open(STDOUT, "/dev/null"); # shut it up (closing won't work)
329 exec("estcmd", @params) || error("can't run estcmd");
333 sub refresh () { #{{{
334 # find existing pages
337 eval q{use File::Find};
341 if (/$config{wiki_file_prune_regexp}/) {
342 $File::Find::prune=1;
344 elsif (! -d $_ && ! -l $_) {
345 my ($f)=/$config{wiki_file_regexp}/; # untaint
347 warn("skipping bad filename $_\n");
350 $f=~s/^\Q$config{srcdir}\E\/?//;
352 $exists{pagename($f)}=1;
360 if (/$config{wiki_file_prune_regexp}/) {
361 $File::Find::prune=1;
363 elsif (! -d $_ && ! -l $_) {
364 my ($f)=/$config{wiki_file_regexp}/; # untaint
366 warn("skipping bad filename $_\n");
369 # Don't add files that are in the
371 $f=~s/^\Q$config{underlaydir}\E\/?//;
372 if (! -e "$config{srcdir}/$f" &&
373 ! -l "$config{srcdir}/$f") {
375 $exists{pagename($f)}=1;
380 }, $config{underlaydir});
384 # check for added or removed pages
386 foreach my $file (@files) {
387 my $page=pagename($file);
388 if (! $oldpagemtime{$page}) {
389 debug("new page $page") unless exists $pagectime{$page};
392 $pagesources{$page}=$file;
393 $pagectime{$page}=mtime(srcfile($file))
394 unless exists $pagectime{$page};
398 foreach my $page (keys %oldpagemtime) {
399 if (! $exists{$page}) {
400 debug("removing old page $page");
401 push @del, $pagesources{$page};
402 prune($config{destdir}."/".$renderedfiles{$page});
403 delete $renderedfiles{$page};
404 $oldpagemtime{$page}=0;
405 delete $pagesources{$page};
409 # render any updated files
410 foreach my $file (@files) {
411 my $page=pagename($file);
413 if (! exists $oldpagemtime{$page} ||
414 mtime(srcfile($file)) > $oldpagemtime{$page}) {
415 debug("rendering changed file $file");
421 # if any files were added or removed, check to see if each page
422 # needs an update due to linking to them or inlining them.
423 # TODO: inefficient; pages may get rendered above and again here;
424 # problem is the bestlink may have changed and we won't know until
427 FILE: foreach my $file (@files) {
428 my $page=pagename($file);
429 foreach my $f (@add, @del) {
431 foreach my $link (@{$links{$page}}) {
432 if (bestlink($page, $link) eq $p) {
433 debug("rendering $file, which links to $p");
443 # Handle backlinks; if a page has added/removed links, update the
444 # pages it links to. Also handles rebuilding dependat pages.
445 # TODO: inefficient; pages may get rendered above and again here;
446 # problem is the backlinks could be wrong in the first pass render
448 if (%rendered || @del) {
449 foreach my $f (@files) {
451 if (exists $depends{$p}) {
452 foreach my $file (keys %rendered, @del) {
454 my $page=pagename($file);
455 if (globlist_match($page, $depends{$p})) {
456 debug("rendering $f, which depends on $page");
466 foreach my $file (keys %rendered, @del) {
467 my $page=pagename($file);
469 if (exists $links{$page}) {
470 foreach my $link (map { bestlink($page, $_) } @{$links{$page}}) {
472 (! exists $oldlinks{$page} ||
473 ! grep { bestlink($page, $_) eq $link } @{$oldlinks{$page}})) {
474 $linkchanged{$link}=1;
478 if (exists $oldlinks{$page}) {
479 foreach my $link (map { bestlink($page, $_) } @{$oldlinks{$page}}) {
481 (! exists $links{$page} ||
482 ! grep { bestlink($page, $_) eq $link } @{$links{$page}})) {
483 $linkchanged{$link}=1;
488 foreach my $link (keys %linkchanged) {
489 my $linkfile=$pagesources{$link};
490 if (defined $linkfile) {
491 debug("rendering $linkfile, to update its backlinks");
493 $rendered{$linkfile}=1;
498 if ($config{hyperestraier} && (%rendered || @del)) {
499 debug("updating hyperestraier search index");
501 estcmd("gather -cm -bc -cl -sd",
502 map { $config{destdir}."/".$renderedfiles{pagename($_)} }
509 debug("generating hyperestraier cgi config");