Attachment 'StatusAttachments.py'

Download

   1 # -*- coding: utf-8 -*-
   2 """
   3 MoinMoin - Macro for Attachment Administration
   4 
   5 Discover unused (not linked) and missing (not yet uploaded) attachments
   6 
   7 @copyright: 2015-2018, David Linke, MoinMoin:DavidLinke, https://github.com/dalito
   8 @license: GNU GPL, see COPYING for details.
   9 """
  10 import os
  11 import sys
  12 
  13 from MoinMoin import config
  14 from MoinMoin import wikiutil
  15 from MoinMoin.PageEditor import PageEditor
  16 from MoinMoin.parser.text_moin_wiki import Parser
  17 from MoinMoin.action import AttachFile
  18 
  19 # Should only the superuser be allowed to use this macro ?
  20 SUPERUSER_ONLY = False
  21 
  22 
  23 def macro_StatusAttachments(macro):
  24     """
  25     Purpose: Discover unused (not linked) attachments and missing attachment
  26 
  27     This macro finds and reports
  28     (i)  attachments that are stored on pages put linked from nowhere
  29     (ii) attachment-links for which the attachment is not yet uploaded.
  30 
  31     The underlay directory is excluded.
  32     """
  33     _ = macro.request.getText
  34     request = macro.request
  35 
  36     # do not show to users not in superuser list
  37     if SUPERUSER_ONLY and not request.user.isSuperUser():
  38         error = _(u'Only superusers can use StatusAttachments macro.')
  39         # Send page with an error message
  40         request.theme.add_msg(error, 'info')
  41         return ''
  42 
  43     # do import from plugin "extensions" dir
  44     LinkParser = wikiutil.importWikiPlugin(
  45         request.cfg, 'parser', 'text_moin_wiki_links', 'LinkParser'
  46     )
  47 
  48     # create instance of parser for link extraction
  49     lparser = LinkParser()
  50 
  51     # collect info per page in lists
  52     stored_attachments = {}
  53     attachments_no_ext = {}
  54     links_to_existing_atts = {}
  55     links_to_missing_atts = {}
  56     stored_non_linked_atts = {}
  57 
  58     # get list of all pagenames excluding underlay
  59     pages = request.rootpage.getPageDict(user=request.user,
  60                                          include_underlay=False)
  61 
  62     for pagename in pages:
  63         page = PageEditor(request, pagename)
  64 
  65         # (i) get linked attachments by parsing raw text
  66         raw = page.get_raw_body()
  67         atts = lparser.find_links(raw, link_filter=lparser.is_attachment)
  68         for att in atts:
  69             link = att.split(':', 1)[1]
  70             if '/' in link:
  71                 storage_page, filename = link.rsplit('/', 1)
  72             else:
  73                 storage_page, filename = pagename, link
  74             filename = wikiutil.taintfilename(filename)
  75             if AttachFile.exists(request, storage_page, filename):
  76                 if storage_page in links_to_existing_atts:
  77                     links_to_existing_atts[storage_page].append(filename)
  78                 else:
  79                     links_to_existing_atts[storage_page] = [filename]
  80             else:
  81                 if storage_page in links_to_missing_atts:
  82                    if filename not in links_to_missing_atts[storage_page]:
  83                         links_to_missing_atts[storage_page].append(filename)
  84                 else:
  85                     links_to_missing_atts[storage_page] = [filename]
  86 
  87         # (ii) get stored attachments by looking at file system
  88         # & (iii) get stored attachments without any extension
  89         page_att_dir = AttachFile.getAttachDir(request, pagename)
  90         if os.path.isdir(page_att_dir):
  91             atts, atts_no_ext = [], []
  92             for fname in os.listdir(page_att_dir):
  93                 atts.append(fname.decode(config.charset))
  94                 if not os.path.splitext(fname)[1]:
  95                     atts_no_ext.append(fname.decode(config.charset))
  96             if atts:
  97                 stored_attachments[pagename] = atts
  98             if atts_no_ext:
  99                 attachments_no_ext[pagename] = atts_no_ext
 100 
 101     # when checking for availability of attachments we need to consider that
 102     # filenames are case insensitive on some platforms
 103     if sys.platform == 'win32':
 104         links_to_existing_atts_fs = {}
 105         for p in links_to_existing_atts:
 106             links_to_existing_atts_fs[p] = [os.path.normcase(att) for att in
 107                                             links_to_existing_atts[p]]
 108     else:
 109         links_to_existing_atts_fs = links_to_existing_atts
 110 
 111     # Find stored attachments that are not linked from anywhere
 112     for pagename in stored_attachments:
 113         for att in stored_attachments[pagename]:
 114             # look up in links_to_existing_atts if linked
 115             if (os.path.normcase(att)
 116                     in links_to_existing_atts_fs.get(pagename, [])):
 117                 continue
 118 
 119             # keep name of non-linked attachment
 120             if pagename in stored_non_linked_atts:
 121                 stored_non_linked_atts[pagename].append(att)
 122             else:
 123                 stored_non_linked_atts[pagename] = [att]
 124 
 125     # Created formatted output. We create wiki raw code that is then rendered.
 126     # Compare to using formatters this is much less verbose (but slower).
 127 
 128     # (i)  attachments that are stored on pages put linked from nowhere
 129     ret = []
 130     ret.append('<<TableOfContents>>')
 131     ret.append(u'== %s ==' %
 132                _(u'Attachments that are not linked from anywhere'))
 133 
 134     if not stored_non_linked_atts:
 135         ret.append(u" * None!")
 136     else:
 137         ret.append(u" * Found '''%i''' attachments that are not linked." %
 138                    len(reduce(list.__add__, stored_non_linked_atts.values())))
 139     for pagename in sorted(stored_non_linked_atts):
 140         ret.append(u" * '''[[%s||&action=AttachFile]]'''" % pagename)
 141         for att in sorted(stored_non_linked_atts[pagename]):
 142             ret.append(u"  * `%s`" % att)
 143 
 144     # (ii) attachment-links for which the attachment is not yet uploaded.
 145     ret.append(u'== %s ==' %
 146                _(u'Attachments that are linked but missing'))
 147     if not links_to_missing_atts:
 148         ret.append(u" * None!")
 149     else:
 150         ret.append(u" * Found '''%i''' links to missing attachments." %
 151                    len(reduce(list.__add__, links_to_missing_atts.values())))
 152     for pagename in sorted(links_to_missing_atts):
 153         ret.append(u" * '''[[%s]]'''" % pagename)
 154         for att in sorted(links_to_missing_atts[pagename]):
 155             ret.append(u"  * `%s`" % att)
 156 
 157     # (iii) List all stored attachments without any extension
 158     ret.append(u'== %s ==' %
 159                _(u'Attachments lacking a file extension'))
 160     if not attachments_no_ext:
 161         ret.append(u" * None!")
 162     else:
 163         ret.append(u" * Found '''%i''' attachments without extension." %
 164                    len(reduce(list.__add__, attachments_no_ext.values())))
 165     for pagename in sorted(attachments_no_ext):
 166         ret.append(u" * '''[[%s||&action=AttachFile]]'''" % pagename)
 167         for att in sorted(attachments_no_ext[pagename]):
 168             ret.append(u"  * `%s`" % att)
 169 
 170     return wikiutil.renderText(request, Parser, '\n'.join(ret))

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] (2018-03-14 23:41:38, 6.8 KB) [[attachment:StatusAttachments.py]]
 All files | Selected Files: delete move to page copy to page

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