add --refresh and make it with with --setup
[ikiwiki.git] / IkiWiki / Render.pm
index df24fd5680ab13a3bc74f3a35bd979bfd93553ba..dc1fc54e7a56770a18dfae04354c77619a573ad2 100644 (file)
@@ -79,11 +79,122 @@ sub parentlinks ($) { #{{{
        return @ret;
 } #}}}
 
        return @ret;
 } #}}}
 
-sub finalize ($$$) { #{{{
+sub rsspage ($) { #{{{
+       my $page=shift;
+
+       return $page.".rss";
+} #}}}
+
+sub postprocess { #{{{
+       # Takes content to postprocess followed by a list of postprocessor
+       # commands and subroutine references to run for the commands.
+       my $page=shift;
+       my $content=shift;
+       my %commands=@_;
+       
+       my $handle=sub {
+               my $escape=shift;
+               my $command=shift;
+               my $params=shift;
+               if (length $escape) {
+                       "[[$command $params]]";
+               }
+               elsif (exists $commands{$command}) {
+                       my %params;
+                       while ($params =~ /(\w+)=\"([^"]+)"(\s+|$)/g) {
+                               $params{$1}=$2;
+                       }
+                       $commands{$command}->($page, %params);
+               }
+               else {
+                       "[[bad directive $command]]";
+               }
+       };
+       
+       $content =~ s{(\\?)$config{wiki_processor_regexp}}{$handle->($1, $2, $3)}eg;
+       return $content;
+} #}}}
+
+sub blog_list ($$) { #{{{
+       my $globlist=shift;
+       my $maxitems=shift;
+       
+       my @list;
+       foreach my $page (keys %pagesources) {
+               if (globlist_match($page, $globlist)) {
+                       push @list, $page;
+               }
+       }
+
+       @list=sort { $pagectime{$b} <=> $pagectime{$a} } @list;
+       return @list if ! $maxitems || @list <= $maxitems;
+       return @list[0..$maxitems - 1];
+} #}}}
+
+sub get_inline_content ($$) { #{{{
+       my $parentpage=shift;
+       my $page=shift;
+       
+       my $file=$pagesources{$page};
+       my $type=pagetype($file);
+       if ($type ne 'unknown') {
+               return htmlize($type, linkify(readfile("$config{srcdir}/$file"), $parentpage));
+       }
+       else {
+               return "";
+       }
+} #}}}
+
+sub postprocess_html_inline { #{{{
+       my $parentpage=shift;
+       my %params=@_;
+       
+       if (! exists $params{pages}) {
+               return "";
+       }
+       if (! exists $params{archive}) {
+               $params{archive}="no";
+       }
+       if (! exists $params{show} && $params{archive} eq "no") {
+               $params{show}=10;
+       }
+       $inlinepages{$parentpage}=$params{pages};
+       
+       my $ret="";
+       
+       if (exists $params{rootpage}) {
+               my $formtemplate=HTML::Template->new(blind_cache => 1,
+                       filename => "$config{templatedir}/blogpost.tmpl");
+               $formtemplate->param(cgiurl => $config{cgiurl});
+               $formtemplate->param(rootpage => $params{rootpage});
+               my $form=$formtemplate->output;
+               $ret.=$form;
+       }
+       
+       my $template=HTML::Template->new(blind_cache => 1,
+               filename => (($params{archive} eq "no") 
+                               ? "$config{templatedir}/inlinepage.tmpl"
+                               : "$config{templatedir}/inlinepagetitle.tmpl"));
+       
+       foreach my $page (blog_list($params{pages}, $params{show})) {
+               next if $page eq $parentpage;
+               $template->param(pagelink => htmllink($parentpage, $page));
+               $template->param(content => get_inline_content($parentpage, $page))
+                       if $params{archive} eq "no";
+               $template->param(ctime => scalar(gmtime($pagectime{$page})));
+               $ret.=$template->output;
+       }
+       
+       return $ret;
+} #}}}
+
+sub genpage ($$$) { #{{{
        my $content=shift;
        my $page=shift;
        my $mtime=shift;
 
        my $content=shift;
        my $page=shift;
        my $mtime=shift;
 
+       $content = postprocess($page, $content, inline => \&postprocess_html_inline);
+       
        my $title=pagetitle(basename($page));
        
        my $template=HTML::Template->new(blind_cache => 1,
        my $title=pagetitle(basename($page));
        
        my $template=HTML::Template->new(blind_cache => 1,
@@ -102,6 +213,10 @@ sub finalize ($$$) { #{{{
                $u=~s/\[\[file\]\]/$pagesources{$page}/g;
                $template->param(historyurl => $u);
        }
                $u=~s/\[\[file\]\]/$pagesources{$page}/g;
                $template->param(historyurl => $u);
        }
+
+       if ($config{rss} && $inlinepages{$page}) {
+               $template->param(rssurl => rsspage(basename($page)));
+       }
        
        $template->param(
                title => $title,
        
        $template->param(
                title => $title,
@@ -116,6 +231,75 @@ sub finalize ($$$) { #{{{
        return $template->output;
 } #}}}
 
        return $template->output;
 } #}}}
 
+sub date_822 ($) { #{{{
+       my $time=shift;
+
+       eval q{use POSIX};
+       return POSIX::strftime("%a, %d %b %Y %H:%M:%S %z", localtime($time));
+} #}}}
+
+sub absolute_urls ($$) { #{{{
+       # sucky sub because rss sucks
+       my $content=shift;
+       my $url=shift;
+
+       $url=~s/[^\/]+$//;
+       
+       $content=~s/<a\s+href="(?!http:\/\/)([^"]+)"/<a href="$url$1"/ig;
+       $content=~s/<img\s+src="(?!http:\/\/)([^"]+)"/<img src="$url$1"/ig;
+       return $content;
+} #}}}
+
+sub genrss ($$$) { #{{{
+       my $content=shift;
+       my $page=shift;
+       my $mtime=shift;
+       
+       my $url="$config{url}/".htmlpage($page);
+       
+       my $template=HTML::Template->new(blind_cache => 1,
+               filename => "$config{templatedir}/rsspage.tmpl");
+       
+       my @items;
+       my $isblog=0;
+       my $gen_blog=sub {
+               my $parentpage=shift;
+               my %params=@_;
+               
+               return "" if exists $params{archive} && $params{archive} eq 'yes';
+               
+               if (! exists $params{show}) {
+                       $params{show}=10;
+               }
+               if (! exists $params{pages}) {
+                       return "";
+               }
+               
+               $isblog=1;
+               foreach my $page (blog_list($params{pages}, $params{show})) {
+                       next if $page eq $parentpage;
+                       push @items, {
+                               itemtitle => pagetitle(basename($page)),
+                               itemurl => "$config{url}/$renderedfiles{$page}",
+                               itempubdate => date_822($pagectime{$page}),
+                               itemcontent => absolute_urls(get_inline_content($parentpage, $page), $url),
+                       } if exists $renderedfiles{$page};
+               }
+               
+               return "";
+       };
+       
+       $content = postprocess($page, $content, inline => $gen_blog);
+
+       $template->param(
+               title => $config{wikiname},
+               pageurl => $url,
+               items => \@items,
+       );
+       
+       return $template->output;
+} #}}}
+
 sub check_overwrite ($$) { #{{{
        # Important security check. Make sure to call this before saving
        # any files to the source directory.
 sub check_overwrite ($$) { #{{{
        # Important security check. Make sure to call this before saving
        # any files to the source directory.
@@ -131,9 +315,9 @@ sub check_overwrite ($$) { #{{{
 } #}}}
 
 sub mtime ($) { #{{{
 } #}}}
 
 sub mtime ($) { #{{{
-       my $page=shift;
+       my $file=shift;
        
        
-       return (stat($page))[9];
+       return (stat($file))[9];
 } #}}}
 
 sub findlinks ($$) { #{{{
 } #}}}
 
 sub findlinks ($$) { #{{{
@@ -158,16 +342,24 @@ sub render ($) { #{{{
                my $page=pagename($file);
                
                $links{$page}=[findlinks($content, $page)];
                my $page=pagename($file);
                
                $links{$page}=[findlinks($content, $page)];
+               delete $inlinepages{$page};
                
                $content=linkify($content, $page);
                $content=htmlize($type, $content);
                
                $content=linkify($content, $page);
                $content=htmlize($type, $content);
-               $content=finalize($content, $page,
-                       mtime("$config{srcdir}/$file"));
                
                check_overwrite("$config{destdir}/".htmlpage($page), $page);
                
                check_overwrite("$config{destdir}/".htmlpage($page), $page);
-               writefile("$config{destdir}/".htmlpage($page), $content);
+               writefile("$config{destdir}/".htmlpage($page),
+                       genpage($content, $page, mtime("$config{srcdir}/$file")));
                $oldpagemtime{$page}=time;
                $renderedfiles{$page}=htmlpage($page);
                $oldpagemtime{$page}=time;
                $renderedfiles{$page}=htmlpage($page);
+
+               # TODO: should really add this to renderedfiles and call
+               # check_overwrite, as above, but currently renderedfiles
+               # only supports listing one file per page.
+               if ($config{rss} && exists $inlinepages{$page}) {
+                       writefile("$config{destdir}/".rsspage($page),
+                               genrss($content, $page, mtime("$config{srcdir}/$file")));
+               }
        }
        else {
                $links{$file}=[];
        }
        else {
                $links{$file}=[];
@@ -222,10 +414,12 @@ sub refresh () { #{{{
        foreach my $file (@files) {
                my $page=pagename($file);
                if (! $oldpagemtime{$page}) {
        foreach my $file (@files) {
                my $page=pagename($file);
                if (! $oldpagemtime{$page}) {
-                       debug("new page $page");
+                       debug("new page $page") unless exists $pagectime{$page};
                        push @add, $file;
                        $links{$page}=[];
                        $pagesources{$page}=$file;
                        push @add, $file;
                        $links{$page}=[];
                        $pagesources{$page}=$file;
+                       $pagectime{$page}=mtime("$config{srcdir}/$file") 
+                               unless exists $pagectime{$page};
                }
        }
        my @del;
                }
        }
        my @del;
@@ -253,7 +447,7 @@ sub refresh () { #{{{
        }
        
        # if any files were added or removed, check to see if each page
        }
        
        # if any files were added or removed, check to see if each page
-       # needs an update due to linking to them
+       # needs an update due to linking to them or inlining them.
        # TODO: inefficient; pages may get rendered above and again here;
        # problem is the bestlink may have changed and we won't know until
        # now
        # TODO: inefficient; pages may get rendered above and again here;
        # problem is the bestlink may have changed and we won't know until
        # now
@@ -274,15 +468,30 @@ FILE:             foreach my $file (@files) {
                }
        }
 
                }
        }
 
-       # handle backlinks; if a page has added/removed links, update the
-       # pages it links to
+       # Handle backlinks; if a page has added/removed links, update the
+       # pages it links to. Also handle inlining here.
        # TODO: inefficient; pages may get rendered above and again here;
        # problem is the backlinks could be wrong in the first pass render
        # above
        # TODO: inefficient; pages may get rendered above and again here;
        # problem is the backlinks could be wrong in the first pass render
        # above
-       if (%rendered) {
+       if (%rendered || @del) {
+               foreach my $f (@files) {
+                       my $p=pagename($f);
+                       if (exists $inlinepages{$p}) {
+                               foreach my $file (keys %rendered, @del) {
+                                       my $page=pagename($file);
+                                       if (globlist_match($page, $inlinepages{$p})) {
+                                               debug("rendering $f, which inlines $page");
+                                               render($f);
+                                               last;
+                                       }
+                               }
+                       }
+               }
+               
                my %linkchanged;
                foreach my $file (keys %rendered, @del) {
                        my $page=pagename($file);
                my %linkchanged;
                foreach my $file (keys %rendered, @del) {
                        my $page=pagename($file);
+                       
                        if (exists $links{$page}) {
                                foreach my $link (map { bestlink($page, $_) } @{$links{$page}}) {
                                        if (length $link &&
                        if (exists $links{$page}) {
                                foreach my $link (map { bestlink($page, $_) } @{$links{$page}}) {
                                        if (length $link &&