Attachment 'VisualSiteMap_1.10.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 is 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.
  12       That way additional treenode information 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 the following line to your (apache?) webserver configuration:
  19       Alias /moin_cache/ /var/cache/moin/
  20     See CACHE_URL and CACHE_DIR below.
  21 
  22     Add this to your stylesheet:
  23     img.sitemap
  24     {
  25       border-width: 1;
  26       border-color: #000000;
  27     }
  28 
  29     The following settings may be worth a change:
  30     * DEPTH_CONTROL
  31     * OUTPUT_FORMAT
  32     * DEFAULT_DEPTH
  33     * MAX_DEPTH
  34     * LINK_TO_SITEMAP
  35 
  36     07.10.2004
  37     * Maximum image size can be configured
  38     * Output image format is configurable
  39     * David Linke changed the output code (print() -> request.write())
  40     * Changed link counting algorithm to get the depth controls right.
  41 
  42     08.10.2004
  43     * IE caching problem with depth controls resolved. Now the current search depth is part of the file names.
  44     * Problems with pagenames containing non ASCII characters fixed.
  45 
  46     14.03.2005
  47     * cleanup & adapted to moin 1.3.4 -- ThomasWaldmann
  48     * Fixed for utf-8 and sub pages
  49 
  50     16.3.2005
  51     * included patch from David Linke for Windows compatibility
  52     * FONTNAME and FONTSIZE
  53     * removed invalid print debug statements
  54     * use config.charset
  55 
  56     19.08.2014
  57     * Cleanup & adapted for moin 1.9.4
  58     * Changed default output type from PNG to SVG.
  59     * Use configured category regex instead of separate setting.
  60     * Configurable: node links point to the VisualSiteMap of the target node (instead of its wiki page).
  61     * Fixed FS/URL escaping inconsistency for pagenames with special characters.
  62     * Enabled DEPTH_CONTROL by default.
  63 
  64     01.09.2015 - v1.10
  65     * escape xml-Entities in URLs (this broke svg output)
  66     * fixed a python3-incompatibility (old octal number format)
  67     * tested with moin 1.9.8
  68 """
  69 
  70 ##################################################################
  71 # Be warned that calculating large graphs may block your server! #
  72 # So be careful with the parameter settings.                     #
  73 ##################################################################
  74 
  75 # This should be a public path on your web server. The dot files, images and map files are created in this directory and
  76 # served from there.
  77 #CACHE_DIR  = "C:/DocumentRoot/cache"
  78 #CACHE_URL  = "http://my-server/cache"
  79 CACHE_DIR  = "/var/cache/moin/"
  80 CACHE_URL  = "/moin_cache"
  81 
  82 # Absolute location of the dot (or neato) executable.
  83 #DOT_EXE    = "C:/Programme/ATT/GraphViz/bin/dot.exe"
  84 #DOT_EXE    = "/usr/bin/dot"
  85 DOT_EXE    = "/usr/bin/neato"
  86 
  87 # Graph controls.
  88 DEFAULT_DEPTH = 2
  89 STRONG_LINK_NR = 4
  90 
  91 # nodes are linked their sitemap (instead of their wiki page)
  92 LINK_TO_SITEMAP = True
  93 
  94 # Optional controls for interactive modification of the search depth.
  95 DEPTH_CONTROL = True
  96 MAX_DEPTH  = 4
  97 
  98 # Desired image format (eg. png, jpg, gif - see the dot documentation)
  99 OUTPUT_FORMAT = "svg"
 100 
 101 # Maximum output size in inches. Set to None to disable size limitation,
 102 # then the graph is made as big as needed (best for readability).
 103 # OUTPUT_SIZE="8,12" sets maximum width to 8, maximum height to 12 inches.
 104 OUTPUT_SIZE = None
 105 
 106 # Name and Size of the font use
 107 # Times, Helvetica, Courier, Symbol are supported on any platform.
 108 # Others may NOT be supported.
 109 # When selecting a font, make sure it support unicode chars (at least the
 110 # ones you use, e.g. german umlauts or french accented chars).
 111 FONTNAME = "Times"
 112 FONTSIZE = "10"
 113 
 114 # Colors of boxes and edges.
 115 BOX_COLOR = "#E0F0FF"
 116 ROOT_COLOR = "#FFE0E0"
 117 STRONG_COLOR = "#E0FFE0"
 118 EDGE_COLOR = "#888888"
 119 
 120 
 121 import re
 122 import os
 123 import subprocess
 124 
 125 from MoinMoin import config, wikiutil
 126 from MoinMoin.Page import Page
 127 
 128 # escape special characters for XML output
 129 try:
 130     # python 3
 131     from xml import escape as xml_escape
 132 except ImportError:
 133     # python 2
 134     from cgi import escape as xml_escape
 135 
 136 
 137 class LocalSiteMap:
 138     def __init__(self, name, maxdepth):
 139         self.name = name
 140         self.maxdepth = maxdepth
 141         self.result = []
 142 
 143     def output(self, request):
 144         pagebuilder = GraphBuilder(request, self.maxdepth)
 145         root = pagebuilder.build_graph(self.name)
 146         # count links
 147         for edge in pagebuilder.all_edges:
 148             edge[0].linkedfrom += 1
 149             edge[1].linkedto += 1
 150         # write nodes
 151         for node in pagebuilder.all_nodes:
 152             self.append('  "%s"'% node.name)
 153             if node.depth > 0:
 154                 if node.linkedto >= STRONG_LINK_NR:
 155                     self.append('  [label="%s",color="%s"];\n' % (node.name, STRONG_COLOR))
 156                 else:
 157                     self.append('  [label="%s"];\n' % (node.name))
 158             else:
 159                 self.append('[label="%s",shape=box,style=filled,color="%s"];\n' % (node.name, ROOT_COLOR))
 160         # write edges
 161         for edge in pagebuilder.all_edges:
 162             self.append('  "%s"->"%s";\n' % (edge[0].name, edge[1].name))
 163 
 164         return ''.join(self.result)
 165 
 166     def append(self, text):
 167         self.result.append(text)
 168 
 169 
 170 class GraphBuilder:
 171 
 172     def __init__(self, request, maxdepth):
 173         self.request = request
 174         self.maxdepth = maxdepth
 175         self.all_nodes = []
 176         self.all_edges = []
 177 
 178     def is_ok(self, child):
 179         if not self.request.user.may.read(child):
 180             return 0
 181         if Page(self.request, child).exists() and not re.search(self.request.cfg.page_category_regex, child):
 182             return 1
 183         return 0
 184 
 185     def build_graph(self, name):
 186         # Reuse generated trees
 187         nodesMap = {}
 188         root = Node(name)
 189         nodesMap[name] = root
 190         root.visited = 1
 191         self.all_nodes.append(root)
 192         self.recurse_build([root], 1, nodesMap)
 193         return root
 194 
 195     def recurse_build(self, nodes, depth, nodesMap):
 196         # collect all nodes of the current search depth here for the next recursion step
 197         child_nodes = []
 198         # iterate over the nodes
 199         for node in nodes:
 200             for child in Page(self.request, node.name).getPageLinks(self.request):
 201                 if self.is_ok(child):
 202                     # Create the node with the given name
 203                     if not nodesMap.has_key(child):
 204                         # create the new node and store it
 205                         newNode = Node(child)
 206                         newNode.depth = depth
 207                     else:
 208                         newNode = nodesMap[child]
 209                     # If the current depth doesn't exceed the maximum depth, add newNode to recursion step
 210                     if depth <= self.maxdepth:
 211                         # The node is appended to the nodes list for the next recursion step.
 212                         nodesMap[child] = newNode
 213                         self.all_nodes.append(newNode)
 214                         child_nodes.append(newNode)
 215                         node.append(newNode)
 216                         # Draw an edge.
 217                         edge = (node, newNode)
 218                         if not edge in self.all_edges:
 219                             self.all_edges.append(edge)
 220         # recurse, if the current recursion step yields children
 221         if len(child_nodes):
 222             self.recurse_build(child_nodes, depth+1, nodesMap)
 223 
 224 
 225 class Node:
 226     def __init__(self, name):
 227         self.name = name
 228         self.children = []
 229         self.visited = 0
 230         self.linkedfrom = 0
 231         self.linkedto = 0
 232         self.depth = 0
 233 
 234     def append(self, node):
 235         self.children.append(node)
 236 
 237 
 238 def execute(pagename, request):
 239     _ = request.getText
 240 
 241     maxdepth = DEFAULT_DEPTH
 242     if DEPTH_CONTROL and request.values.has_key('depth'):
 243         maxdepth = int(request.values['depth'][0])
 244 
 245     if maxdepth > MAX_DEPTH:
 246         maxdepth = MAX_DEPTH
 247 
 248     baseurl = request.getBaseURL().rstrip("/")
 249     def get_page_link(pname, to_sitemap=LINK_TO_SITEMAP, **kwargs):
 250         if pname is None:
 251             pagelinkname = r'\N'
 252         else:
 253             pagelinkname = wikiutil.quoteWikinameURL(pname)
 254         if to_sitemap:
 255             link = "%s/%s?action=VisualSiteMap" % (baseurl, pagelinkname)
 256             for key, value in kwargs.iteritems():
 257                 link += xml_escape("&%s=%s" % (key, value))
 258         else:
 259             link = "%s/%s" % (baseurl, pagelinkname)
 260         return link
 261 
 262     request.theme.send_title(_('Visual Map of %s') % pagename, pagename=pagename)
 263 
 264     wikinamefs = wikiutil.quoteWikinameFS(pagename)
 265     fnprefix = os.path.join(CACHE_DIR, '%s_%s' % (wikinamefs, maxdepth))
 266     dotfilename = '%s.%s' % (fnprefix, 'dot')
 267     imagefilename = '%s.%s' % (fnprefix, OUTPUT_FORMAT)
 268     mapfilename = '%s.%s' % (fnprefix, 'cmap')
 269     imageurl = '%s/%s_%s.%s' % (CACHE_URL, wikinamefs, maxdepth, OUTPUT_FORMAT)
 270 
 271     lsm = LocalSiteMap(pagename, maxdepth).output(request).encode(config.charset)
 272 
 273     os.umask(0o22)
 274     dotfile = file(dotfilename, 'w')
 275     dotfile.write('digraph G {\n')
 276     if OUTPUT_SIZE:
 277         dotfile.write('  size="%s"\n' % OUTPUT_SIZE)
 278         dotfile.write('  ratio=compress;\n')
 279     dotfile.write('  URL="%s";\n' % get_page_link(pagename, to_sitemap=False))
 280     dotfile.write('  overlap=false;\n')
 281     dotfile.write('  concentrate=true;\n')
 282     dotfile.write('  edge [color="%s"];\n' % EDGE_COLOR)
 283     dotfile.write('  node [URL="%s", ' % get_page_link(None, depth=maxdepth))
 284     dotfile.write('fontcolor=black, fontname="%s", fontsize=%s, style=filled, color="%s"]\n' % (FONTNAME, FONTSIZE, BOX_COLOR))
 285     dotfile.write(lsm)
 286     dotfile.write('}\n')
 287     dotfile.close()
 288 
 289     subprocess.call([DOT_EXE, "-T%s" % OUTPUT_FORMAT, "-o%s" % imagefilename, dotfilename])
 290     subprocess.call([DOT_EXE, "-Tcmap", "-o%s" % mapfilename, dotfilename])
 291 
 292     # show the image
 293     request.write('<center><img class="sitemap" border="1" src="%s" usemap="#map1"/></center>' % imageurl)
 294 
 295     # image map for links ("img" does not enable svg links)
 296     request.write('<map name="map1">')
 297     mapfile = file(mapfilename, 'r')
 298     for row in mapfile:
 299         request.write(row)
 300     mapfile.close()
 301     request.write('</map>')
 302 
 303     if DEPTH_CONTROL:
 304         linkname = wikiutil.quoteWikinameURL(pagename)
 305         links = []
 306         if maxdepth > 1:
 307             links.append('<a href="%s">Less</a>' % get_page_link(pagename, depth=maxdepth-1))
 308         if maxdepth < MAX_DEPTH:
 309             links.append('<a href="%s">More</a>' % get_page_link(pagename, depth=maxdepth+1))
 310         request.write('<p align="center">%s</p>' % ' | '.join(links))
 311 
 312     request.write('<p align="center"><small>Search depth is %s. Nodes linked more than %s times are highlighted.</small></p>' % (maxdepth, STRONG_LINK_NR))
 313 
 314     request.theme.send_footer(pagename)

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.
  • [get | view] (2011-04-14 07:28:03, 4.7 KB) [[attachment:CreateNewPage.py]]
  • [get | view] (2011-04-14 07:26:24, 4.2 KB) [[attachment:CreateNewPage1.6.py]]
  • [get | view] (2006-09-10 21:29:29, 40.4 KB) [[attachment:CreatePdfDocument2_0_3.py]]
  • [get | view] (2006-09-12 06:05:06, 40.5 KB) [[attachment:CreatePdfDocument2_0_4.py]]
  • [get | view] (2006-09-12 12:00:09, 40.6 KB) [[attachment:CreatePdfDocument2_0_5.py]]
  • [get | view] (2006-11-14 21:56:11, 43.5 KB) [[attachment:CreatePdfDocument2_0_6.py]]
  • [get | view] (2006-11-15 17:00:47, 43.8 KB) [[attachment:CreatePdfDocument2_0_7.py]]
  • [get | view] (2006-11-16 22:06:18, 43.8 KB) [[attachment:CreatePdfDocument2_0_8.py]]
  • [get | view] (2006-12-17 15:54:21, 43.6 KB) [[attachment:CreatePdfDocument2_0_9.py]]
  • [get | view] (2007-08-20 09:10:23, 67.2 KB) [[attachment:CreatePdfDocument2_1_0.py]]
  • [get | view] (2007-08-21 07:39:49, 67.1 KB) [[attachment:CreatePdfDocument2_1_1.py]]
  • [get | view] (2007-09-11 19:16:37, 67.3 KB) [[attachment:CreatePdfDocument2_1_2.py]]
  • [get | view] (2007-09-18 20:17:58, 68.1 KB) [[attachment:CreatePdfDocument2_1_3.py]]
  • [get | view] (2007-09-21 13:32:54, 71.1 KB) [[attachment:CreatePdfDocument2_1_4.py]]
  • [get | view] (2007-09-23 20:56:30, 73.4 KB) [[attachment:CreatePdfDocument2_1_5.py]]
  • [get | view] (2007-09-25 20:54:48, 74.5 KB) [[attachment:CreatePdfDocument2_2_0.py]]
  • [get | view] (2008-06-23 21:08:49, 77.0 KB) [[attachment:CreatePdfDocument2_3_0.py]]
  • [get | view] (2008-06-26 19:25:07, 81.0 KB) [[attachment:CreatePdfDocument2_3_1.py]]
  • [get | view] (2008-07-06 05:50:38, 83.1 KB) [[attachment:CreatePdfDocument2_3_2.py]]
  • [get | view] (2008-07-09 17:42:02, 83.3 KB) [[attachment:CreatePdfDocument2_3_3.py]]
  • [get | view] (2008-09-07 11:11:01, 83.5 KB) [[attachment:CreatePdfDocument2_3_4.py]]
  • [get | view] (2009-01-11 15:53:09, 84.3 KB) [[attachment:CreatePdfDocument2_3_5.py]]
  • [get | view] (2009-02-16 06:52:06, 84.2 KB) [[attachment:CreatePdfDocument2_3_6.py]]
  • [get | view] (2010-01-29 11:53:21, 82.8 KB) [[attachment:CreatePdfDocument2_4_0.py]]
  • [get | view] (2010-01-31 14:10:03, 84.6 KB) [[attachment:CreatePdfDocument2_4_1.py]]
  • [get | view] (2010-09-18 16:23:23, 85.6 KB) [[attachment:CreatePdfDocument2_4_2.py]]
  • [get | view] (2006-06-16 20:56:53, 4.9 KB) [[attachment:FlashManager.py-1.5.3-1]]
  • [get | view] (2003-12-07 18:15:53, 3.9 KB) [[attachment:HTML2MoinMoin.py]]
  • [get | view] (2005-10-16 08:24:35, 4.9 KB) [[attachment:HelpOn-1.3.5-4.py]]
  • [get | view] (2006-02-03 19:21:04, 4.9 KB) [[attachment:HelpOn-1.5.1-5.py]]
  • [get | view] (2006-07-04 10:45:22, 4.8 KB) [[attachment:HelpOn-1.5.4-6.py]]
  • [get | view] (2006-07-04 22:39:14, 4.8 KB) [[attachment:HelpOn-1.6.0-7.py]]
  • [get | view] (2006-07-06 13:50:17, 4.0 KB) [[attachment:HelpOn-1.6.0-8.py]]
  • [get | view] (2008-01-10 17:43:24, 4.8 KB) [[attachment:HelpOn-1.6.0-9.py]]
  • [get | view] (2008-08-19 14:44:54, 5.0 KB) [[attachment:HelpOn-1.7.1-10.py]]
  • [get | view] (2005-02-20 18:28:34, 10.8 KB) [[attachment:IRSS.py]]
  • [get | view] (2005-03-09 22:46:23, 2.9 KB) [[attachment:ImportHtml-1.2.py]]
  • [get | view] (2003-12-07 18:15:53, 2.8 KB) [[attachment:ImportHtml.py]]
  • [get | view] (2003-12-07 18:15:53, 1.8 KB) [[attachment:IrcChat.py]]
  • [get | view] (2008-06-09 11:27:20, 4.4 KB) [[attachment:MoinCrypt.py]]
  • [get | view] (2010-11-29 12:08:27, 7.5 KB) [[attachment:PageActions.py]]
  • [get | view] (2006-08-07 15:12:19, 0.5 KB) [[attachment:PermanentLink.py]]
  • [get | view] (2003-12-07 18:15:53, 6.3 KB) [[attachment:PhoneDial.py]]
  • [get | view] (2005-04-17 14:21:47, 3.6 KB) [[attachment:RecommendPage-1.3.4-1.py]]
  • [get | view] (2005-04-19 18:21:52, 5.5 KB) [[attachment:RecommendPage-1.3.4-2.py]]
  • [get | view] (2005-05-02 19:53:09, 5.6 KB) [[attachment:RecommendPage-1.3.4-3.py]]
  • [get | view] (2005-09-03 07:33:35, 6.3 KB) [[attachment:RecommendPage-1.3.4-4.py]]
  • [get | view] (2005-09-05 17:44:03, 6.9 KB) [[attachment:RecommendPage-1.3.5-5.py]]
  • [get | view] (2005-09-07 16:42:26, 7.5 KB) [[attachment:RecommendPage-1.3.5-6.py]]
  • [get | view] (2005-09-08 16:06:28, 7.7 KB) [[attachment:RecommendPage-1.3.5-7.py]]
  • [get | view] (2005-11-01 11:31:51, 9.0 KB) [[attachment:RecommendPage-1.3.5-8.py]]
  • [get | view] (2006-02-03 19:40:51, 8.3 KB) [[attachment:RecommendPage-1.5.1-9.py]]
  • [get | view] (2008-01-11 09:14:35, 6.8 KB) [[attachment:RecommendPage-1.6.0-10.py]]
  • [get | view] (2008-08-19 14:44:59, 6.9 KB) [[attachment:RecommendPage-1.7.1-11.py]]
  • [get | view] (2008-06-09 11:27:40, 1.7 KB) [[attachment:ShowActions.py]]
  • [get | view] (2008-06-09 10:34:02, 5.3 KB) [[attachment:ShowDecrypted.py]]
  • [get | view] (2005-03-30 21:17:28, 7.7 KB) [[attachment:Slideshow.py]]
  • [get | view] (2004-02-02 20:48:31, 2.0 KB) [[attachment:SubscribeUser.py]]
  • [get | view] (2007-01-26 17:08:30, 2.2 KB) [[attachment:Subscribers-1.6.0.py]]
  • [get | view] (2003-12-07 18:15:53, 1.8 KB) [[attachment:Subscribers.py]]
  • [get | view] (2006-03-18 23:16:51, 0.8 KB) [[attachment:UserPreferences.py]]
  • [get | view] (2004-01-05 09:56:25, 8.1 KB) [[attachment:VisualSiteMap.py]]
  • [get | view] (2015-08-30 21:04:23, 11.1 KB) [[attachment:VisualSiteMap_1.10.py]]
  • [get | view] (2004-10-08 10:59:16, 9.3 KB) [[attachment:VisualSiteMap_1.2.py]]
  • [get | view] (2005-03-16 01:30:09, 9.8 KB) [[attachment:VisualSiteMap_1.3.py]]
  • [get | view] (2014-08-19 01:34:10, 10.8 KB) [[attachment:VisualSiteMap_1.9.py]]
  • [get | view] (2007-08-18 18:52:55, 1.0 KB) [[attachment:backlink.py]]
  • [get | view] (2007-03-15 05:53:49, 23.5 KB) [[attachment:findandreplace0.1Beta.py]]
  • [get | view] (2005-03-27 20:32:10, 3.6 KB) [[attachment:gallery2image-1.3.3-1.py]]
  • [get | view] (2005-08-03 20:14:56, 4.0 KB) [[attachment:gallery2image-1.3.3-2.py]]
  • [get | view] (2005-11-13 18:10:26, 20.7 KB) [[attachment:gallery2image-1.3.5-10.py]]
  • [get | view] (2005-11-25 22:03:50, 20.8 KB) [[attachment:gallery2image-1.3.5-11.py]]
  • [get | view] (2005-08-08 17:23:43, 8.4 KB) [[attachment:gallery2image-1.3.5-4.py]]
  • [get | view] (2005-08-13 15:15:45, 13.7 KB) [[attachment:gallery2image-1.3.5-5.py]]
  • [get | view] (2005-08-31 22:05:22, 15.5 KB) [[attachment:gallery2image-1.3.5-6.py]]
  • [get | view] (2005-10-29 20:23:50, 15.9 KB) [[attachment:gallery2image-1.3.5-8.py]]
  • [get | view] (2005-11-01 11:31:24, 17.6 KB) [[attachment:gallery2image-1.3.5-9.py]]
  • [get | view] (2006-01-27 20:52:32, 20.9 KB) [[attachment:gallery2image-1.5.1-12.py]]
  • [get | view] (2006-08-06 09:01:01, 22.1 KB) [[attachment:gallery2image-1.5.4-13.py]]
  • [get | view] (2006-08-11 18:21:40, 22.2 KB) [[attachment:gallery2image-1.5.4-14.py]]
  • [get | view] (2006-11-16 20:23:27, 22.6 KB) [[attachment:gallery2image-1.5.6-16.py]]
  • [get | view] (2006-08-11 18:30:22, 22.2 KB) [[attachment:gallery2image-1.6.0-15.py]]
  • [get | view] (2008-02-06 10:13:45, 22.3 KB) [[attachment:gallery2image-1.6.0-16.py]]
  • [get | view] (2008-05-20 15:51:09, 22.4 KB) [[attachment:gallery2image-1.6.3-17.py]]
  • [get | view] (2006-09-06 06:19:48, 1.3 KB) [[attachment:getmmap.py]]
  • [get | view] (2004-07-18 09:48:00, 1.5 KB) [[attachment:localnames.py]]
  • [get | view] (2005-03-25 15:02:31, 2.6 KB) [[attachment:newpageonly.py]]
  • [get | view] (2005-03-30 09:02:00, 3.5 KB) [[attachment:newpageonly_20050330.py]]
  • [get | view] (2006-06-06 19:12:27, 9.7 KB) [[attachment:pdf.py]]
  • [get | view] (2006-08-30 10:51:51, 36.0 KB) [[attachment:pdf2_0_0.py]]
  • [get | view] (2006-08-30 13:57:36, 36.5 KB) [[attachment:pdf2_0_1.py]]
  • [get | view] (2006-02-04 04:25:29, 1.0 KB) [[attachment:sisterindex.py]]
  • [get | view] (2004-10-28 07:33:10, 0.7 KB) [[attachment:xml.py]]
 All files | Selected Files: delete move to page copy to page

You are not allowed to attach a file to this page.