suggested plugin: blocks (free relationships)
parent
415fecfcb7
commit
bcfb05123c
|
@ -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
|
|
@ -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
|
|
@ -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]]
|
||||
|
|
Loading…
Reference in New Issue