Merge commit 'upstream/master' into prv/po
[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 3.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 (pagespec_match_list([keys %pagesources],
36                                 $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         # Common prefix should not be a page in the map.
65         while (defined $common_prefix && length $common_prefix &&
66                exists $mapitems{$common_prefix}) {
67                 $common_prefix=IkiWiki::dirname($common_prefix);
68         }
69
70         # Needs to update whenever a page is added or removed (or in some
71         # cases, when its content changes, if show=title), so register a
72         # dependency.
73         add_depends($params{page}, $params{pages});
74         # Explicitly add all currently shown pages, to detect when pages
75         # are removed.
76         foreach my $item (keys %mapitems) {
77                 add_depends($params{page}, $item);
78         }
79
80         # Create the map.
81         my $parent="";
82         my $indent=0;
83         my $openli=0;
84         my $addparent="";
85         my $map = "<div class='map'>\n";
86
87         # Return empty div if %mapitems is empty
88         if (!scalar(keys %mapitems)) {
89                 $map .= "</div>\n";
90                 return $map; 
91         } 
92         else { # continue populating $map
93                 $map .= "<ul>\n";
94         }
95
96         foreach my $item (sort keys %mapitems) {
97                 my @linktext = (length $mapitems{$item} ? (linktext => $mapitems{$item}) : ());
98                 $item=~s/^\Q$common_prefix\E\///
99                         if defined $common_prefix && length $common_prefix;
100                 my $depth = ($item =~ tr/\//\//) + 1;
101                 my $baseitem=IkiWiki::dirname($item);
102                 while (length $parent && length $baseitem && $baseitem !~ /^\Q$parent\E(\/|$)/) {
103                         $parent=IkiWiki::dirname($parent);
104                         last if length $addparent && $baseitem =~ /^\Q$addparent\E(\/|$)/;
105                         $addparent="";
106                         $indent--;
107                         $map .= "</li>\n";
108                         if ($indent > 0) {
109                                 $map .= "</ul>\n";
110                         }
111                 }
112                 while ($depth < $indent) {
113                         $indent--;
114                         $map .= "</li>\n";
115                         if ($indent > 0) {
116                                 $map .= "</ul>\n";
117                         }
118                 }
119                 my @bits=split("/", $item);
120                 my $p="";
121                 $p.="/".shift(@bits) for 1..$indent;
122                 while ($depth > $indent) {
123                         $indent++;
124                         if ($indent > 1) {
125                                 $map .= "<ul>\n";
126                         }
127                         if ($depth > $indent) {
128                                 $p.="/".shift(@bits);
129                                 $addparent=$p;
130                                 $addparent=~s/^\///;
131                                 $map .= "<li>"
132                                         .htmllink($params{page}, $params{destpage},
133                                                  "/".$common_prefix.$p, class => "mapparent",
134                                                  noimageinline => 1)
135                                         ."\n";
136                                 $openli=1;
137                         }
138                         else {
139                                 $openli=0;
140                         }
141                 }
142                 $map .= "</li>\n" if $openli;
143                 $map .= "<li>"
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                 $indent--;
154                 $map .= "</li>\n</ul>\n";
155         }
156         $map .= "</div>\n";
157         return $map;
158 }
159
160 1