web commit by http://ethan.betacantrips.com/: new plugin
authorjoey <joey@0fa5a96a-9a0e-0410-b3b2-a0fd24251071>
Sun, 29 Jul 2007 04:05:35 +0000 (04:05 +0000)
committerjoey <joey@0fa5a96a-9a0e-0410-b3b2-a0fd24251071>
Sun, 29 Jul 2007 04:05:35 +0000 (04:05 +0000)
doc/todo/missingparents.pm.mdwn [new file with mode: 0644]

diff --git a/doc/todo/missingparents.pm.mdwn b/doc/todo/missingparents.pm.mdwn
new file mode 100644 (file)
index 0000000..152c824
--- /dev/null
@@ -0,0 +1,266 @@
+This is another blogging support thing, and it relies on 
+[[pagespec_relative_to_a_target]] (but only to figure out whether a given page
+has a child). Basically, you give it a page called missingparents.mdwn,
+something like this:
+
+<pre>
+[[missingparents pages="posts/* and !posts/*/*" generate="""[[template id=year text="$page"]]"""]]
+[[missingparents pages="posts/*/* and !posts/*/*/*" generate="""[[template id=month text="$page"]]"""]]
+[[missingparents pages="posts/*/*/* and !posts/*/*/*/*" generate="""[[template id=day text="$page"]]"""]]
+</pre>
+
+And it scans the whole wiki for pages that match the pagespecs but are missing
+parents. If any are found, they are generated automatically using the text in
+the "generate" parameter (except $page is substituted for the page title).
+*These generated pages aren't kept in version control*, but of course they're
+ordinary wiki pages and can be edited by the web form or otherwise added, at 
+which point the missingparents plugin lets go of them. (TODO: CGI.pm needs to
+know to rcs_add these pages if they are edited, and it doesn't.) If all of the
+children of a missingparent page goes away, the missingparent itself is 
+unlinked automatically, and all missingparents are deleted on wiki rebuild.
+
+To implement this, I needed to tell ikiwiki that pages were being added and
+removed in a non-standard way, and so created functions newpage and delpage
+in the IkiWiki namespace to do these things. delpage is modeled on the 
+Render.pm code that deletes pages, so I re-used it in Render.pm. I also
+needed a way to add files to be deleted on a refresh(), so I added a 
+needsdelete hook, parallel in form to needsbuild.
+
+This patch, or one like it, would enable better blogging support, by adding
+the ability to hierarchically organize blog posts and automatically generate
+structural pages for year, month, or day. Please apply. --Ethan
+
+<pre>
+Index: IkiWiki/Render.pm
+===================================================================
+--- IkiWiki/Render.pm  (revision 3926)
++++ IkiWiki/Render.pm  (working copy)
+@@ -322,17 +322,7 @@
+               if (! $exists{$page}) {
+                       debug(sprintf(gettext("removing old page %s"), $page));
+                       push @del, $pagesources{$page};
+-                      $links{$page}=[];
+-                      $renderedfiles{$page}=[];
+-                      $pagemtime{$page}=0;
+-                      prune($config{destdir}."/".$_)
+-                              foreach @{$oldrenderedfiles{$page}};
+-                      delete $pagesources{$page};
+-                      foreach (keys %destsources) {
+-                              if ($destsources{$_} eq $page) {
+-                                      delete $destsources{$_};
+-                              }
+-                      }
++                      delpage($page);
+               }
+       }
+@@ -377,6 +367,10 @@
+               }
+       }
++      if (@del) {
++              run_hooks(needsdelete => sub { shift->(\@del) });
++      }
++
+       if (%rendered || @del) {
+               # rebuild dependant pages
+               foreach my $f (@files) {
+Index: IkiWiki/Plugin/missingparents.pm
+===================================================================
+--- IkiWiki/Plugin/missingparents.pm   (revision 0)
++++ IkiWiki/Plugin/missingparents.pm   (revision 0)
+@@ -0,0 +1,136 @@
++#!/usr/bin/perl
++# missingparents plugin: detect missing parents of pages and create them
++package IkiWiki::Plugin::missingparents;
++
++use warnings;
++use strict;
++use IkiWiki 2.00;
++use IkiWiki::Plugin::relative;
++
++my %ownfiles;
++my @pagespecs;
++
++sub import { #{{{
++      hook(type => "checkconfig", id => "missingparents", call => \&checkconfig);
++      hook(type => "needsdelete", id => "missingparents", call => \&needsdelete);
++      hook(type => "needsbuild", id => "missingparents", call => \&needsbuild);
++      hook(type => "savestate", id => "missingparents", call => \&savestate);
++      hook(type => "preprocess", id => "missingparents", call => \&preprocess_missingparents);
++} # }}}
++
++sub checkconfig () { #{{{
++      IkiWiki::preprocess("missingparents", "missingparents",
++              readfile(srcfile("missingparents.mdwn")));
++      loadstate();
++      if ($config{rebuild}){
++              foreach my $file (keys %ownfiles) {
++                      unlink $config{srcdir}.'/'.$file;
++              }
++      }
++} #}}}
++
++sub preprocess_missingparents (@) { #{{{
++      my %params=@_;
++
++      if (! defined $params{pages} || ! defined $params{generate}) {
++              return "[[missingparents ".gettext("missing pages or generate parameter")."]]";
++      }
++
++      push @pagespecs, \%params;
++
++      #translators: This is used to display what missingparents are defined.
++      #translators: First parameter is a pagespec, the second
++      #translators: is text for pages that match that pagespec.
++      return sprintf(gettext("missingparents in %s will be %s"), 
++                     '`'.$params{pages}.'`', '`\\'.$params{generate}.'`');
++} # }}}
++
++my $state_loaded=0;
++sub loadstate() { #{{{
++      my $filename = "$config{wikistatedir}/missingparents";
++      if (-e $filename) {
++              open (IN, $filename) ||
++                    die "$filename: $!";
++              while (<IN>) {
++                      chomp;
++                      $ownfiles{$_} = 1;
++              }
++
++              close IN;
++
++              $state_loaded=1;
++      }
++} #}}}
++
++sub savestate() { #{{{
++      my $filename = "$config{wikistatedir}/missingparents.new";
++      my $cleanup = sub { unlink ($filename) };
++      open (OUT, ">$filename") || error("open $filename: $!", $cleanup);
++      foreach my $data (keys %ownfiles) {
++              print OUT "$data\n" if $ownfiles{$data};
++      }
++      rename($filename, "$config{wikistatedir}/missingparents") ||
++              error("rename $filename: $!", $cleanup);
++} #}}}
++
++sub needsdelete (@) { #{{{
++      my $files=shift;
++      
++      my @mydel;
++      my $pruned = 1;
++      do {
++              $pruned = 0;
++              foreach my $file (keys %ownfiles) {
++                      my $page = pagename($file);
++                      if (! IkiWiki::PageSpec::match_has_child($page, "")) {
++                              # No children -- get rid of it
++                              push @mydel, $page;
++                              delete $ownfiles{$file};
++                              IkiWiki::delpage($page);
++                              unlink $config{srcdir}."/".$file;
++                              $pruned = 1;
++                      }
++              }
++      } while($pruned);
++      foreach my $page (@mydel){
++              push @{$files}, $page;
++      }
++} #}}}
++
++sub check_matches($) { #{{{
++      my $page = shift;
++      return if $IkiWiki::pagesources{$page};
++
++      foreach my $miss (@pagespecs) {
++              next unless pagespec_match($page, $miss->{pages});
++              my $text = $miss->{generate};
++              $text =~ s/\$page/$page/;
++              my $output = $page.".mdwn";
++              writefile($output, "$config{srcdir}/", $text);
++              IkiWiki::newpage($output, $page);
++              return $output;
++      }
++      return "";
++} #}}}
++
++sub needsbuild ($) { #{{{
++      my $files=shift;
++      my @new;
++
++      foreach my $file (@{$files}) {
++              my $page = pagename $file;
++              my $newfile = "";
++              foreach my $parent (split '/', $page) {
++                      $newfile .= $parent;
++                      my $output = check_matches($newfile);
++                      push @new, $output if $output;
++                      $newfile .= "/";
++              }
++      }
++      foreach my $file (@new) {
++              $ownfiles{$file} = 1;
++              push @{$files}, $file;
++      }
++} #}}}
++
++1
+Index: IkiWiki/Plugin/rst.pm
+===================================================================
+--- IkiWiki/Plugin/rst.pm      (revision 3926)
++++ IkiWiki/Plugin/rst.pm      (working copy)
+@@ -25,7 +25,7 @@
+ html = publish_string(stdin.read(), writer_name='html', 
+        settings_overrides = { 'halt_level': 6, 
+                               'file_insertion_enabled': 0,
+-                              'raw_enabled': 0 }
++                              'raw_enabled': 1 }
+ );
+ print html[html.find('<body>')+6:html.find('</body>')].strip();
+ ";
+Index: IkiWiki.pm
+===================================================================
+--- IkiWiki.pm (revision 3926)
++++ IkiWiki.pm (working copy)
+@@ -16,7 +16,7 @@
+ use Exporter q{import};
+ our @EXPORT = qw(hook debug error template htmlpage add_depends pagespec_match
+                  bestlink htmllink readfile writefile pagetype srcfile pagename
+-                 displaytime will_render gettext urlto targetpage
++                 displaytime will_render gettext urlto targetpage newpage delpage
+                  %config %links %renderedfiles %pagesources %destsources);
+ our $VERSION = 2.00; # plugin interface version, next is ikiwiki version
+ our $version='unknown'; # VERSION_AUTOREPLACE done by Makefile, DNE
+@@ -330,6 +336,30 @@
+               error("failed renaming $newfile to $destdir/$file: $!", $cleanup);
+ } #}}}
++sub newpage($$) { #{{{
++      my $file=shift;
++      my $page=shift;
++
++      $pagemtime{$page} = $pagectime{$page} = time;
++      $pagesources{$page} = $file;
++      $pagecase{lc $page} = $page;
++} #}}}
++
++sub delpage($) { #{{{
++      my $page=shift;
++      $links{$page}=[];
++      $renderedfiles{$page}=[];
++      $pagemtime{$page}=0;
++      prune($config{destdir}."/".$_)
++          foreach @{$oldrenderedfiles{$page}};
++      delete $pagesources{$page};
++      foreach (keys %destsources) {
++              if ($destsources{$_} eq $page) {
++                      delete $destsources{$_};
++                      }
++              }
++} #}}}
++
+ my %cleared;
+ sub will_render ($$;$) { #{{{
+       my $page=shift;
+</pre>
\ No newline at end of file