1#!/usr/bin/perl
2#
3# Produce a hierarchical map of links.
4#
5# by Alessandro Dotti Contra <alessandro@hyboria.org>
6#
7# Revision: 0.2
8package IkiWiki::Plugin::map;
9
10use warnings;
11use strict;
12use IkiWiki 3.00;
13
14sub import {
15	hook(type => "getsetup", id => "map", call => \&getsetup);
16	hook(type => "preprocess", id => "map", call => \&preprocess);
17}
18
19sub getsetup () {
20	return
21		plugin => {
22			safe => 1,
23			rebuild => undef,
24			section => "widget",
25		},
26}
27
28sub preprocess (@) {
29	my %params=@_;
30	$params{pages}="*" unless defined $params{pages};
31
32	# Needs to update whenever a page is added or removed (or in some
33	# cases, when its content changes, if show= is specified).
34	my $deptype=deptype(exists $params{show} ? "content" : "presence");
35
36	my $common_prefix;
37
38	# Get all the items to map.
39	my %mapitems;
40	foreach my $page (pagespec_match_list($params{page}, $params{pages},
41					deptype => $deptype)) {
42		if (exists $params{show} &&
43		    exists $pagestate{$page} &&
44		    exists $pagestate{$page}{meta}{$params{show}}) {
45			$mapitems{$page}=$pagestate{$page}{meta}{$params{show}};
46		}
47		else {
48			$mapitems{$page}='';
49		}
50		# Check for a common prefix.
51		if (! defined $common_prefix) {
52			$common_prefix=$page;
53		}
54		elsif (length $common_prefix &&
55		       $page !~ /^\Q$common_prefix\E(\/|$)/) {
56			my @a=split(/\//, $page);
57			my @b=split(/\//, $common_prefix);
58			$common_prefix="";
59			while (@a && @b && $a[0] eq $b[0]) {
60				if (length $common_prefix) {
61					$common_prefix.="/";
62				}
63				$common_prefix.=shift(@a);
64				shift @b;
65			}
66		}
67	}
68
69	# Common prefix should not be a page in the map.
70	while (defined $common_prefix && length $common_prefix &&
71	       exists $mapitems{$common_prefix}) {
72		$common_prefix=IkiWiki::dirname($common_prefix);
73	}
74
75	# Set this to 1 or more spaces to pretty-print maps for debugging
76	my $spaces = "";
77
78	# Create the map.
79	my $parent="";
80	my $indent=0;
81	my $openli=0;
82	my $addparent="";
83	my $map = "<div class='map'>\n";
84
85	if (! keys %mapitems) {
86		# return empty div for empty map
87		$map .= "</div>\n";
88		return $map;
89	}
90	else {
91		$map .= "<ul>\n";
92	}
93
94	foreach my $item (sort keys %mapitems) {
95		my @linktext = (length $mapitems{$item} ? (linktext => $mapitems{$item}) : ());
96		$item=~s/^\Q$common_prefix\E\///
97			if defined $common_prefix && length $common_prefix;
98		my $depth = ($item =~ tr/\//\//) + 1;
99		my $baseitem=IkiWiki::dirname($item);
100		while (length $parent && length $baseitem && $baseitem !~ /^\Q$parent\E(\/|$)/) {
101			$parent=IkiWiki::dirname($parent);
102			last if length $addparent && $baseitem =~ /^\Q$addparent\E(\/|$)/;
103			$addparent="";
104			$map .= ($spaces x $indent) . "</li>\n";
105			if ($indent > 1) {
106				$map .= ($spaces x $indent) . "</ul><map:collapse>\n";
107			}
108			$indent--;
109		}
110		while ($depth < $indent) {
111			$map .= ($spaces x $indent) . "</li>\n";
112			if ($indent > 1) {
113				$map .= ($spaces x $indent) . "</ul>\n";
114			}
115			$indent--;
116		}
117		my @bits=split("/", $item);
118		my $p="";
119		$p.="/".shift(@bits) for 1..$indent;
120		while ($depth > $indent) {
121			$indent++;
122			if ($indent > 1) {
123				$map .= ($spaces x $indent) . "<ul><map:collapse>\n";
124			}
125			if ($depth > $indent) {
126				$p.="/".shift(@bits);
127				$addparent=$p;
128				$addparent=~s/^\///;
129				$map .= ($spaces x $indent) . "<li>\n";
130				$map .= ($spaces x $indent)
131					.htmllink($params{page}, $params{destpage},
132						 "/".$common_prefix.$p, class => "mapparent",
133						 noimageinline => 1)
134					."\n";
135				$openli=1;
136			}
137			else {
138				$openli=0;
139			}
140		}
141		$map .= ($spaces x $indent) . "</li>\n" if $openli;
142		$map .= ($spaces x $indent) . "<li>\n";
143		$map .= ($spaces x $indent)
144			.htmllink($params{page}, $params{destpage},
145				"/".$common_prefix."/".$item,
146				@linktext,
147				class => "mapitem", noimageinline => 1)
148			."\n";
149		$openli=1;
150		$parent=$item;
151	}
152	while ($indent > 0) {
153		$map .= ($spaces x $indent) . "</li>\n";
154		$indent--;
155		$map .= ($spaces x $indent) . "</ul>\n";
156	}
157	$map =~ s{\n *</ul><map:collapse>\n *<ul><map:collapse>\n}{\n}gs;
158	$map =~ s{<map:collapse>}{}g;
159	$map .= "</div>\n";
160	return $map;
161}
162
1631
164