po(mybestlink): fixed when fed with path beginning with /
[ikiwiki] / IkiWiki / Plugin / map.pm
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
8 package IkiWiki::Plugin::map;
9
10 use warnings;
11 use strict;
12 use IkiWiki 2.00;
13
14 sub import { #{{{
15         hook(type => "getsetup", id => "map", call => \&getsetup);
16         hook(type => "preprocess", id => "map", call => \&preprocess);
17 } # }}}
18
19 sub getsetup () { #{{{
20         return
21                 plugin => {
22                         safe => 1,
23                         rebuild => undef,
24                 },
25 } #}}}
26
27 sub preprocess (@) { #{{{
28         my %params=@_;
29         $params{pages}="*" unless defined $params{pages};
30         
31         my $common_prefix;
32
33         # Get all the items to map.
34         my %mapitems;
35         foreach my $page (keys %pagesources) {
36                 if (pagespec_match($page, $params{pages}, location => $params{page})) {
37                         if (exists $params{show} && 
38                             exists $pagestate{$page} &&
39                             exists $pagestate{$page}{meta}{$params{show}}) {
40                                 $mapitems{$page}=$pagestate{$page}{meta}{$params{show}};
41                         }
42                         else {
43                                 $mapitems{$page}='';
44                         }
45                         # Check for a common prefix.
46                         if (! defined $common_prefix) {
47                                 $common_prefix=$page;
48                         }
49                         elsif (length $common_prefix &&
50                                $page !~ /^\Q$common_prefix\E(\/|$)/) {
51                                 my @a=split(/\//, $page);
52                                 my @b=split(/\//, $common_prefix);
53                                 $common_prefix="";
54                                 while (@a && @b && $a[0] eq $b[0]) {
55                                         if (length $common_prefix) {
56                                                 $common_prefix.="/";
57                                         }
58                                         $common_prefix.=shift(@a);
59                                         shift @b;
60                                 }
61                         }
62                 }
63         }
64         
65         # Common prefix should not be a page in the map.
66         while (defined $common_prefix && length $common_prefix &&
67                exists $mapitems{$common_prefix}) {
68                 $common_prefix=IkiWiki::dirname($common_prefix);
69         }
70
71         # Needs to update whenever a page is added or removed (or in some
72         # cases, when its content changes, if show=title), so register a
73         # dependency.
74         add_depends($params{page}, $params{pages});
75         # Explicitly add all currently shown pages, to detect when pages
76         # are removed.
77         add_depends($params{page}, join(" or ", keys %mapitems));
78
79         # Create the map.
80         my $parent="";
81         my $indent=0;
82         my $openli=0;
83         my $addparent="";
84         my $map = "<div class='map'>\n<ul>\n";
85         foreach my $item (sort keys %mapitems) {
86                 my @linktext = (length $mapitems{$item} ? (linktext => $mapitems{$item}) : ());
87                 $item=~s/^\Q$common_prefix\E\///
88                         if defined $common_prefix && length $common_prefix;
89                 my $depth = ($item =~ tr/\//\//) + 1;
90                 my $baseitem=IkiWiki::dirname($item);
91                 while (length $parent && length $baseitem && $baseitem !~ /^\Q$parent\E(\/|$)/) {
92                         $parent=IkiWiki::dirname($parent);
93                         last if length $addparent && $baseitem =~ /^\Q$addparent\E(\/|$)/;
94                         $addparent="";
95                         $indent--;
96                         $map .= "</li>\n";
97                         if ($indent > 0) {
98                                 $map .= "</ul>\n";
99                         }
100                 }
101                 while ($depth < $indent) {
102                         $indent--;
103                         $map .= "</li>\n";
104                         if ($indent > 0) {
105                                 $map .= "</ul>\n";
106                         }
107                 }
108                 my @bits=split("/", $item);
109                 my $p="";
110                 $p.="/".shift(@bits) for 1..$indent;
111                 while ($depth > $indent) {
112                         $indent++;
113                         if ($indent > 1) {
114                                 $map .= "<ul>\n";
115                         }
116                         if ($depth > $indent) {
117                                 $p.="/".shift(@bits);
118                                 $addparent=$p;
119                                 $addparent=~s/^\///;
120                                 $map .= "<li>"
121                                         .htmllink($params{page}, $params{destpage},
122                                                  "/".$common_prefix.$p, class => "mapparent",
123                                                  noimageinline => 1)
124                                         ."\n";
125                                 $openli=1;
126                         }
127                         else {
128                                 $openli=0;
129                         }
130                 }
131                 $map .= "</li>\n" if $openli;
132                 $map .= "<li>"
133                         .htmllink($params{page}, $params{destpage}, 
134                                 "/".$common_prefix."/".$item,
135                                 @linktext,
136                                 class => "mapitem", noimageinline => 1)
137                         ."\n";
138                 $openli=1;
139                 $parent=$item;
140         }
141         while ($indent > 0) {
142                 $indent--;
143                 $map .= "</li>\n</ul>\n";
144         }
145         $map .= "</div>\n";
146         return $map;
147 } # }}}
148
149 1