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