suggested plugin: blocks (free relationships)

master
chrysn 2013-08-09 01:14:44 +02:00
parent 415fecfcb7
commit bcfb05123c
3 changed files with 214 additions and 0 deletions

View File

@ -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

View File

@ -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

View 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/flexible relationships between pages]]
* [[todo/inline postform autotitles]]
* [[todo/internal definition list support]]
* [[todo/mirrorlist with per-mirror usedirs settings]]