suggested plugin: blocks (free relationships)
authorchrysn <chrysn@fsfe.org>
Thu, 8 Aug 2013 23:14:44 +0000 (01:14 +0200)
committerchrysn <chrysn@fsfe.org>
Thu, 8 Aug 2013 23:18:38 +0000 (01:18 +0200)
doc/todo/flexible_relationships_between_pages.mdwn [new file with mode: 0644]
doc/todo/flexible_relationships_between_pages/blocks.pm.mdwn [new file with mode: 0644]
doc/users/chrysn/interests.mdwn

diff --git a/doc/todo/flexible_relationships_between_pages.mdwn b/doc/todo/flexible_relationships_between_pages.mdwn
new file mode 100644 (file)
index 0000000..ee3dd8d
--- /dev/null
@@ -0,0 +1,84 @@
+it has been some years since the [[matching different kinds of links]] issue
+was tackled, but hardly a plugin is using it.
+
+in order to enhance on the [[todo/rel attribute for links]] and [[todo/better bug tracking support]]
+issues and to provide a more general infrastructure, i'd like to propose a
+generic plugin for typed links. following the use case i've developed it for,
+i'll call it `blocks` for the moment (but am open to better suggestions).
+
+outline
+=======
+
+the plugin has a **configuration option** called `blocks_names`, which consists
+of pairs of verbs; the typical example is `blocks/blockedby`, but other values
+could be `next/prev up/down` or `owner/owns`.
+
+for each verb in the options, there is a **directive** which is used to state
+the relationship; relationships can be declared on both ends, so a page `bugA`
+with the contents `\[[!blocks bugB]]` is semantically equivalent to a page
+`bugB` with the contents `\[[!blockedby bugA]]`.
+
+for each verb, there is also a **pagespec** which matches all pages that are
+the origin of a relationship to a given page. if `developerA` `\[[!owns
+bug1]]`, then if `bug1` contains `\[[!map pages="owns(.)"]]`, it will show the
+owning developer. these specs match both ways, ie. if `bug1` `\[[!owner
+developerA]]`, the said map directive will still produce the same result.
+
+details
+=======
+
+* single word relationships vs. symmetric relationships
+
+  with some verbs, it is possible that a relationship is only used in one
+  direction (eg `index`, even though one could declare it as
+  `index/isindexof`).
+
+  other verbs are symmetric, eg. `equivalent`, which need different treatment.
+
+* "taglink" style directives
+
+  the [[plugins/tag]] plugin would be a special case for this plugin (apart
+  from the autotag and tagdir features). as there is a `\[[!taglink ...]]`
+  directive, there could be an analogous directive for every single directive.
+
+* implementation notes
+
+  the way pagespec hooks are implemented required some nasty perl tricks, for
+  which the people who showed me felt very bad for having spoilt me. indeed,
+  `no strict refs;` and `*$forward_name = $forward_match;` are not exactly
+  ideal. a change in the pagespec declaration api (why not just `hook` like
+  everything else) would make the implementation cleaner.
+
+* configuration location
+
+  i aimed for static configuration of the `block_names` in the setup file. this
+  could be made more general like in the [[plugins/shortcut]] plugin, but that
+  would make things more complex.
+
+* no html links with `rel=` yet
+
+  as there are no taglink style links between the articles so far, no htmllink
+  gets rendered that could carry the relationship name in its rel field.
+
+  having the inverse relationship description in backlinks (as in the link
+  created by the map directive in the example above) would be hard to
+  implement. (actually, i think it'd be easier to determine the rel values from
+  the taggedlinks for *every* htmllink than to influence the backlinks in this
+  plugin).
+
+* one direction also creates a normal link
+
+  due to the way add\_link treats relationships, the forward relationship is
+  always going to be reflected in the links/backlinks. a section of
+  [[todo/matching different kinds of links]] was dismissed with "let's not
+  worry about it", this plugin might be reason to worry about it again. (i'd
+  consider what is in @links to be a representation of which hyperlinks are
+  there, and in this case, none are generated).
+
+implementation
+==============
+
+there is a working but slightly incomplete (basically where it comes to the
+details mentioned above) implementation in [[blocks.pm]].
+
+--chrysn
diff --git a/doc/todo/flexible_relationships_between_pages/blocks.pm.mdwn b/doc/todo/flexible_relationships_between_pages/blocks.pm.mdwn
new file mode 100644 (file)
index 0000000..0ef97b2
--- /dev/null
@@ -0,0 +1,129 @@
+#!/usr/bin/perl
+# Ikiwiki "blocks" relationship plugin.
+package IkiWiki::Plugin::blocks;
+
+use warnings;
+use strict;
+use IkiWiki 3.00;
+
+sub import {
+       hook(type => "getsetup", id => "blocks", call => \&getsetup);
+       hook(type => "checkconfig", id => "skeleton", call => \&checkconfig);
+}
+
+sub getsetup () {
+       return
+               plugin => {
+                       safe => 1,
+                       rebuild => 1,
+               },
+               blocks_names => {
+                       type => "string",
+                       example => "blocks/blockedby",
+                       description => "comma separated list of defined relationship pairs, the forward and backward name separated by a slash",
+                       safe => 1,
+                       rebuild => 1,
+               },
+}
+
+sub checkconfig () {
+       my $blocksnames;
+       if (defined $config{blocks_names}) {
+               $blocksnames = $config{blocks_names};
+       } else {
+               $blocksnames = "blocks/blockedby";
+       }
+
+       while ( $blocksnames =~ /([^ ]+)/g )
+       {
+               if ( $1 =~ m@([a-zA-Z0-9]+)(/([a-zA-Z0-9]+))?@ )
+               {
+                       my $from = $1;
+                       my $to = $3;
+                       hook(
+                               type => "preprocess",
+                               shortcut => 1, # gets interpreted by listdirectives; see doc/bugs/cannot_preview_shortcuts.mdwn / ikiwiki commit 354d22e2
+                               no_override => 1,
+                               id => $from,
+                               scan => 1,
+                               call => sub { preprocess_blocks($from, 1, @_); }
+                       );
+                       if ($to)
+                       {
+                               hook(
+                                       type => "preprocess",
+                                       shortcut => 1,
+                                       no_override => 1,
+                                       id => $to,
+                                       scan => 1,
+                                       call => sub { preprocess_blocks($from, 0, @_); }
+                               );
+                       }
+
+                       my $backward_match; my $backward_name;
+                       my $forward_match; my $forward_name;
+
+                       $backward_match = sub ($$;@) {
+                               my $page=shift;
+                               my $glob=shift;
+                               return IkiWiki::PageSpec::match_backlink($page, $glob, linktype => $from, @_);
+                       };
+
+                       $backward_name = "IkiWiki::PageSpec::match_$from";
+
+                       if ($to)
+                       {
+                               $forward_match = sub ($$;@) {
+                                       my $page=shift;
+                                       my $glob=shift;
+                                       return IkiWiki::PageSpec::match_link($page, $glob, linktype => $from, @_);
+                               };
+
+                               $forward_name = "IkiWiki::PageSpec::match_$to";
+                       }
+
+                       {
+                               no strict 'refs';
+
+                               if ($to)
+                               {
+                                       *$forward_name = $forward_match;
+                               }
+                               *$backward_name = $backward_match;
+                       }
+               } else {
+                       error gettext("Malformed option in blocks_names");
+               }
+       }
+}
+
+sub preprocess_blocks ($$@) {
+       # with flip=0, the directive occurring on page A pointing at page B
+       # means that A $relation B, with flip=1, it means B $relation A
+       my $relation = shift;
+       my $flip = shift;
+
+       if (! @_) {
+               return "";
+       }
+       my %params=@_;
+       my $page = $params{page};
+       delete $params{page};
+       delete $params{destpage};
+       delete $params{preview};
+
+       foreach my $blocks (keys %params) {
+               $blocks=linkpage($blocks);
+               
+               # hidden WikiLink
+               if ( $flip == 0 ) {
+                       add_link($page, $blocks, $relation);
+               } else {
+                       add_link($blocks, $page, $relation);
+               }
+       }
+               
+       return "";
+}
+
+1
index a70089a455779b78df6fe9da2d11deae08c33b11..a5c8f33bf218a90f7a819efec8a85d4f8a872321 100644 (file)
@@ -14,6 +14,7 @@ these are the topics [[chrysn]] is or was interested in inside ikiwiki:
 * [[todo/Better bug tracking support]]
 * [[todo/calendar with "create" links]]
 * [[todo/credentials page]]
 * [[todo/Better bug tracking support]]
 * [[todo/calendar with "create" links]]
 * [[todo/credentials page]]
+* [[todo/flexible relationships between pages]]
 * [[todo/inline postform autotitles]]
 * [[todo/internal definition list support]]
 * [[todo/mirrorlist with per-mirror usedirs settings]]
 * [[todo/inline postform autotitles]]
 * [[todo/internal definition list support]]
 * [[todo/mirrorlist with per-mirror usedirs settings]]