summaryrefslogtreecommitdiff
path: root/.library
diff options
context:
space:
mode:
Diffstat (limited to '.library')
-rw-r--r--.library/IkiWiki/Plugin/copyright.pm60
-rw-r--r--.library/IkiWiki/Plugin/field.pm722
-rw-r--r--.library/IkiWiki/Plugin/fill_empty_page.pm65
-rw-r--r--.library/IkiWiki/Plugin/getfield.pm126
-rw-r--r--.library/IkiWiki/Plugin/license.pm70
-rw-r--r--.library/IkiWiki/Plugin/reset_mtimes.pm84
-rw-r--r--.library/IkiWiki/Plugin/texinfo.pm226
-rw-r--r--.library/IkiWiki/Plugin/ymlfront.pm434
8 files changed, 1672 insertions, 115 deletions
diff --git a/.library/IkiWiki/Plugin/copyright.pm b/.library/IkiWiki/Plugin/copyright.pm
new file mode 100644
index 00000000..16acaccd
--- /dev/null
+++ b/.library/IkiWiki/Plugin/copyright.pm
@@ -0,0 +1,60 @@
+# A plugin for ikiwiki to implement adding a footer with copyright information
+# based on a default value taken out of a file.
+
+# Copyright © 2007, 2008 Thomas Schwinge <tschwinge@gnu.org>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+# Unless overridden with the `meta' plugin, a footer with copyright information
+# will be added to every page using a source file `copyright' (e.g.,
+# `copyright.mdwn') (using the same ``locating rules'' as for the sidebar
+# plugin).
+#
+# The state which page's copyright text was gathered from which source is not
+# tracked, so you'll need a full wiki-rebuild if the `copyright' file is
+# changed.
+
+package IkiWiki::Plugin::copyright;
+
+use warnings;
+use strict;
+use IkiWiki 2.00;
+
+my %copyright;
+
+sub import
+{
+ hook (type => "scan", id => "copyright", call => \&scan);
+}
+
+sub scan (@)
+{
+ my %params = @_;
+ my $page = $params{page};
+
+ return if defined $pagestate{$page}{meta}{copyright};
+
+ my $content;
+ my $copyright_page = bestlink ($page, "copyright") || return;
+ my $copyright_file = $pagesources{$copyright_page} || return;
+
+ # Only an optimization to avoid reading the same file again and again.
+ $copyright{$copyright_file} = readfile (srcfile ($copyright_file))
+ unless defined $copyright{$copyright_file};
+
+ $pagestate{$page}{meta}{copyright} = $copyright{$copyright_file};
+}
+
+1
diff --git a/.library/IkiWiki/Plugin/field.pm b/.library/IkiWiki/Plugin/field.pm
new file mode 100644
index 00000000..d77e7282
--- /dev/null
+++ b/.library/IkiWiki/Plugin/field.pm
@@ -0,0 +1,722 @@
+#!/usr/bin/perl
+# Ikiwiki field plugin.
+# See doc/plugin/contrib/field.mdwn for documentation.
+package IkiWiki::Plugin::field;
+use warnings;
+use strict;
+=head1 NAME
+
+IkiWiki::Plugin::field - front-end for per-page record fields.
+
+=head1 VERSION
+
+This describes version B<1.20101101> of IkiWiki::Plugin::field
+
+=cut
+
+our $VERSION = '1.20101115';
+
+=head1 PREREQUISITES
+
+ IkiWiki
+
+=head1 AUTHOR
+
+ Kathryn Andersen (RUBYKAT)
+ http://github.com/rubykat
+
+=head1 COPYRIGHT
+
+Copyright (c) 2009-2010 Kathryn Andersen
+
+This program is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=cut
+
+use IkiWiki 3.00;
+
+my %Fields = (
+ _first => {
+ id => '_first',
+ seq => 'BB',
+ },
+ _last => {
+ id => '_last',
+ seq => 'YY',
+ },
+ _middle => {
+ id => '_middle',
+ seq => 'MM',
+ },
+);
+my @FieldsLookupOrder = ();
+
+my %Cache = ();
+
+sub field_get_value ($$);
+
+sub import {
+ hook(type => "getsetup", id => "field", call => \&getsetup);
+ hook(type => "checkconfig", id => "field", call => \&checkconfig);
+ hook(type => "scan", id => "field", call => \&scan, last=>1);
+ hook(type => "pagetemplate", id => "field", call => \&pagetemplate);
+}
+
+# ===============================================
+# Hooks
+# ---------------------------
+sub getsetup () {
+ return
+ plugin => {
+ safe => 1,
+ rebuild => undef,
+ },
+ field_register => {
+ type => "hash",
+ example => "field_register => {meta => 'last'}",
+ description => "simple registration of fields by plugin",
+ safe => 0,
+ rebuild => undef,
+ },
+ field_allow_config => {
+ type => "boolean",
+ example => "field_allow_config => 1",
+ description => "allow config settings to be queried",
+ safe => 0,
+ rebuild => undef,
+ },
+ field_tags => {
+ type => "hash",
+ example => "field_tags => {BookAuthor => '/books/authors'}",
+ description => "fields flagged as tag-fields",
+ safe => 0,
+ rebuild => undef,
+ },
+}
+
+sub checkconfig () {
+ # use the simple by-plugin pagestatus method for
+ # those plugins registered with the field_register config option.
+ if (defined $config{field_register})
+ {
+ if (ref $config{field_register} eq 'ARRAY')
+ {
+ foreach my $id (@{$config{field_register}})
+ {
+ field_register(id=>$id);
+ }
+ }
+ elsif (ref $config{field_register} eq 'HASH')
+ {
+ foreach my $id (keys %{$config{field_register}})
+ {
+ field_register(id=>$id, order=>$config{field_register}->{$id});
+ }
+ }
+ else
+ {
+ field_register(id=>$config{field_register});
+ }
+ }
+ if (!defined $config{field_allow_config})
+ {
+ $config{field_allow_config} = 0;
+ }
+} # checkconfig
+
+sub scan (@) {
+ my %params=@_;
+ my $page=$params{page};
+ my $content=$params{content};
+
+ # scan for tag fields
+ if ($config{field_tags})
+ {
+ foreach my $field (keys %{$config{field_tags}})
+ {
+ my @values = field_get_value($field, $page);
+ if (@values)
+ {
+ foreach my $tag (@values)
+ {
+ if ($tag)
+ {
+ my $link = $config{field_tags}{$field} . '/'
+ . titlepage($tag);
+ add_link($page, $link, lc($field));
+ }
+ }
+ }
+ }
+ }
+} # scan
+
+sub pagetemplate (@) {
+ my %params=@_;
+ my $page=$params{page};
+ my $template=$params{template};
+
+ field_set_template_values($template, $page);
+} # pagetemplate
+
+# ===============================================
+# Field interface
+# ---------------------------
+
+sub field_register (%) {
+ my %param=@_;
+ if (!exists $param{id})
+ {
+ error 'field_register requires id parameter';
+ return 0;
+ }
+ if (exists $param{call} and !ref $param{call})
+ {
+ error 'field_register call parameter must be function';
+ return 0;
+ }
+
+ my $id = $param{id};
+ $Fields{$id} = \%param;
+
+ # add this to the ordering hash
+ # first, last, order; by default, middle
+ my $when = ($param{first}
+ ? '_first'
+ : ($param{last}
+ ? '_last'
+ : ($param{order}
+ ? ($param{order} eq 'first'
+ ? '_first'
+ : ($param{order} eq 'last'
+ ? '_last'
+ : ($param{order} eq 'middle'
+ ? '_middle'
+ : $param{order}
+ )
+ )
+ )
+ : '_middle'
+ )
+ ));
+ add_lookup_order($id, $when);
+ return 1;
+} # field_register
+
+sub field_get_value ($$) {
+ my $field_name = shift;
+ my $page = shift;
+
+ # This will return the first value it finds
+ # where the value returned is not undefined.
+ # This will return an array of values if wantarray is true.
+
+ # The reason why it checks every registered plugin rather than have
+ # plugins declare which fields they know about, is that it is quite
+ # possible that a plugin doesn't know, ahead of time, what fields
+ # will be available; for example, a YAML format plugin would return
+ # any field that happens to be defined in a YAML page file, which
+ # could be anything!
+
+ # check the cache first
+ my $lc_field_name = lc($field_name);
+ if (wantarray)
+ {
+ if (exists $Cache{$page}{$lc_field_name}{array}
+ and defined $Cache{$page}{$lc_field_name}{array})
+ {
+ return @{$Cache{$page}{$lc_field_name}{array}};
+ }
+ }
+ else
+ {
+ if (exists $Cache{$page}{$lc_field_name}{scalar}
+ and defined $Cache{$page}{$lc_field_name}{scalar})
+ {
+ return $Cache{$page}{$lc_field_name}{scalar};
+ }
+ }
+
+ if (!@FieldsLookupOrder)
+ {
+ build_fields_lookup_order();
+ }
+
+ # Get either the scalar or the array value depending
+ # on what is requested - don't get both because it wastes time.
+ if (wantarray)
+ {
+ my @array_value = undef;
+ foreach my $id (@FieldsLookupOrder)
+ {
+ # get the data from the pagestate hash if it's there
+ if (exists $pagestate{$page}{$id}{$field_name}
+ and defined $pagestate{$page}{$id}{$field_name})
+ {
+ @array_value = (ref $pagestate{$page}{$id}{$field_name}
+ ? @{$pagestate{$page}{$id}{$field_name}}
+ : ($pagestate{$page}{$id}{$field_name}));
+ }
+ elsif (exists $pagestate{$page}{$id}{$lc_field_name}
+ and defined $pagestate{$page}{$id}{$lc_field_name})
+ {
+ @array_value = (ref $pagestate{$page}{$id}{$lc_field_name}
+ ? @{$pagestate{$page}{$id}{$lc_field_name}}
+ : ($pagestate{$page}{$id}{$lc_field_name}));
+ }
+ elsif (exists $Fields{$id}{call})
+ {
+ @array_value = $Fields{$id}{call}->($field_name, $page);
+ }
+ if (@array_value and $array_value[0])
+ {
+ last;
+ }
+ }
+ if (!@array_value)
+ {
+ @array_value = field_calculated_values($field_name, $page);
+ }
+ # cache the value
+ $Cache{$page}{$lc_field_name}{array} = \@array_value;
+ return @array_value;
+ }
+ else # scalar
+ {
+ my $value = undef;
+ foreach my $id (@FieldsLookupOrder)
+ {
+ # get the data from the pagestate hash if it's there
+ # but only if it's already a scalar
+ if (exists $pagestate{$page}{$id}{$field_name}
+ and !ref $pagestate{$page}{$id}{$field_name})
+ {
+ $value = $pagestate{$page}{$id}{$field_name};
+ }
+ elsif (exists $pagestate{$page}{$id}{$lc_field_name}
+ and !ref $pagestate{$page}{$id}{$lc_field_name})
+ {
+ $value = $pagestate{$page}{$id}{$lc_field_name};
+ }
+ elsif (exists $Fields{$id}{call})
+ {
+ $value = $Fields{$id}{call}->($field_name, $page);
+ }
+ if (defined $value)
+ {
+ last;
+ }
+ }
+ if (!defined $value)
+ {
+ $value = field_calculated_values($field_name, $page);
+ }
+ # cache the value
+ $Cache{$page}{$lc_field_name}{scalar} = $value;
+ return $value;
+ }
+
+ return undef;
+} # field_get_value
+
+# set the values for the given HTML::Template template
+sub field_set_template_values ($$;@) {
+ my $template = shift;
+ my $page = shift;
+ my %params = @_;
+
+ my $get_value_fn = (exists $params{value_fn}
+ ? $params{value_fn}
+ : \&field_get_value);
+
+ # Find the parameter names in this template
+ # and see if you can find their values.
+
+ # The reason we check the template for field names is because we
+ # don't know what fields the registered plugins provide; and this is
+ # reasonable because for some plugins (e.g. a YAML data plugin) they
+ # have no way of knowing, ahead of time, what fields they might be
+ # able to provide.
+
+ my @parameter_names = $template->param();
+ foreach my $field (@parameter_names)
+ {
+ # Don't redefine if the field already has a value set.
+ next if ($template->param($field));
+
+ my $type = $template->query(name => $field);
+ if ($type eq 'LOOP' and $field =~ /_LOOP$/oi)
+ {
+ # Loop fields want arrays.
+ # Figure out what field names to look for:
+ # * names are from the enclosed loop fields
+ my @loop_fields = $template->query(loop => $field);
+
+ my @loop_vals = ();
+ my %loop_field_arrays = ();
+ foreach my $fn (@loop_fields)
+ {
+ if ($fn !~ /^__/o) # not a special loop variable
+ {
+ my @ival_array = $get_value_fn->($fn, $page);
+ if (@ival_array)
+ {
+ $loop_field_arrays{$fn} = \@ival_array;
+ }
+ }
+ }
+ foreach my $fn (sort keys %loop_field_arrays)
+ {
+ my $i = 0;
+ foreach my $v (@{$loop_field_arrays{$fn}})
+ {
+ if (!defined $loop_vals[$i])
+ {
+ $loop_vals[$i] = {};
+ }
+ $loop_vals[$i]{$fn} = $v;
+ $i++;
+ }
+ }
+ $template->param($field => \@loop_vals);
+ }
+ else # not a loop field
+ {
+ my $value = $get_value_fn->($field, $page);
+ if (defined $value)
+ {
+ $template->param($field => $value);
+ }
+ }
+ }
+} # field_set_template_values
+
+# ===============================================
+# Private Functions
+# ---------------------------
+
+# Calculate the lookup order
+# <module, >module, AZ
+# This is crabbed from the PmWiki Markup function
+sub add_lookup_order {
+ my $id = shift;
+ my $when = shift;
+
+ # may have given an explicit ordering
+ if ($when =~ /^[A-Z][A-Z]$/o)
+ {
+ $Fields{$id}{seq} = $when;
+ }
+ else
+ {
+ my $cmp = '=';
+ my $seq_field = $when;
+ if ($when =~ /^([<>])(.+)$/o)
+ {
+ $cmp = $1;
+ $seq_field = $2;
+ }
+ $Fields{$seq_field}{dep}{$id} = $cmp;
+ if (exists $Fields{$seq_field}{seq}
+ and defined $Fields{$seq_field}{seq})
+ {
+ $Fields{$id}{seq} = $Fields{$seq_field}{seq} . $cmp;
+ }
+ }
+ if ($Fields{$id}{seq})
+ {
+ foreach my $i (keys %{$Fields{$id}{dep}})
+ {
+ my $m = $Fields{$id}{dep}{$i};
+ add_lookup_order($i, "$m$id");
+ }
+ delete $Fields{$id}{dep};
+ }
+}
+
+sub build_fields_lookup_order {
+
+ # remove the _first, _last and _middle dummy fields
+ # because we don't need them anymore
+ delete $Fields{_first};
+ delete $Fields{_last};
+ delete $Fields{_middle};
+ my %lookup_spec = ();
+ # Make a hash of the lookup sequences
+ foreach my $id (sort keys %Fields)
+ {
+ my $seq = ($Fields{$id}{seq}
+ ? $Fields{$id}{seq}
+ : 'MM');
+ if (!exists $lookup_spec{$seq})
+ {
+ $lookup_spec{$seq} = {};
+ }
+ $lookup_spec{$seq}{$id} = 1;
+ }
+
+ # get the field-lookup order by (a) sorting by lookup_spec
+ # and (b) sorting by field-name for the fields that registered
+ # the same field-lookup order
+ foreach my $ord (sort keys %lookup_spec)
+ {
+ push @FieldsLookupOrder, sort keys %{$lookup_spec{$ord}};
+ }
+} # build_fields_lookup_order
+
+# standard values deduced from other values
+sub field_calculated_values {
+ my $field_name = shift;
+ my $page = shift;
+
+ my $value = undef;
+
+ # Exception for titles
+ # If the title hasn't been found, construct it
+ if ($field_name eq 'title')
+ {
+ $value = pagetitle(IkiWiki::basename($page));
+ }
+ # and set "page" if desired
+ elsif ($field_name eq 'page')
+ {
+ $value = $page;
+ }
+ # the page above this page; aka the current directory
+ elsif ($field_name eq 'parent_page')
+ {
+ if ($page =~ m{^(.*)/[-\.\w]+$}o)
+ {
+ $value = $1;
+ }
+ }
+ elsif ($field_name eq 'basename')
+ {
+ $value = IkiWiki::basename($page);
+ }
+ elsif ($config{field_allow_config}
+ and $field_name =~ /^config-(.*)$/oi)
+ {
+ my $cfield = $1;
+ if (exists $config{$cfield})
+ {
+ $value = $config{$cfield};
+ }
+ }
+ elsif ($field_name =~ /^(.*)-tagpage$/o)
+ {
+ my @array_value = undef;
+ my $real_fn = $1;
+ if (exists $config{field_tags}{$real_fn}
+ and defined $config{field_tags}{$real_fn})
+ {
+ my @values = field_get_value($real_fn, $page);
+ if (@values)
+ {
+ foreach my $tag (@values)
+ {
+ if ($tag)
+ {
+ my $link = $config{field_tags}{$real_fn} . '/' . $tag;
+ push @array_value, $link;
+ }
+ }
+ if (wantarray)
+ {
+ return @array_value;
+ }
+ else
+ {
+ $value = join(",", @array_value) if $array_value[0];
+ }
+ }
+ }
+ }
+ return (wantarray ? ($value) : $value);
+} # field_calculated_values
+
+# match field funcs
+# page-to-check, wanted
+sub match_a_field ($$) {
+ my $page=shift;
+ my $wanted=shift;
+
+ # The field name is first; the rest is the match
+ my $field_name;
+ my $glob;
+ if ($wanted =~ /^(\w+)\s+(.*)$/o)
+ {
+ $field_name = $1;
+ $glob = $2;
+ }
+ else
+ {
+ return IkiWiki::FailReason->new("cannot match field");
+ }
+
+ # turn glob into a safe regexp
+ my $re=IkiWiki::glob2re($glob);
+
+ my $val = IkiWiki::Plugin::field::field_get_value($field_name, $page);
+
+ if (defined $val) {
+ if ($val=~/^$re$/i) {
+ return IkiWiki::SuccessReason->new("$re matches $field_name of $page", $page => $IkiWiki::DEPEND_CONTENT, "" => 1);
+ }
+ else {
+ return IkiWiki::FailReason->new("$re does not match $field_name of $page", "" => 1);
+ }
+ }
+ else {
+ return IkiWiki::FailReason->new("$page does not have a $field_name", "" => 1);
+ }
+} # match_a_field
+
+# check against individual items of a field
+# (treat the field as an array)
+# page-to-check, wanted
+sub match_a_field_item ($$) {
+ my $page=shift;
+ my $wanted=shift;
+
+ # The field name is first; the rest is the match
+ my $field_name;
+ my $glob;
+ if ($wanted =~ /^(\w+)\s+(.*)$/o)
+ {
+ $field_name = $1;
+ $glob = $2;
+ }
+ else
+ {
+ return IkiWiki::FailReason->new("cannot match field");
+ }
+
+ # turn glob into a safe regexp
+ my $re=IkiWiki::glob2re($glob);
+
+ my @val_array = IkiWiki::Plugin::field::field_get_value($field_name, $page);
+
+ if (@val_array)
+ {
+ foreach my $val (@val_array)
+ {
+ if (defined $val) {
+ if ($val=~/^$re$/i) {
+ return IkiWiki::SuccessReason->new("$re matches $field_name of $page", $page => $IkiWiki::DEPEND_CONTENT, "" => 1);
+ }
+ }
+ }
+ # not found
+ return IkiWiki::FailReason->new("$re does not match $field_name of $page", "" => 1);
+ }
+ else {
+ return IkiWiki::FailReason->new("$page does not have a $field_name", "" => 1);
+ }
+} # match_a_field_item
+
+# ===============================================
+# PageSpec functions
+# ---------------------------
+
+package IkiWiki::PageSpec;
+
+sub match_field ($$;@) {
+ my $page=shift;
+ my $wanted=shift;
+ return IkiWiki::Plugin::field::match_a_field($page, $wanted);
+} # match_field
+
+sub match_destfield ($$;@) {
+ my $page=shift;
+ my $wanted=shift;
+ my %params=@_;
+
+ return IkiWiki::FailReason->new("cannot match destpage") unless exists $params{destpage};
+
+ # Match the field on the destination page, not the source page
+ return IkiWiki::Plugin::field::match_a_field($params{destpage}, $wanted);
+} # match_destfield
+
+sub match_field_item ($$;@) {
+ my $page=shift;
+ my $wanted=shift;
+ return IkiWiki::Plugin::field::match_a_field_item($page, $wanted);
+} # match_field
+
+sub match_destfield_item ($$;@) {
+ my $page=shift;
+ my $wanted=shift;
+ my %params=@_;
+
+ return IkiWiki::FailReason->new("cannot match destpage") unless exists $params{destpage};
+
+ # Match the field on the destination page, not the source page
+ return IkiWiki::Plugin::field::match_a_field_item($params{destpage}, $wanted);
+} # match_destfield
+
+sub match_field_tagged ($$;@) {
+ my $page=shift;
+ my $wanted=shift;
+ my %params=@_;
+
+ # The field name is first; the rest is the match
+ my $field_name;
+ my $glob;
+ if ($wanted =~ /^(\w+)\s+(.*)$/o)
+ {
+ $field_name = $1;
+ $glob = $2;
+ }
+ else
+ {
+ return IkiWiki::FailReason->new("cannot match field");
+ }
+ return match_link($page, $glob, linktype => lc($field_name), @_);
+}
+
+sub match_destfield_tagged ($$;@) {
+ my $page=shift;
+ my $wanted=shift;
+ my %params=@_;
+
+ return IkiWiki::FailReason->new("cannot match destpage") unless exists $params{destpage};
+
+ # Match the field on the destination page, not the source page
+ return IkiWiki::Plugin::field::match_field_tagged($params{destpage}, $wanted);
+}
+
+# ===============================================
+# SortSpec functions
+# ---------------------------
+package IkiWiki::SortSpec;
+
+sub cmp_field {
+ my $field = shift;
+ error(gettext("sort=field requires a parameter")) unless defined $field;
+
+ my $left = IkiWiki::Plugin::field::field_get_value($field, $a);
+ my $right = IkiWiki::Plugin::field::field_get_value($field, $b);
+
+ $left = "" unless defined $left;
+ $right = "" unless defined $right;
+ return $left cmp $right;
+}
+
+sub cmp_field_natural {
+ my $field = shift;
+ error(gettext("sort=field requires a parameter")) unless defined $field;
+
+ eval q{use Sort::Naturally};
+ error $@ if $@;
+
+ my $left = IkiWiki::Plugin::field::field_get_value($field, $a);
+ my $right = IkiWiki::Plugin::field::field_get_value($field, $b);
+
+ $left = "" unless defined $left;
+ $right = "" unless defined $right;
+ return Sort::Naturally::ncmp($left, $right);
+}
+
+1;
diff --git a/.library/IkiWiki/Plugin/fill_empty_page.pm b/.library/IkiWiki/Plugin/fill_empty_page.pm
deleted file mode 100644
index b2ce1fc0..00000000
--- a/.library/IkiWiki/Plugin/fill_empty_page.pm
+++ /dev/null
@@ -1,65 +0,0 @@
-# A plugin for ikiwiki to implement filling newly created pages from a template
-# file.
-
-# Copyright © 2007 Thomas Schwinge <tschwinge@gnu.org>
-#
-# This program is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by the
-# Free Software Foundation; either version 2, or (at your option) any later
-# version.
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
-# for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-# Newly created pages will be filled from a file `empty_page.mdwn' if such a
-# file can be found by using the same rules as for the sidebar plugin.
-#
-# <http://ikiwiki.info/todo/default_content_for_new_post/>
-
-package IkiWiki::Plugin::fill_empty_page;
-
-use warnings;
-use strict;
-use IkiWiki 2.00;
-
-sub import
-{
- hook (type => "formbuilder_setup", id => "fill_empty_page",
- call => \&formbuilder_setup);
-}
-
-sub formbuilder_setup
-{
- my %params = @_;
- my $form = $params {form};
- my $page = $form->field ("page");
-
- return if $form->title ne "editpage";
- return if $form->field("do") ne "create";
-
- return if defined $form->field ("editcontent");
-
- # This is obviously not the last conclusion of wisdom.
- my $empty_page_page = bestlink ($page, "empty_page") || return;
- my $empty_page_file = $pagesources {$empty_page_page} || return;
- my $empty_page_type = pagetype ($empty_page_file);
- return unless defined $empty_page_type;
-
- my $content = readfile (srcfile ($empty_page_file));
-
- if (defined $content && length $content)
- {
- $form->field (name => "editcontent", value => $content);
-
- $form->tmpl_param ("page_preview", "Filled automagically from `"
- . $empty_page_file . "`.");
- }
-}
-
-1
diff --git a/.library/IkiWiki/Plugin/getfield.pm b/.library/IkiWiki/Plugin/getfield.pm
new file mode 100644
index 00000000..971e7ecb
--- /dev/null
+++ b/.library/IkiWiki/Plugin/getfield.pm
@@ -0,0 +1,126 @@
+#!/usr/bin/perl
+# Ikiwiki getfield plugin.
+# Substitute field values in the content of the page.
+# See plugin/contrib/getfield for documentation.
+package IkiWiki::Plugin::getfield;
+use strict;
+=head1 NAME
+
+IkiWiki::Plugin::getfield - query the values of fields
+
+=head1 VERSION
+
+This describes version B<1.20101101> of IkiWiki::Plugin::getfield
+
+=cut
+
+our $VERSION = '1.20101101';
+
+=head1 PREREQUISITES
+
+ IkiWiki
+ IkiWiki::Plugin::field
+
+=head1 AUTHOR
+
+ Kathryn Andersen (RUBYKAT)
+ http://github.com/rubykat
+
+=head1 COPYRIGHT
+
+Copyright (c) 2009 Kathryn Andersen
+
+This program is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=cut
+
+use IkiWiki 3.00;
+
+sub import {
+ hook(type => "getsetup", id => "getfield", call => \&getsetup);
+ hook(type => "filter", id => "getfield", call => \&do_filter, last=>1);
+
+ IkiWiki::loadplugin("field");
+}
+
+#---------------------------------------------------------------
+# Hooks
+# --------------------------------
+
+sub getsetup () {
+ return
+ plugin => {
+ safe => 1,
+ rebuild => undef,
+ },
+}
+
+sub do_filter (@) {
+ my %params=@_;
+ my $page = $params{page};
+ my $destpage = ($params{destpage} ? $params{destpage} : $params{page});
+
+ my $page_file=$pagesources{$page};
+ my $page_type=pagetype($page_file);
+ if (defined $page_type)
+ {
+ while ($params{content} =~ /{{\$([-\w\/]+#)?[-\w]+}}/)
+ {
+ # substitute {{$var}} variables (source-page)
+ $params{content} =~ s/{{\$([-\w]+)}}/get_field_value($1,$page)/eg;
+
+ # substitute {{$page#var}} variables (source-page)
+ $params{content} =~ s/{{\$([-\w\/]+)#([-\w]+)}}/get_other_page_field_value($2,$page,$1)/eg;
+ }
+ }
+
+ $page_file=$pagesources{$destpage};
+ $page_type=pagetype($page_file);
+ if (defined $page_type)
+ {
+ while ($params{content} =~ /{{\+\$([-\w\/]+#)?[-\w]+\+}}/)
+ {
+ # substitute {{+$var+}} variables (dest-page)
+ $params{content} =~ s/{{\+\$([-\w]+)\+}}/get_field_value($1,$destpage)/eg;
+ # substitute {{+$page#var+}} variables (source-page)
+ $params{content} =~ s/{{\+\$([-\w\/]+)#([-\w]+)\+}}/get_other_page_field_value($2,$destpage,$1)/eg;
+ }
+ }
+
+ return $params{content};
+} # do_filter
+
+#---------------------------------------------------------------
+# Private functions
+# --------------------------------
+sub get_other_page_field_value ($$$) {
+ my $field = shift;
+ my $page = shift;
+ my $other_page = shift;
+
+ my $use_page = bestlink($page, $other_page);
+ # add a dependency for the page from which we get the value
+ add_depends($page, $use_page);
+
+ my $val = get_field_value($field, $use_page);
+ if ($val eq $field)
+ {
+ return "${other_page}#$field";
+ }
+ return $val;
+
+} # get_other_page_field_value
+
+sub get_field_value ($$) {
+ my $field = shift;
+ my $page = shift;
+
+ my $value = IkiWiki::Plugin::field::field_get_value($field,$page);
+ return $value if defined $value;
+
+ # if there is no value, return the field name.
+ return $field;
+} # get_field_value
+
+1;
diff --git a/.library/IkiWiki/Plugin/license.pm b/.library/IkiWiki/Plugin/license.pm
index e608461f..651c039a 100644
--- a/.library/IkiWiki/Plugin/license.pm
+++ b/.library/IkiWiki/Plugin/license.pm
@@ -1,7 +1,7 @@
# A plugin for ikiwiki to implement adding a footer with licensing information
-# to every rendered page.
+# based on a default value taken out of a file.
-# Copyright © 2007 Thomas Schwinge <tschwinge@gnu.org>
+# Copyright © 2007, 2008 Thomas Schwinge <tschwinge@gnu.org>
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
@@ -17,13 +17,13 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-# A footer with licensing information will be added to every rendered page if
-# either (a) a file `license.mdwn' is found (using the same rules as for the
-# sidebar plugin) or (b) (which be used to override (a)) a statement à la
-# ``[[license text=WHATEVER]]'' is found in the source page.
+# Unless overridden with the `meta' plugin, a footer with licensing information
+# will be added to every page using a source file `license' (e.g.,
+# `license.mdwn') (using the same ``locating rules'' as for the sidebar
+# plugin).
#
# The state which page's license text was gathered from which source is not
-# tracked, so you'll need a full wiki-rebuild if (a)'s files are changed.
+# tracked, so you'll need a full wiki-rebuild if the `license' file is changed.
package IkiWiki::Plugin::license;
@@ -31,59 +31,29 @@ use warnings;
use strict;
use IkiWiki 2.00;
-sub import
-{
- hook (type => "preprocess", id => "license", call => \&preprocess);
- hook (type => "pagetemplate", id => "license", call => \&pagetemplate);
-}
-
-my %text;
+my %license;
-sub preprocess (@)
+sub import
{
- my %params = @_;
- my $page = $params {page};
-
- # We don't return any text here, but record the passed text.
- $text {$page} = $params {text};
- return "";
+ hook (type => "scan", id => "license", call => \&scan);
}
-sub pagetemplate (@)
+sub scan (@)
{
my %params = @_;
- my $page = $params {page};
- my $destpage = $params {destpage};
+ my $page = $params{page};
- my $template = $params {template};
+ return if defined $pagestate{$page}{meta}{license};
- if ($template->query (name => "license"))
- {
- my $content;
- my $pagetype;
- if (defined $text {$page})
- {
- $pagetype = pagetype ($pagesources {$page});
- $content = $text {$page};
- }
- else
- {
- my $license_page = bestlink ($page, "license") || return;
- my $license_file = $pagesources {$license_page} || return;
- $pagetype = pagetype ($license_file);
- $content = readfile (srcfile ($license_file));
- }
+ my $content;
+ my $license_page = bestlink ($page, "license") || return;
+ my $license_file = $pagesources{$license_page} || return;
- if (defined $content && length $content)
- {
- $template->param (license =>
- IkiWiki::htmlize ($destpage, $pagetype,
- IkiWiki::linkify ($page, $destpage,
- IkiWiki::preprocess ($page, $destpage,
- IkiWiki::filter ($page, $destpage, $content)))));
+ # Only an optimization to avoid reading the same file again and again.
+ $license{$license_file} = readfile (srcfile ($license_file))
+ unless defined $license{$license_file};
- }
- }
+ $pagestate{$page}{meta}{license} = $license{$license_file};
}
1
diff --git a/.library/IkiWiki/Plugin/reset_mtimes.pm b/.library/IkiWiki/Plugin/reset_mtimes.pm
new file mode 100644
index 00000000..a168652b
--- /dev/null
+++ b/.library/IkiWiki/Plugin/reset_mtimes.pm
@@ -0,0 +1,84 @@
+# Copyright © 2010 Thomas Schwinge <thomas@schwinge.name>
+
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+package IkiWiki::Plugin::reset_mtimes;
+
+use warnings;
+use strict;
+use IkiWiki 3.00;
+
+# This plugin resets pages' / files' mtimes according to the RCS information
+# when using --refresh mode.
+#
+# Note that the files' mtimes are *always* set, even if the file has
+# un-committed changes.
+#
+# <http://ikiwiki.info/bugs/pagemtime_in_refresh_mode/>
+
+sub import
+{
+ hook (type => "needsbuild",
+ id => "reset_mtimes",
+ call => \&needsbuild);
+}
+
+sub needsbuild (@)
+{
+ # Only proceed if --gettime is in effect, as we're clearly not intersted in
+ # this functionality otherwise.
+ return unless $config{gettime};
+
+ my $files = shift;
+ foreach my $file (@$files)
+ {
+ # [TODO. Perhaps not necessary. Can this hook ever be called for
+ # removed pages -- that need to be ``rebuilt'' in the sense that
+ # they're to be removed?] Don't bother for pages that don't exist
+ # anymore.
+ next unless -e "$config{srcdir}/$file";
+
+ my $page = pagename ($file);
+ debug ("needsbuild: <$file> <$page>");
+
+ # Only ever update -- otherwise ikiwiki shall do its own thing.
+ if (defined $IkiWiki::pagemtime{$page})
+ {
+ debug ("pagemtime: " . $IkiWiki::pagemtime{$page});
+
+ my $mtime = 0;
+ eval
+ {
+ $mtime = IkiWiki::rcs_getmtime ($file);
+ };
+ if ($@)
+ {
+ print STDERR $@;
+ }
+ elsif ($mtime > 0)
+ {
+ $IkiWiki::pagemtime{$page} = $mtime;
+
+ # We have to set the actual file's mtime too, as otherwise
+ # ikiwiki will update it again and again.
+ utime($mtime, $mtime, "$config{srcdir}/$file");
+ }
+
+ debug ("pagemtime: " . $IkiWiki::pagemtime{$page});
+ }
+ }
+}
+
+1
diff --git a/.library/IkiWiki/Plugin/texinfo.pm b/.library/IkiWiki/Plugin/texinfo.pm
new file mode 100644
index 00000000..8c651160
--- /dev/null
+++ b/.library/IkiWiki/Plugin/texinfo.pm
@@ -0,0 +1,226 @@
+# A GNU Texinfo rendering plugin.
+
+# Copyright © 2007 Thomas Schwinge <tschwinge@gnu.org>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+# http://ikiwiki.info/plugins/contrib/texinfo/
+
+package IkiWiki::Plugin::texinfo;
+
+use warnings;
+use strict;
+use IkiWiki 2.00;
+
+# From `Ikiwiki/Plugins/teximg.pm'.
+sub create_tmp_dir ($)
+{
+ # Create a temp directory, it will be removed when ikiwiki exits.
+ my $base = shift;
+
+ my $template = $base . ".XXXXXXXXXX";
+ use File::Temp qw (tempdir);
+ my $tmpdir = tempdir ($template, TMPDIR => 1, CLEANUP => 1);
+ return $tmpdir;
+}
+
+sub import
+{
+ hook (type => "filter", id => "texi", call => \&filter);
+ hook (type => "htmlize", id => "texi", call => \&htmlize);
+ hook (type => "pagetemplate", id => "texi", call => \&pagetemplate);
+}
+
+my %copyright;
+my %license;
+
+sub filter (@)
+{
+ my %params = @_;
+ my $page = $params{page};
+
+# TODO. For ``$page eq 'shortcuts''' this fails. Is this expected?
+ goto out unless defined $pagesources{$page};
+
+ # Only care for `.texi' files.
+ goto out unless pagetype ($pagesources{$page}) eq 'texi';
+
+ # No need to parse twice.
+ goto out if exists $copyright{$page};
+
+# TODO. Check the `meta' plugin about when to do this at all.
+ $copyright{$page} = undef;
+ $license{$page} = undef;
+ # We assume that the copyright and licensing information is to be taken
+ # from the main `.texi' file.
+ @_ = split /\n/, $params{content};
+ # Do some parsing to cut out the interesting bits, if there are any.
+ while (defined ($_ = shift @_))
+ {
+ # Did we find a start tag?
+ last if /^\@copying$/;
+ }
+ # Already at the end of the page?
+ goto out unless defined $_;
+ while (defined ($_ = shift @_))
+ {
+ # Already at the end of the copying section? (Shouldn't happen.)
+ goto out if /^\@end\ copying/;
+ # Found the ``^Copyright'' line?
+ last if /^Copyright/;
+ }
+ # Already at the end of the page? (Shouldn't happen.)
+ goto out unless defined $_;
+ # Copyright text will follow.
+ $copyright{$page} = $_;
+ while (defined ($_ = shift @_))
+ {
+ # Found the separator of copyright and licensind information?
+ last if /^$/;
+ # Already at the end of the copying section?
+ goto finish if /^\@end\ copying/;
+ $copyright{$page} .= ' ' . $_;
+ }
+ # Already at the end of the page? (Shouldn't happen.)
+ goto finish unless defined $_;
+ # Licensing text will follow.
+ while (defined ($_ = shift @_))
+ {
+ # Already at the end of the copying section?
+ last if /^\@end\ copying/;
+ $license{$page} .= ' ' . $_;
+ }
+
+ finish:
+ # ``Render'' by hand some stuff that is commonly found in this section.
+ if (defined $copyright{$page})
+ {
+ $copyright{$page} =~ s/\@copyright{}/©/g;
+ }
+ if (defined $license{$page})
+ {
+ $license{$page} =~ s/\@quotation//g;
+ $license{$page} =~ s/\@end\ quotation//g;
+ $license{$page} =~ s/\@ignore/<!--/g;
+ $license{$page} =~ s/\@end\ ignore/-->/g;
+ }
+
+ out:
+ return $params{content};
+}
+
+sub htmlize (@)
+{
+ my %params = @_;
+ my $page = $params{page};
+
+ my $home;
+ if (defined $pagesources{$page})
+ {
+ $home = $config{srcdir} . '/' . dirname ($pagesources{$page});
+ }
+ else
+ {
+ # This happens in the CGI web frontent, when freshly creating a
+ # `texi'-type page and selecting to ``Preview'' the page before doing a
+ # ``Save Page''.
+# TODO.
+ $home = $config{srcdir};
+ }
+
+ my $pid;
+ my $sigpipe = 0;
+ $SIG{PIPE} = sub
+ {
+ $sigpipe = 1;
+ };
+
+ my $tmp = eval
+ {
+ create_tmp_dir ("texinfo")
+ };
+ if (! $@ &&
+ # `makeinfo' can't work directly on stdin.
+ writefile ("texinfo.texi", $tmp, $params{content}) == 0)
+ {
+ return "couldn't write temporary file";
+ }
+
+ use File::Basename;
+ use IPC::Open2;
+ $pid = open2 (*IN, *OUT,
+ 'makeinfo',
+ '--html',
+ '--no-split', '--output=-',
+ # We might be run from a directory different from the one the
+ # `.texi' file is stored in.
+# TODO. Should we `cd' to this directory instead?
+ '-P', $home,
+# TODO. Adding the following allows for putting files like `gpl.texinfo' into
+# the top-level wiki directory.
+ '-I', $config{srcdir},
+ $tmp . "/texinfo.texi");
+ # open2 doesn't respect "use open ':utf8'"
+ binmode (IN, ':utf8');
+# binmode (OUT, ':utf8');
+
+# print OUT $params{content};
+ close OUT;
+
+ local $/ = undef;
+ my $ret = <IN>;
+ close IN;
+ waitpid $pid, 0;
+ $SIG{PIPE} = "DEFAULT";
+
+ return "failed to render" if $sigpipe;
+
+ # Cut out the interesting bits.
+ $ret =~ s/.*<body>//s;
+ $ret =~ s/<\/body>.*//s;
+
+ return $ret;
+}
+
+sub pagetemplate (@)
+{
+ my %params = @_;
+ my $page = $params{page};
+ my $destpage = $params{destpage};
+
+ my $template = $params{template};
+
+# TODO. Check the `meta' plugin about when to do this at all.
+ if ($template->query (name => "copyright") &&
+ ! defined $template->param ('copyright'))
+ {
+ if (defined $copyright{$page} && length $copyright{$page})
+ {
+ $template->param (copyright =>
+ IkiWiki::linkify ($page, $destpage, $copyright{$page}));
+ }
+ }
+ if ($template->query (name => "license") &&
+ ! defined $template->param ('license'))
+ {
+ if (defined $license{$page} && length $license{$page})
+ {
+ $template->param (license =>
+ IkiWiki::linkify ($page, $destpage, $license{$page}));
+ }
+ }
+}
+
+1
diff --git a/.library/IkiWiki/Plugin/ymlfront.pm b/.library/IkiWiki/Plugin/ymlfront.pm
new file mode 100644
index 00000000..9c033833
--- /dev/null
+++ b/.library/IkiWiki/Plugin/ymlfront.pm
@@ -0,0 +1,434 @@
+#!/usr/bin/perl
+# YAML format for structured data
+# See plugins/contrib/ymlfront for documentation.
+package IkiWiki::Plugin::ymlfront;
+use warnings;
+use strict;
+=head1 NAME
+
+IkiWiki::Plugin::ymlfront - add YAML-format data to a page
+
+=head1 VERSION
+
+This describes version B<1.20100808> of IkiWiki::Plugin::ymlfront
+
+=cut
+
+our $VERSION = '1.20101116';
+
+=head1 PREREQUISITES
+
+ IkiWiki
+ IkiWiki::Plugin::field
+ YAML::Any
+
+=head1 AUTHOR
+
+ Kathryn Andersen (RUBYKAT)
+ http://github.com/rubykat
+
+=head1 COPYRIGHT
+
+Copyright (c) 2009 Kathryn Andersen
+
+This program is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=cut
+use IkiWiki 3.00;
+
+sub import {
+ hook(type => "getsetup", id => "ymlfront", call => \&getsetup);
+ hook(type => "checkconfig", id => "ymlfront", call => \&checkconfig);
+ hook(type => "filter", id => "ymlfront", call => \&filter, first=>1);
+ hook(type => "preprocess", id => "ymlfront", call => \&preprocess, scan=>1);
+ hook(type => "scan", id => "ymlfront", call => \&scan);
+ hook(type => "checkcontent", id => "ymlfront", call => \&checkcontent);
+
+ IkiWiki::loadplugin('field');
+ IkiWiki::Plugin::field::field_register(id=>'ymlfront',
+ call=>\&yml_get_value,
+ first=>1);
+}
+
+# ------------------------------------------------------------
+# Package Vars
+# --------------------------------
+my $ymlfront_regex = qr{
+ (\\?) # 1: escape?
+ \[\[(!) # directive open; 2: prefix
+ (ymlfront) # 3: command
+ ( # 4: the parameters..
+ \s+ # Must have space if parameters present
+ (?:
+ (?:[-\w]+=)? # named parameter key?
+ (?:
+ """.*?""" # triple-quoted value
+ |
+ "[^"]*?" # single-quoted value
+ |
+ [^"\s\]]+ # unquoted value
+ )
+ \s* # whitespace or end
+ # of directive
+ )
+ *)? # 0 or more parameters
+ \]\] # directive closed
+ }sx;
+
+# ------------------------------------------------------------
+# Hooks
+# --------------------------------
+sub getsetup () {
+ return
+ plugin => {
+ safe => 1,
+ rebuild => 1,
+ },
+ ymlfront_delim => {
+ type => "array",
+ example => "ymlfront_sep => [qw(--YAML-START-- --YAML-END--)]",
+ description => "delimiters of YAML data",
+ safe => 0,
+ rebuild => undef,
+ },
+}
+
+sub checkconfig () {
+ eval q{use YAML::Any};
+ eval q{use YAML} if $@;
+ if ($@)
+ {
+ return error ("ymlfront: failed to use YAML::Any or YAML");
+ }
+
+ $YAML::UseBlock = 1;
+ $YAML::Syck::ImplicitUnicode = 1;
+
+ if (!defined $config{ymlfront_delim})
+ {
+ $config{ymlfront_delim} = [qw(--- ---)];
+ }
+} # checkconfig
+
+# scan gets called before filter
+sub scan (@) {
+ my %params=@_;
+ my $page = $params{page};
+
+ my $page_file=$pagesources{$page} || return;
+ my $page_type=pagetype($page_file);
+ if (!defined $page_type)
+ {
+ return;
+ }
+ # clear the old data
+ if (exists $pagestate{$page}{ymlfront})
+ {
+ delete $pagestate{$page}{ymlfront};
+ }
+ my $parsed_yml = parse_yml(%params);
+ if (defined $parsed_yml
+ and defined $parsed_yml->{yml})
+ {
+ # save the data to pagestate
+ foreach my $fn (keys %{$parsed_yml->{yml}})
+ {
+ my $fval = $parsed_yml->{yml}->{$fn};
+ $pagestate{$page}{ymlfront}{$fn} = $fval;
+ }
+ }
+ # update meta hash
+ if (exists $pagestate{$page}{ymlfront}{title}
+ and $pagestate{$page}{ymlfront}{title})
+ {
+ $pagestate{$page}{meta}{title} = $pagestate{$page}{ymlfront}{title};
+ }
+ if (exists $pagestate{$page}{ymlfront}{description}
+ and $pagestate{$page}{ymlfront}{description})
+ {
+ $pagestate{$page}{meta}{description} = $pagestate{$page}{ymlfront}{description};
+ }
+ if (exists $pagestate{$page}{ymlfront}{author}
+ and $pagestate{$page}{ymlfront}{author})
+ {
+ $pagestate{$page}{meta}{author} = $pagestate{$page}{ymlfront}{author};
+ }
+} # scan
+
+# use this for data in a [[!ymlfront ...]] directive
+sub preprocess (@) {
+ my %params=@_;
+ my $page = $params{page};
+
+ if (! exists $params{data}
+ or ! defined $params{data}
+ or !$params{data})
+ {
+ error gettext("missing data parameter")
+ }
+ # All the work of this is done in scan mode;
+ # when in preprocessing mode, just return an empty string.
+ my $scan=! defined wantarray;
+
+ if (!$scan)
+ {
+ return '';
+ }
+
+ # clear the old data
+ if (exists $pagestate{$page}{ymlfront})
+ {
+ delete $pagestate{$page}{ymlfront};
+ }
+ my $parsed_yml = parse_yml(%params);
+ if (defined $parsed_yml
+ and defined $parsed_yml->{yml})
+ {
+ # save the data to pagestate
+ foreach my $fn (keys %{$parsed_yml->{yml}})
+ {
+ my $fval = $parsed_yml->{yml}->{$fn};
+ $pagestate{$page}{ymlfront}{$fn} = $fval;
+ }
+ }
+ # update meta hash
+ if (exists $pagestate{$page}{ymlfront}{title}
+ and $pagestate{$page}{ymlfront}{title})
+ {
+ $pagestate{$page}{meta}{title} = $pagestate{$page}{ymlfront}{title};
+ }
+ if (exists $pagestate{$page}{ymlfront}{description}
+ and $pagestate{$page}{ymlfront}{description})
+ {
+ $pagestate{$page}{meta}{description} = $pagestate{$page}{ymlfront}{description};
+ }
+ if (exists $pagestate{$page}{ymlfront}{author}
+ and $pagestate{$page}{ymlfront}{author})
+ {
+ $pagestate{$page}{meta}{author} = $pagestate{$page}{ymlfront}{author};
+ }
+ return '';
+} # preprocess
+
+sub filter (@) {
+ my %params=@_;
+ my $page = $params{page};
+
+ my $page_file=$pagesources{$page} || return $params{content};
+ my $page_type=pagetype($page_file);
+ if (!defined $page_type)
+ {
+ return $params{content};
+ }
+ my $parsed_yml = parse_yml(%params);
+ if (defined $parsed_yml
+ and defined $parsed_yml->{yml}
+ and defined $parsed_yml->{content})
+ {
+ $params{content} = $parsed_yml->{content};
+ # also check for a content value
+ if (exists $pagestate{$page}{ymlfront}{content}
+ and defined $pagestate{$page}{ymlfront}{content}
+ and $pagestate{$page}{ymlfront}{content})
+ {
+ $params{content} .= $pagestate{$page}{ymlfront}{content};
+ }
+ }
+
+ return $params{content};
+} # filter
+
+# check the correctness of the YAML code before saving a page
+sub checkcontent {
+ my %params=@_;
+ my $page = $params{page};
+
+ my $page_file=$pagesources{$page};
+ if ($page_file)
+ {
+ my $page_type=pagetype($page_file);
+ if (!defined $page_type)
+ {
+ return undef;
+ }
+ }
+ my $parsed_yml = parse_yml(%params);
+ if (!defined $parsed_yml)
+ {
+ debug("ymlfront: Save of $page failed: $@");
+ return gettext("YAML data incorrect: $@");
+ }
+ return undef;
+} # checkcontent
+
+# ------------------------------------------------------------
+# Field functions
+# --------------------------------
+sub yml_get_value ($$) {
+ my $field_name = shift;
+ my $page = shift;
+
+ my $value = undef;
+ if (exists $pagestate{$page}{ymlfront}{$field_name})
+ {
+ $value = $pagestate{$page}{ymlfront}{$field_name};
+ }
+ elsif (exists $pagestate{$page}{ymlfront}{lc($field_name)})
+ {
+ $value = $pagestate{$page}{ymlfront}{lc($field_name)};
+ }
+ if (defined $value)
+ {
+ if (ref $value)
+ {
+ my @value_array = @{$value};
+ return (wantarray
+ ? @value_array
+ : join(",", @value_array));
+ }
+ else
+ {
+ return (wantarray ? ($value) : $value);
+ }
+ }
+ return undef;
+} # yml_get_value
+
+# ------------------------------------------------------------
+# Helper functions
+# --------------------------------
+
+# parse the YAML data from the given content
+# Expects page, content
+# Returns { yml=>%yml_data, content=>$content } or undef
+sub parse_yml {
+ my %params=@_;
+ my $page = $params{page};
+ my $content = $params{content};
+
+ my $page_file=$pagesources{$page};
+ if ($page_file)
+ {
+ my $page_type=pagetype($page_file);
+ if (!defined $page_type)
+ {
+ return undef;
+ }
+ }
+ my $start_of_content = '';
+ my $yml_str = '';
+ my $rest_of_content = '';
+ if ($params{data})
+ {
+ $yml_str = $params{data};
+ }
+ elsif ($content)
+ {
+ my $ystart = $config{ymlfront_delim}[0];
+ my $yend = $config{ymlfront_delim}[1];
+ if ($ystart eq '---'
+ and $yend eq '---'
+ and $content =~ /^---[\n\r](.*?[\n\r])---[\n\r](.*)$/s)
+ {
+ $yml_str = $1;
+ $rest_of_content = $2;
+ }
+ elsif ($content =~ /^(.*?)${ystart}[\n\r](.*?[\n\r])${yend}([\n\r].*)$/s)
+ {
+ $yml_str = $2;
+ $rest_of_content = $1 . $3;
+ }
+ elsif ($content =~ $ymlfront_regex)
+ {
+ my $escape=$1;
+ my $prefix=$2;
+ my $command=$3;
+ my $params=$4;
+ if ($escape)
+ {
+ $rest_of_content = $content;
+ }
+ else
+ {
+ my %phash = ();
+ while ($params =~ m{
+ (?:([-\w]+)=)? # 1: named parameter key?
+ (?:
+ """(.*?)""" # 2: triple-quoted value
+ |
+ "([^"]*?)" # 3: single-quoted value
+ |
+ (\S+) # 4: unquoted value
+ )
+ (?:\s+|$) # delimiter to next param
+ }sgx) {
+ my $key=$1;
+ my $val;
+ if (defined $2) {
+ $val=$2;
+ $val=~s/\r\n/\n/mg;
+ $val=~s/^\n+//g;
+ $val=~s/\n+$//g;
+ }
+ elsif (defined $3) {
+ $val=$3;
+ }
+ elsif (defined $4) {
+ $val=$4;
+ }
+
+ if (defined $key) {
+ $phash{$key} = $val;
+ }
+ else {
+ $phash{''} = $val;
+ }
+ }
+ if (defined $phash{data})
+ {
+ $yml_str = $phash{data};
+ $content =~ /^(.*?)\[\[!ymlfront.*?\]\](.*?)$/s;
+ $start_of_content = $1;
+ $rest_of_content = $2;
+ # TODO: This breaks if the YAML string itself contains ]].
+ # Workaround: all [[!ymlfront [...]]] directives shall be
+ # at the end of the files.
+ $rest_of_content = '';
+ }
+ }
+ }
+ }
+ if ($yml_str)
+ {
+ # if {{$page}} is there, do an immediate substitution
+ $yml_str =~ s/\{\{\$page\}\}/$page/sg;
+
+ my $ydata;
+ eval q{$ydata = Load($yml_str);};
+ if ($@)
+ {
+ debug("ymlfront: Load of $page failed: $@");
+ return undef;
+ }
+ if (!$ydata)
+ {
+ debug("ymlfront: no YAML for $page");
+ return undef;
+ }
+ my %lc_data = ();
+ if ($ydata)
+ {
+ # make lower-cased versions of the data
+ foreach my $fn (keys %{$ydata})
+ {
+ my $fval = $ydata->{$fn};
+ $lc_data{lc($fn)} = $fval;
+ }
+ }
+ return { yml=>\%lc_data,
+ content=>$start_of_content . $rest_of_content};
+ }
+ return { yml=>undef, content=>$content };
+} # parse_yml
+1;