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