Attachment 'VisualSiteMap_1.2.py'
Download 1 """
2 MoinMoin - VisualSiteMap action
3
4 Idea is based on the webdot.py action.
5
6 More or less redid it from scratch. Differs from the webdot action in several ways:
7 * Uses the dot executable, not webdot, since webdot's not available on windows.
8 * All links up to the search depth are displayed.
9 * There's no maximal limit to the displayed nodes.
10 * Nodes are marked during depth first visit, so each node is visited only once.
11 * The visit method in class LocalSiteMap gets the whole tree as parameter. That way additional treenode information
12 may be shown in the graph.
13 * All edges between nodes contained in the graph are displayed, even if MAX_DEPTH is exceeded that way.
14 * Optional depth controls
15 * Nodes linked more then STRONG_LINK_NR times are highlighted using the STRONG_COLOR
16 * Search depth is configurable
17
18 Add this to your stylesheet:
19 img.sitemap
20 {
21 border-width: 1;
22 border-color: #000000;
23 }
24
25 07.10.2004
26 * Maximum image size can be configured
27 * Output image format is configurable
28 * David Linke changed the output code (print() -> request.write())
29 * Changed link counting algorithm to get the depth controls right.
30
31 08.10.2004
32 * IE caching problem with depth controls resolved. Now the current search depth is part of the file names.
33 * Problems with pagenames containing non ASCII characters fixed.
34 $Id$
35 """
36
37 ##################################################################
38 # Be warned that calculating large graphs may block your server! #
39 # So be careful with the parameter settings. #
40 ##################################################################
41
42 # Imports
43 import string,sys,re,os
44 from MoinMoin import config, wikiutil, user
45 from MoinMoin.Page import Page
46
47 # Graph controls.
48 DEFAULT_DEPTH = 2
49 STRONG_LINK_NR = 4
50
51 # Optional controls for interactive modification of the search depth.
52 DEPTH_CONTROL = 0
53 MAX_DEPTH = 4
54
55 # This should be a public path on your web server. The dot files, images and map files are created in this directory and
56 # served from there.
57 CACHE_DIR = "C:/DocumentRoot/cache/";
58 CACHE_URL = "http://my-server/cache/";
59
60 # Absolute location of the dot (or neato) executable.
61 DOT_EXE = "C:/Programme/ATT/GraphViz/bin/dot.exe";
62
63 # Desired image format (eg. png, jpg, gif - see the dot documentation)
64 OUTPUT_FORMAT = "png"
65
66 # Maximum output size in inches. Set to None to disable size limitation.
67 # OUTPUT_SIZE="8,4" sets maximum width to 8, maximum height to 4 inches.
68 OUTPUT_SIZE="10,10"
69
70 # Colors of boxes and edges.
71 BOX_COLOR ="#E0F0FF"
72 ROOT_COLOR = "#FFE0E0"
73 STRONG_COLOR = "#E0FFE0"
74 EDGE_COLOR ="#888888"
75
76 # Categories are filtered in some way.
77 CATEGORY_STRING = "^Kategorie"
78
79 # Code starts here
80 def execute(pagename, request):
81 _ = request.getText
82
83 maxdepth = int(DEFAULT_DEPTH)
84 if DEPTH_CONTROL and request.form.has_key('depth'):
85 maxdepth = int(request.form['depth'][0])
86
87 if int(maxdepth) > int(MAX_DEPTH):
88 maxdepth = MAX_DEPTH
89
90 request.http_headers()
91 wikiutil.send_title(request, _('Visual Map of %s') % (pagename), pagename=pagename)
92
93 baseurl = request.getBaseURL()
94
95 wikiname = wikiutil.quoteWikiname(pagename)
96 dotfilename = '%s/%s_%s.dot' % (CACHE_DIR, wikiname, maxdepth)
97 imagefilename = '%s/%s_%s.%s' % (CACHE_DIR, wikiname, maxdepth, OUTPUT_FORMAT)
98 imageurl = '%s/%s_%s.%s' % (CACHE_URL, wikiname, maxdepth, OUTPUT_FORMAT)
99 mapfilename = '%s/%s_%s.cmap' % (CACHE_DIR, wikiname, maxdepth)
100
101 dotfile = open(dotfilename,'w')
102
103 dotfile.write('digraph G {\n')
104 if OUTPUT_SIZE:
105 dotfile.write(' size="%s"\n' % OUTPUT_SIZE)
106 dotfile.write(' ratio=compress;\n')
107 dotfile.write(' URL="%s";\n' % wikiname)
108 dotfile.write(' overlap=false;\n')
109 dotfile.write(' concentrate=true;\n')
110 dotfile.write(' edge [color="%s"];\n' % EDGE_COLOR)
111 dotfile.write(' node [URL="%s/\N", ' % baseurl)
112 dotfile.write('fontcolor=black, fontname=%s , fontsize=%s, style=filled, color="%s"]\n' % ("arial","8", BOX_COLOR))
113 dotfile.write(LocalSiteMap(pagename, maxdepth).output(request))
114 dotfile.write('}\n')
115 dotfile.close()
116
117 os.system('%s -T%s -o%s %s' % (DOT_EXE, OUTPUT_FORMAT, imagefilename, dotfilename))
118 os.system('%s -Tcmap -o%s %s' % (DOT_EXE, mapfilename, dotfilename))
119
120 request.write('<center><img class="sitemap" border=1 src="%s" usemap="#map1"></center>' % (imageurl))
121 request.write('<map name="map1">')
122 mapfile = open(mapfilename,'r')
123 for row in mapfile.readlines():
124 request.write(row)
125 mapfile.close()
126
127 request.write('</map>')
128
129 if DEPTH_CONTROL:
130 linkname = wikiutil.quoteWikiname(pagename)
131 request.write('<p align="center">')
132 if maxdepth > 1:
133 request.write('<a href="%s/%s?action=VisualSiteMap&depth=%s">Less</a>' % (baseurl, linkname, maxdepth-1))
134 else:
135 request.write('Less')
136
137 request.write(' | ')
138
139 if maxdepth < MAX_DEPTH:
140 request.write('<a href="%s/%s?action=VisualSiteMap&depth=%s">More</a>' % (baseurl, linkname, maxdepth+1))
141 else:
142 request.write('More')
143 request.write('</p>')
144
145 request.write('<p align="center"><small>Search depth is %s. Nodes linked more than %s times are highlighted.</small></p>' % (maxdepth, STRONG_LINK_NR))
146
147 wikiutil.send_footer(request, pagename)
148
149 class LocalSiteMap:
150 def __init__(self, name, maxdepth):
151 self.name = name
152 self.result = []
153 self.maxdepth = maxdepth
154
155 def output(self, request):
156 pagebuilder = GraphBuilder(request, self.maxdepth)
157 root = pagebuilder.build_graph(self.name)
158 # count links
159 # print '<h2> All links </h2>'
160 for edge in pagebuilder.all_edges:
161 edge[0].linkedfrom += 1
162 edge[1].linkedto += 1
163 # print edge[0].name + '->' + edge[1].name + '<BR>'
164 # write nodes
165 for node in pagebuilder.all_nodes:
166 self.append(' %s'% wikiutil.quoteWikiname(node.name))
167 if node.depth > 0:
168 if node.linkedto >= STRONG_LINK_NR:
169 self.append(' [label="%s",color="%s"];\n' % (node.name, STRONG_COLOR))
170 else:
171 self.append(' [label="%s"];\n' % (node.name))
172
173 else:
174 self.append('[label="%s",shape=box,style=filled,color="%s"];\n' % (node.name, ROOT_COLOR))
175 # write edges
176 for edge in pagebuilder.all_edges:
177 self.append(' %s->%s;\n' % (wikiutil.quoteWikiname(edge[0].name),wikiutil.quoteWikiname(edge[1].name)))
178
179 return string.join(self.result, '')
180
181 def append(self, text):
182 self.result.append(text)
183
184
185 class GraphBuilder:
186
187 def __init__(self, request, maxdepth):
188 self.request = request
189 self.maxdepth = maxdepth
190 self.all_nodes = []
191 self.all_edges = []
192
193 def is_ok(self, child):
194 if not self.request.user.may.read(child):
195 return 0
196 if Page(child).exists() and (not re.search(r'%s' % CATEGORY_STRING,child)):
197 return 1
198 return 0
199
200 def build_graph(self, name):
201 # Reuse generated trees
202 nodesMap = {}
203 root = Node(name)
204 nodesMap[name] = root
205 root.visited = 1
206 self.all_nodes.append(root)
207 self.recurse_build([root], 1, nodesMap)
208 return root
209
210 def recurse_build(self, nodes, depth, nodesMap):
211 # collect all nodes of the current search depth here for the next recursion step
212 child_nodes = []
213 # iterate over the nodes
214 for node in nodes:
215 # print "<h2>%s: Kids of %s</h2>" % (depth,node.name)
216 for child in Page(node.name).getPageLinks(self.request):
217 if self.is_ok(child):
218 # print "Child %s" % child
219 # Create the node with the given name
220 if not nodesMap.has_key(child):
221 # create the new node and store it
222 newNode = Node(child)
223 newNode.depth = depth
224 # print "is new"
225 else:
226 newNode = nodesMap[child]
227 # print "is old"
228 # print ". <br>"
229 # If the current depth doesn't exceed the maximum depth, add newNode to recursion step
230 if (int(depth) <= int(self.maxdepth)):
231 # The node is appended to the nodes list for the next recursion step.
232 nodesMap[child] = newNode
233 self.all_nodes.append(newNode)
234 child_nodes.append(newNode)
235 node.append(newNode)
236 # Draw an edge.
237 edge = (node, newNode)
238 if (not edge in self.all_edges):
239 self.all_edges.append(edge)
240 # recurse, if the current recursion step yields children
241 if len(child_nodes):
242 self.recurse_build(child_nodes, depth+1, nodesMap)
243
244 class Node:
245 def __init__(self, name):
246 self.name = name
247 self.children = []
248 self.visited = 0
249 self.linkedfrom = 0
250 self.linkedto = 0
251 self.depth = 0
252
253 def append(self, node):
254 self.children.append(node)
Attached Files
To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.You are not allowed to attach a file to this page.