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 $template->param(headercontent => $config{headercontent});
213 wikiname => $config{wikiname},
214 parentlinks => [parentlinks($page)],
216 backlinks => [backlinks($page)],
217 discussionlink => htmllink($page, "Discussion", 1, 1),
218 mtime => scalar(gmtime($mtime)),
219 styleurl => styleurl($page),
222 return $template->output;
225 sub check_overwrite ($$) { #{{{
226 # Important security check. Make sure to call this before saving
227 # any files to the source directory.
231 if (! exists $renderedfiles{$src} && -e $dest && ! $config{rebuild}) {
232 error("$dest already exists and was rendered from ".
233 join(" ",(grep { $renderedfiles{$_} eq $dest } keys
235 ", before, so not rendering from $src");
242 return (stat($file))[9];
245 sub findlinks ($$) { #{{{
250 while ($content =~ /(?<!\\)$config{wiki_link_regexp}/g) {
251 push @links, titlepage($2);
253 # Discussion links are a special case since they're not in the text
254 # of the page, but on its template.
255 return @links, "$page/discussion";
258 sub render ($) { #{{{
261 my $type=pagetype($file);
262 my $srcfile=srcfile($file);
263 if ($type ne 'unknown') {
264 my $content=readfile($srcfile);
265 my $page=pagename($file);
267 $links{$page}=[findlinks($content, $page)];
268 delete $depends{$page};
270 $content=linkify($content, $page);
271 $content=preprocess($page, $content);
272 $content=htmlize($type, $content);
274 check_overwrite("$config{destdir}/".htmlpage($page), $page);
275 writefile(htmlpage($page), $config{destdir},
276 genpage($content, $page, mtime($srcfile)));
277 $oldpagemtime{$page}=time;
278 $renderedfiles{$page}=htmlpage($page);
281 my $content=readfile($srcfile, 1);
283 delete $depends{$file};
284 check_overwrite("$config{destdir}/$file", $file);
285 writefile($file, $config{destdir}, $content, 1);
286 $oldpagemtime{$file}=time;
287 $renderedfiles{$file}=$file;
295 my $dir=dirname($file);
296 while (rmdir($dir)) {
301 sub refresh () { #{{{
302 # find existing pages
305 eval q{use File::Find};
309 if (/$config{wiki_file_prune_regexp}/) {
310 $File::Find::prune=1;
312 elsif (! -d $_ && ! -l $_) {
313 my ($f)=/$config{wiki_file_regexp}/; # untaint
315 warn("skipping bad filename $_\n");
318 $f=~s/^\Q$config{srcdir}\E\/?//;
320 $exists{pagename($f)}=1;
328 if (/$config{wiki_file_prune_regexp}/) {
329 $File::Find::prune=1;
331 elsif (! -d $_ && ! -l $_) {
332 my ($f)=/$config{wiki_file_regexp}/; # untaint
334 warn("skipping bad filename $_\n");
337 # Don't add files that are in the
339 $f=~s/^\Q$config{underlaydir}\E\/?//;
340 if (! -e "$config{srcdir}/$f" &&
341 ! -l "$config{srcdir}/$f") {
343 $exists{pagename($f)}=1;
348 }, $config{underlaydir});
352 # check for added or removed pages
354 foreach my $file (@files) {
355 my $page=pagename($file);
356 if (! $oldpagemtime{$page}) {
357 debug("new page $page") unless exists $pagectime{$page};
360 $pagesources{$page}=$file;
361 $pagectime{$page}=mtime(srcfile($file))
362 unless exists $pagectime{$page};
366 foreach my $page (keys %oldpagemtime) {
367 if (! $exists{$page}) {
368 debug("removing old page $page");
369 push @del, $pagesources{$page};
370 prune($config{destdir}."/".$renderedfiles{$page});
371 delete $renderedfiles{$page};
372 $oldpagemtime{$page}=0;
373 delete $pagesources{$page};
377 # render any updated files
378 foreach my $file (@files) {
379 my $page=pagename($file);
381 if (! exists $oldpagemtime{$page} ||
382 mtime(srcfile($file)) > $oldpagemtime{$page}) {
383 debug("rendering changed file $file");
389 # if any files were added or removed, check to see if each page
390 # needs an update due to linking to them or inlining them.
391 # TODO: inefficient; pages may get rendered above and again here;
392 # problem is the bestlink may have changed and we won't know until
395 FILE: foreach my $file (@files) {
396 my $page=pagename($file);
397 foreach my $f (@add, @del) {
399 foreach my $link (@{$links{$page}}) {
400 if (bestlink($page, $link) eq $p) {
401 debug("rendering $file, which links to $p");
411 # Handle backlinks; if a page has added/removed links, update the
412 # pages it links to. Also handles rebuilding dependat pages.
413 # TODO: inefficient; pages may get rendered above and again here;
414 # problem is the backlinks could be wrong in the first pass render
416 if (%rendered || @del) {
417 foreach my $f (@files) {
419 if (exists $depends{$p}) {
420 foreach my $file (keys %rendered, @del) {
422 my $page=pagename($file);
423 if (globlist_match($page, $depends{$p})) {
424 debug("rendering $f, which depends on $page");
434 foreach my $file (keys %rendered, @del) {
435 my $page=pagename($file);
437 if (exists $links{$page}) {
438 foreach my $link (map { bestlink($page, $_) } @{$links{$page}}) {
440 (! exists $oldlinks{$page} ||
441 ! grep { bestlink($page, $_) eq $link } @{$oldlinks{$page}})) {
442 $linkchanged{$link}=1;
446 if (exists $oldlinks{$page}) {
447 foreach my $link (map { bestlink($page, $_) } @{$oldlinks{$page}}) {
449 (! exists $links{$page} ||
450 ! grep { bestlink($page, $_) eq $link } @{$links{$page}})) {
451 $linkchanged{$link}=1;
456 foreach my $link (keys %linkchanged) {
457 my $linkfile=$pagesources{$link};
458 if (defined $linkfile) {
459 debug("rendering $linkfile, to update its backlinks");
461 $rendered{$linkfile}=1;
466 if (@del && exists $hooks{delete}) {
467 foreach my $id (keys %{$hooks{delete}}) {
468 $hooks{delete}{$id}{call}->(@del);
471 if (%rendered && exists $hooks{render}) {
472 foreach my $id (keys %{$hooks{render}}) {
473 $hooks{render}{$id}{call}->(keys %rendered);