Back to index

moin  1.9.0~rc2
Classes | Functions | Variables
MoinMoin.action.AttachFile Namespace Reference

Classes

class  AttachmentAlreadyExists
 External interface - these are called from the core code. More...
class  ContainerItem

Functions

def getBasePath
def getAttachDir
def absoluteName
def get_action
def getAttachUrl
def getIndicator
def getFilename
def exists
def size
def info
def _write_stream
def add_attachment
def remove_attachment
def _addLogEntry
 Internal helpers.
def _access_file
def _build_filelist
def _get_files
def _get_filelist
def error_msg
def send_link_rel
 Create parts of the Web interface.
def send_uploadform
def execute
 Web interface for file upload, viewing and deletion.
def _do_upload_form
def upload_form
def _do_upload
def _do_del
def move_file
def _do_attachment_move
def _do_move
def _do_box
def _do_get
def _do_install
def _do_unzip
def send_viewfile
def _do_view
def do_admin_browser
 File attachment administration.

Variables

tuple logging = log.getLogger(__name__)
tuple action_name = __name__.split('.')

Class Documentation

class MoinMoin::action::AttachFile::AttachmentAlreadyExists

External interface - these are called from the core code.

Definition at line 55 of file AttachFile.py.


Function Documentation

def MoinMoin.action.AttachFile._access_file (   pagename,
  request 
) [private]
Check form parameter `target` and return a tuple of
    `(pagename, filename, filepath)` for an existing attachment.

    Return `(pagename, None, None)` if an error occurs.

Definition at line 278 of file AttachFile.py.

00278 
00279 def _access_file(pagename, request):
00280     """ Check form parameter `target` and return a tuple of
00281         `(pagename, filename, filepath)` for an existing attachment.
00282 
00283         Return `(pagename, None, None)` if an error occurs.
00284     """
00285     _ = request.getText
00286 
00287     error = None
00288     if not request.values.get('target'):
00289         error = _("Filename of attachment not specified!")
00290     else:
00291         filename = wikiutil.taintfilename(request.values['target'])
00292         fpath = getFilename(request, pagename, filename)
00293 
00294         if os.path.isfile(fpath):
00295             return (pagename, filename, fpath)
00296         error = _("Attachment '%(filename)s' does not exist!") % {'filename': filename}
00297 
00298     error_msg(pagename, request, error)
00299     return (pagename, None, None)
00300 

Here is the call graph for this function:

Here is the caller graph for this function:

def MoinMoin.action.AttachFile._addLogEntry (   request,
  action,
  pagename,
  filename 
) [private]

Internal helpers.

Add an entry to the edit log on uploads and deletes.

    `action` should be "ATTNEW" or "ATTDEL"

Definition at line 260 of file AttachFile.py.

00260 
00261 def _addLogEntry(request, action, pagename, filename):
00262     """ Add an entry to the edit log on uploads and deletes.
00263 
00264         `action` should be "ATTNEW" or "ATTDEL"
00265     """
00266     from MoinMoin.logfile import editlog
00267     t = wikiutil.timestamp2version(time.time())
00268     fname = wikiutil.url_quote(filename)
00269 
00270     # Write to global log
00271     log = editlog.EditLog(request)
00272     log.add(request, t, 99999999, action, pagename, request.remote_addr, fname)
00273 
00274     # Write to local log
00275     log = editlog.EditLog(request, rootpagename=pagename)
00276     log.add(request, t, 99999999, action, pagename, request.remote_addr, fname)
00277 

Here is the caller graph for this function:

def MoinMoin.action.AttachFile._build_filelist (   request,
  pagename,
  showheader,
  readonly,
  mime_type = '*' 
) [private]

Definition at line 301 of file AttachFile.py.

00301 
00302 def _build_filelist(request, pagename, showheader, readonly, mime_type='*'):
00303     _ = request.getText
00304     fmt = request.html_formatter
00305 
00306     # access directory
00307     attach_dir = getAttachDir(request, pagename)
00308     files = _get_files(request, pagename)
00309 
00310     if mime_type != '*':
00311         files = [fname for fname in files if mime_type == mimetypes.guess_type(fname)[0]]
00312 
00313     html = []
00314     if files:
00315         if showheader:
00316             html.append(fmt.rawHTML(_(
00317                 "To refer to attachments on a page, use '''{{{attachment:filename}}}''', \n"
00318                 "as shown below in the list of files. \n"
00319                 "Do '''NOT''' use the URL of the {{{[get]}}} link, \n"
00320                 "since this is subject to change and can break easily.",
00321                 wiki=True
00322             )))
00323 
00324         label_del = _("del")
00325         label_move = _("move")
00326         label_get = _("get")
00327         label_edit = _("edit")
00328         label_view = _("view")
00329         label_unzip = _("unzip")
00330         label_install = _("install")
00331 
00332         may_read = request.user.may.read(pagename)
00333         may_write = request.user.may.write(pagename)
00334         may_delete = request.user.may.delete(pagename)
00335 
00336         html.append(fmt.bullet_list(1))
00337         for file in files:
00338             mt = wikiutil.MimeType(filename=file)
00339             fullpath = os.path.join(attach_dir, file).encode(config.charset)
00340             st = os.stat(fullpath)
00341             base, ext = os.path.splitext(file)
00342             parmdict = {'file': wikiutil.escape(file),
00343                         'fsize': "%.1f" % (float(st.st_size) / 1024),
00344                         'fmtime': request.user.getFormattedDateTime(st.st_mtime),
00345                        }
00346 
00347             links = []
00348             if may_delete and not readonly:
00349                 links.append(fmt.url(1, getAttachUrl(pagename, file, request, do='del')) +
00350                              fmt.text(label_del) +
00351                              fmt.url(0))
00352 
00353             if may_delete and not readonly:
00354                 links.append(fmt.url(1, getAttachUrl(pagename, file, request, do='move')) +
00355                              fmt.text(label_move) +
00356                              fmt.url(0))
00357 
00358             links.append(fmt.url(1, getAttachUrl(pagename, file, request)) +
00359                          fmt.text(label_get) +
00360                          fmt.url(0))
00361 
00362             links.append(fmt.url(1, getAttachUrl(pagename, file, request, do='view')) +
00363                          fmt.text(label_view) +
00364                          fmt.url(0))
00365 
00366             if may_write and not readonly:
00367                 edit_url = getAttachUrl(pagename, file, request, do='modify')
00368                 if edit_url:
00369                     links.append(fmt.url(1, edit_url) +
00370                                  fmt.text(label_edit) +
00371                                  fmt.url(0))
00372 
00373             try:
00374                 is_zipfile = zipfile.is_zipfile(fullpath)
00375                 if is_zipfile:
00376                     is_package = packages.ZipPackage(request, fullpath).isPackage()
00377                     if is_package and request.user.isSuperUser():
00378                         links.append(fmt.url(1, getAttachUrl(pagename, file, request, do='install')) +
00379                                      fmt.text(label_install) +
00380                                      fmt.url(0))
00381                     elif (not is_package and mt.minor == 'zip' and
00382                           may_read and may_write and may_delete):
00383                         links.append(fmt.url(1, getAttachUrl(pagename, file, request, do='unzip')) +
00384                                      fmt.text(label_unzip) +
00385                                      fmt.url(0))
00386             except RuntimeError:
00387                 # We don't want to crash with a traceback here (an exception
00388                 # here could be caused by an uploaded defective zip file - and
00389                 # if we crash here, the user does not get a UI to remove the
00390                 # defective zip file again).
00391                 # RuntimeError is raised by zipfile stdlib module in case of
00392                 # problems (like inconsistent slash and backslash usage in the
00393                 # archive).
00394                 logging.exception("An exception within zip file attachment handling occurred:")
00395 
00396             html.append(fmt.listitem(1))
00397             html.append("[%s]" % " | ".join(links))
00398             html.append(" (%(fmtime)s, %(fsize)s KB) [[attachment:%(file)s]]" % parmdict)
00399             html.append(fmt.listitem(0))
00400         html.append(fmt.bullet_list(0))
00401 
00402     else:
00403         if showheader:
00404             html.append(fmt.paragraph(1))
00405             html.append(fmt.text(_("No attachments stored for %(pagename)s") % {
00406                                    'pagename': pagename}))
00407             html.append(fmt.paragraph(0))
00408 
00409     return ''.join(html)
00410 

Here is the call graph for this function:

Here is the caller graph for this function:

def MoinMoin.action.AttachFile._do_attachment_move (   pagename,
  request 
) [private]

Definition at line 684 of file AttachFile.py.

00684 
00685 def _do_attachment_move(pagename, request):
00686     _ = request.getText
00687 
00688     if 'cancel' in request.form:
00689         return _('Move aborted!')
00690     if not wikiutil.checkTicket(request, request.form['ticket']):
00691         return _('Please use the interactive user interface to move attachments!')
00692     if not request.user.may.delete(pagename):
00693         return _('You are not allowed to move attachments from this page.')
00694 
00695     if 'newpagename' in request.form:
00696         new_pagename = request.form.get('newpagename')
00697     else:
00698         upload_form(pagename, request, msg=_("Move aborted because new page name is empty."))
00699     if 'newattachmentname' in request.form:
00700         new_attachment = request.form.get('newattachmentname')
00701         if new_attachment != wikiutil.taintfilename(new_attachment):
00702             upload_form(pagename, request, msg=_("Please use a valid filename for attachment '%(filename)s'.") % {
00703                                   'filename': new_attachment})
00704             return
00705     else:
00706         upload_form(pagename, request, msg=_("Move aborted because new attachment name is empty."))
00707 
00708     attachment = request.form.get('oldattachmentname')
00709     move_file(request, pagename, new_pagename, attachment, new_attachment)
00710 

Here is the call graph for this function:

def MoinMoin.action.AttachFile._do_box (   pagename,
  request 
) [private]

Definition at line 765 of file AttachFile.py.

00765 
00766 def _do_box(pagename, request):
00767     _ = request.getText
00768 
00769     pagename, filename, fpath = _access_file(pagename, request)
00770     if not request.user.may.read(pagename):
00771         return _('You are not allowed to get attachments from this page.')
00772     if not filename:
00773         return # error msg already sent in _access_file
00774 
00775     timestamp = datetime.datetime.fromtimestamp(os.path.getmtime(fpath))
00776     if_modified = request.if_modified_since
00777     if if_modified and if_modified >= timestamp:
00778         request.status_code = 304
00779     else:
00780         ci = ContainerItem(request, pagename, filename)
00781         filename = wikiutil.taintfilename(request.values['member'])
00782         mt = wikiutil.MimeType(filename=filename)
00783         content_type = mt.content_type()
00784         mime_type = mt.mime_type()
00785 
00786         # TODO: fix the encoding here, plain 8 bit is not allowed according to the RFCs
00787         # There is no solution that is compatible to IE except stripping non-ascii chars
00788         filename_enc = filename.encode(config.charset)
00789 
00790         # for dangerous files (like .html), when we are in danger of cross-site-scripting attacks,
00791         # we just let the user store them to disk ('attachment').
00792         # For safe files, we directly show them inline (this also works better for IE).
00793         dangerous = mime_type in request.cfg.mimetypes_xss_protect
00794         content_dispo = dangerous and 'attachment' or 'inline'
00795 
00796         now = time.time()
00797         request.headers.add('Date', http_date(now))
00798         request.headers.add('Content-Type', content_type)
00799         request.headers.add('Last-Modified', http_date(timestamp))
00800         request.headers.add('Expires', http_date(now - 365 * 24 * 3600))
00801         #request.headers.add('Content-Length', os.path.getsize(fpath))
00802         content_dispo_string = '%s; filename="%s"' % (content_dispo, filename_enc)
00803         request.headers.add('Content-Disposition', content_dispo_string)
00804 
00805         # send data
00806         request.send_file(ci.get(filename))
00807 

Here is the call graph for this function:

def MoinMoin.action.AttachFile._do_del (   pagename,
  request 
) [private]

Definition at line 635 of file AttachFile.py.

00635 
00636 def _do_del(pagename, request):
00637     _ = request.getText
00638 
00639     pagename, filename, fpath = _access_file(pagename, request)
00640     if not request.user.may.delete(pagename):
00641         return _('You are not allowed to delete attachments on this page.')
00642     if not filename:
00643         return # error msg already sent in _access_file
00644 
00645     remove_attachment(request, pagename, filename)
00646 
00647     upload_form(pagename, request, msg=_("Attachment '%(filename)s' deleted.") % {'filename': filename})
00648 

Here is the call graph for this function:

def MoinMoin.action.AttachFile._do_get (   pagename,
  request 
) [private]

Definition at line 808 of file AttachFile.py.

00808 
00809 def _do_get(pagename, request):
00810     _ = request.getText
00811 
00812     pagename, filename, fpath = _access_file(pagename, request)
00813     if not request.user.may.read(pagename):
00814         return _('You are not allowed to get attachments from this page.')
00815     if not filename:
00816         return # error msg already sent in _access_file
00817 
00818     timestamp = datetime.datetime.fromtimestamp(os.path.getmtime(fpath))
00819     if_modified = request.if_modified_since
00820     if if_modified and if_modified >= timestamp:
00821         request.status_code = 304
00822     else:
00823         mt = wikiutil.MimeType(filename=filename)
00824         content_type = mt.content_type()
00825         mime_type = mt.mime_type()
00826 
00827         # TODO: fix the encoding here, plain 8 bit is not allowed according to the RFCs
00828         # There is no solution that is compatible to IE except stripping non-ascii chars
00829         filename_enc = filename.encode(config.charset)
00830 
00831         # for dangerous files (like .html), when we are in danger of cross-site-scripting attacks,
00832         # we just let the user store them to disk ('attachment').
00833         # For safe files, we directly show them inline (this also works better for IE).
00834         dangerous = mime_type in request.cfg.mimetypes_xss_protect
00835         content_dispo = dangerous and 'attachment' or 'inline'
00836 
00837         now = time.time()
00838         request.headers.add('Date', http_date(now))
00839         request.headers.add('Content-Type', content_type)
00840         request.headers.add('Last-Modified', http_date(timestamp))
00841         request.headers.add('Expires', http_date(now - 365 * 24 * 3600))
00842         request.headers.add('Content-Length', os.path.getsize(fpath))
00843         content_dispo_string = '%s; filename="%s"' % (content_dispo, filename_enc)
00844         request.headers.add('Content-Disposition', content_dispo_string)
00845 
00846         # send data
00847         request.send_file(open(fpath, 'rb'))
00848 

Here is the call graph for this function:

def MoinMoin.action.AttachFile._do_install (   pagename,
  request 
) [private]

Definition at line 849 of file AttachFile.py.

00849 
00850 def _do_install(pagename, request):
00851     _ = request.getText
00852 
00853     pagename, target, targetpath = _access_file(pagename, request)
00854     if not request.user.isSuperUser():
00855         return _('You are not allowed to install files.')
00856     if not target:
00857         return
00858 
00859     package = packages.ZipPackage(request, targetpath)
00860 
00861     if package.isPackage():
00862         if package.installPackage():
00863             msg = _("Attachment '%(filename)s' installed.") % {'filename': target}
00864         else:
00865             msg = _("Installation of '%(filename)s' failed.") % {'filename': target}
00866         if package.msg:
00867             msg += " " + package.msg
00868     else:
00869         msg = _('The file %s is not a MoinMoin package file.') % target
00870 
00871     upload_form(pagename, request, msg=msg)
00872 

Here is the call graph for this function:

def MoinMoin.action.AttachFile._do_move (   pagename,
  request 
) [private]

Definition at line 711 of file AttachFile.py.

00711 
00712 def _do_move(pagename, request):
00713     _ = request.getText
00714 
00715     pagename, filename, fpath = _access_file(pagename, request)
00716     if not request.user.may.delete(pagename):
00717         return _('You are not allowed to move attachments from this page.')
00718     if not filename:
00719         return # error msg already sent in _access_file
00720 
00721     # move file
00722     d = {'action': action_name,
00723          'url': request.href(pagename),
00724          'do': 'attachment_move',
00725          'ticket': wikiutil.createTicket(request),
00726          'pagename': wikiutil.escape(pagename, 1),
00727          'attachment_name': wikiutil.escape(filename, 1),
00728          'move': _('Move'),
00729          'cancel': _('Cancel'),
00730          'newname_label': _("New page name"),
00731          'attachment_label': _("New attachment name"),
00732         }
00733     formhtml = '''
00734 <form action="%(url)s" method="POST">
00735 <input type="hidden" name="action" value="%(action)s">
00736 <input type="hidden" name="do" value="%(do)s">
00737 <input type="hidden" name="ticket" value="%(ticket)s">
00738 <table>
00739     <tr>
00740         <td class="label"><label>%(newname_label)s</label></td>
00741         <td class="content">
00742             <input type="text" name="newpagename" value="%(pagename)s" size="80">
00743         </td>
00744     </tr>
00745     <tr>
00746         <td class="label"><label>%(attachment_label)s</label></td>
00747         <td class="content">
00748             <input type="text" name="newattachmentname" value="%(attachment_name)s" size="80">
00749         </td>
00750     </tr>
00751     <tr>
00752         <td></td>
00753         <td class="buttons">
00754             <input type="hidden" name="oldattachmentname" value="%(attachment_name)s">
00755             <input type="submit" name="move" value="%(move)s">
00756             <input type="submit" name="cancel" value="%(cancel)s">
00757         </td>
00758     </tr>
00759 </table>
00760 </form>''' % d
00761     thispage = Page(request, pagename)
00762     request.theme.add_msg(formhtml, "dialog")
00763     return thispage.send_page()
00764 

Here is the call graph for this function:

def MoinMoin.action.AttachFile._do_unzip (   pagename,
  request,
  overwrite = False 
) [private]

Definition at line 873 of file AttachFile.py.

00873 
00874 def _do_unzip(pagename, request, overwrite=False):
00875     _ = request.getText
00876     pagename, filename, fpath = _access_file(pagename, request)
00877 
00878     if not (request.user.may.delete(pagename) and request.user.may.read(pagename) and request.user.may.write(pagename)):
00879         return _('You are not allowed to unzip attachments of this page.')
00880 
00881     if not filename:
00882         return # error msg already sent in _access_file
00883 
00884     try:
00885         if not zipfile.is_zipfile(fpath):
00886             return _('The file %(filename)s is not a .zip file.') % {'filename': filename}
00887 
00888         # determine how which attachment names we have and how much space each is occupying
00889         curr_fsizes = dict([(f, size(request, pagename, f)) for f in _get_files(request, pagename)])
00890 
00891         # Checks for the existance of one common prefix path shared among
00892         # all files in the zip file. If this is the case, remove the common prefix.
00893         # We also prepare a dict of the new filenames->filesizes.
00894         zip_path_sep = '/'  # we assume '/' is as zip standard suggests
00895         fname_index = None
00896         mapping = []
00897         new_fsizes = {}
00898         zf = zipfile.ZipFile(fpath)
00899         for zi in zf.infolist():
00900             name = zi.filename
00901             if not name.endswith(zip_path_sep):  # a file (not a directory)
00902                 if fname_index is None:
00903                     fname_index = name.rfind(zip_path_sep) + 1
00904                     path = name[:fname_index]
00905                 if (name.rfind(zip_path_sep) + 1 != fname_index  # different prefix len
00906                     or
00907                     name[:fname_index] != path): # same len, but still different
00908                     mapping = []  # zip is not acceptable
00909                     break
00910                 if zi.file_size >= request.cfg.unzip_single_file_size:  # file too big
00911                     mapping = []  # zip is not acceptable
00912                     break
00913                 finalname = name[fname_index:]  # remove common path prefix
00914                 finalname = finalname.decode(config.charset, 'replace')  # replaces trash with \uFFFD char
00915                 mapping.append((name, finalname))
00916                 new_fsizes[finalname] = zi.file_size
00917 
00918         # now we either have an empty mapping (if the zip is not acceptable),
00919         # an identity mapping (no subdirs in zip, just all flat), or
00920         # a mapping (origname, finalname) where origname is the zip member filename
00921         # (including some prefix path) and finalname is a simple filename.
00922 
00923         # calculate resulting total file size / count after unzipping:
00924         if overwrite:
00925             curr_fsizes.update(new_fsizes)
00926             total = curr_fsizes
00927         else:
00928             new_fsizes.update(curr_fsizes)
00929             total = new_fsizes
00930         total_count = len(total)
00931         total_size = sum(total.values())
00932 
00933         if not mapping:
00934             msg = _("Attachment '%(filename)s' not unzipped because some files in the zip "
00935                     "are either not in the same directory or exceeded the single file size limit (%(maxsize_file)d kB)."
00936                    ) % {'filename': filename,
00937                         'maxsize_file': request.cfg.unzip_single_file_size / 1000, }
00938         elif total_size > request.cfg.unzip_attachments_space:
00939             msg = _("Attachment '%(filename)s' not unzipped because it would have exceeded "
00940                     "the per page attachment storage size limit (%(size)d kB).") % {
00941                         'filename': filename,
00942                         'size': request.cfg.unzip_attachments_space / 1000, }
00943         elif total_count > request.cfg.unzip_attachments_count:
00944             msg = _("Attachment '%(filename)s' not unzipped because it would have exceeded "
00945                     "the per page attachment count limit (%(count)d).") % {
00946                         'filename': filename,
00947                         'count': request.cfg.unzip_attachments_count, }
00948         else:
00949             not_overwritten = []
00950             for origname, finalname in mapping:
00951                 try:
00952                     # Note: reads complete zip member file into memory. ZipFile does not offer block-wise reading:
00953                     add_attachment(request, pagename, finalname, zf.read(origname), overwrite)
00954                 except AttachmentAlreadyExists:
00955                     not_overwritten.append(finalname)
00956             if not_overwritten:
00957                 msg = _("Attachment '%(filename)s' partially unzipped (did not overwrite: %(filelist)s).") % {
00958                         'filename': filename,
00959                         'filelist': ', '.join(not_overwritten), }
00960             else:
00961                 msg = _("Attachment '%(filename)s' unzipped.") % {'filename': filename}
00962     except RuntimeError, err:
00963         # We don't want to crash with a traceback here (an exception
00964         # here could be caused by an uploaded defective zip file - and
00965         # if we crash here, the user does not get a UI to remove the
00966         # defective zip file again).
00967         # RuntimeError is raised by zipfile stdlib module in case of
00968         # problems (like inconsistent slash and backslash usage in the
00969         # archive).
00970         logging.exception("An exception within zip file attachment handling occurred:")
00971         msg = _("A severe error occurred:") + ' ' + str(err)
00972 
00973     upload_form(pagename, request, msg=msg)
00974 

Here is the call graph for this function:

def MoinMoin.action.AttachFile._do_upload (   pagename,
  request 
) [private]

Definition at line 533 of file AttachFile.py.

00533 
00534 def _do_upload(pagename, request):
00535     _ = request.getText
00536     # Currently we only check TextCha for upload (this is what spammers ususally do),
00537     # but it could be extended to more/all attachment write access
00538     if not TextCha(request).check_answer_from_form():
00539         return _('TextCha: Wrong answer! Go back and try again...')
00540 
00541     form = request.form
00542 
00543     file_upload = request.files.get('file')
00544     if not file_upload:
00545         # This might happen when trying to upload file names
00546         # with non-ascii characters on Safari.
00547         return _("No file content. Delete non ASCII characters from the file name and try again.")
00548 
00549     try:
00550         overwrite = int(form.get('overwrite', '0'))
00551     except:
00552         overwrite = 0
00553 
00554     if not request.user.may.write(pagename):
00555         return _('You are not allowed to attach a file to this page.')
00556 
00557     if overwrite and not request.user.may.delete(pagename):
00558         return _('You are not allowed to overwrite a file attachment of this page.')
00559 
00560     target = form.get('target', u'').strip()
00561     if not target:
00562         target = file_upload.filename or u''
00563 
00564     target = wikiutil.clean_input(target)
00565 
00566     if not target:
00567         return _("Filename of attachment not specified!")
00568 
00569     # add the attachment
00570     try:
00571         target, bytes = add_attachment(request, pagename, target, file_upload.stream, overwrite=overwrite)
00572         msg = _("Attachment '%(target)s' (remote name '%(filename)s')"
00573                 " with %(bytes)d bytes saved.") % {
00574                 'target': target, 'filename': file_upload.filename, 'bytes': bytes}
00575     except AttachmentAlreadyExists:
00576         msg = _("Attachment '%(target)s' (remote name '%(filename)s') already exists.") % {
00577             'target': target, 'filename': file_upload.filename}
00578 
00579     # return attachment list
00580     upload_form(pagename, request, msg)
00581 

Here is the call graph for this function:

def MoinMoin.action.AttachFile._do_upload_form (   pagename,
  request 
) [private]

Definition at line 513 of file AttachFile.py.

00513 
00514 def _do_upload_form(pagename, request):
00515     upload_form(pagename, request)
00516 

Here is the call graph for this function:

def MoinMoin.action.AttachFile._do_view (   pagename,
  request 
) [private]

Definition at line 1071 of file AttachFile.py.

01071 
01072 def _do_view(pagename, request):
01073     _ = request.getText
01074 
01075     orig_pagename = pagename
01076     pagename, filename, fpath = _access_file(pagename, request)
01077     if not request.user.may.read(pagename):
01078         return _('You are not allowed to view attachments of this page.')
01079     if not filename:
01080         return
01081 
01082     request.formatter.page = Page(request, pagename)
01083 
01084     # send header & title
01085     # Use user interface language for this generated page
01086     request.setContentLanguage(request.lang)
01087     title = _('attachment:%(filename)s of %(pagename)s') % {
01088         'filename': filename, 'pagename': pagename}
01089     request.theme.send_title(title, pagename=pagename)
01090 
01091     # send body
01092     request.write(request.formatter.startContent())
01093     send_viewfile(orig_pagename, request)
01094     send_uploadform(pagename, request)
01095     request.write(request.formatter.endContent())
01096 
01097     request.theme.send_footer(pagename)
01098     request.theme.send_closing_html()
01099 

Here is the call graph for this function:

def MoinMoin.action.AttachFile._get_filelist (   request,
  pagename 
) [private]

Definition at line 421 of file AttachFile.py.

00421 
00422 def _get_filelist(request, pagename):
00423     return _build_filelist(request, pagename, 1, 0)
00424 

Here is the call graph for this function:

Here is the caller graph for this function:

def MoinMoin.action.AttachFile._get_files (   request,
  pagename 
) [private]

Definition at line 411 of file AttachFile.py.

00411 
00412 def _get_files(request, pagename):
00413     attach_dir = getAttachDir(request, pagename)
00414     if os.path.isdir(attach_dir):
00415         files = [fn.decode(config.charset) for fn in os.listdir(attach_dir)]
00416         files.sort()
00417     else:
00418         files = []
00419     return files
00420 

Here is the call graph for this function:

Here is the caller graph for this function:

def MoinMoin.action.AttachFile._write_stream (   content,
  stream,
  bufsize = 8192 
) [private]

Definition at line 185 of file AttachFile.py.

00185 
00186 def _write_stream(content, stream, bufsize=8192):
00187     if hasattr(content, 'read'): # looks file-like
00188         import shutil
00189         shutil.copyfileobj(content, stream, bufsize)
00190     elif isinstance(content, str):
00191         stream.write(content)
00192     else:
00193         logging.error("unsupported content object: %r" % content)
00194         raise

Here is the caller graph for this function:

def MoinMoin.action.AttachFile.absoluteName (   url,
  pagename 
)
Get (pagename, filename) of an attachment: link
    @param url: PageName/filename.ext or filename.ext (unicode)
    @param pagename: name of the currently processed page (unicode)
    @rtype: tuple of unicode
    @return: PageName, filename.ext

Definition at line 73 of file AttachFile.py.

00073 
00074 def absoluteName(url, pagename):
00075     """ Get (pagename, filename) of an attachment: link
00076         @param url: PageName/filename.ext or filename.ext (unicode)
00077         @param pagename: name of the currently processed page (unicode)
00078         @rtype: tuple of unicode
00079         @return: PageName, filename.ext
00080     """
00081     url = wikiutil.AbsPageName(pagename, url)
00082     pieces = url.split(u'/')
00083     if len(pieces) == 1:
00084         return pagename, pieces[0]
00085     else:
00086         return u"/".join(pieces[:-1]), pieces[-1]
00087 

def MoinMoin.action.AttachFile.add_attachment (   request,
  pagename,
  target,
  filecontent,
  overwrite = 0 
)
save <filecontent> to an attachment <target> of page <pagename>

    filecontent can be either a str (in memory file content),
    or an open file object (file content in e.g. a tempfile).

Definition at line 195 of file AttachFile.py.

00195 
00196 def add_attachment(request, pagename, target, filecontent, overwrite=0):
00197     """ save <filecontent> to an attachment <target> of page <pagename>
00198 
00199         filecontent can be either a str (in memory file content),
00200         or an open file object (file content in e.g. a tempfile).
00201     """
00202     # replace illegal chars
00203     target = wikiutil.taintfilename(target)
00204 
00205     # get directory, and possibly create it
00206     attach_dir = getAttachDir(request, pagename, create=1)
00207     fpath = os.path.join(attach_dir, target).encode(config.charset)
00208 
00209     exists = os.path.exists(fpath)
00210     if exists:
00211         if overwrite:
00212             remove_attachment(request, pagename, target)
00213         else:
00214             raise AttachmentAlreadyExists
00215 
00216     # save file
00217     stream = open(fpath, 'wb')
00218     try:
00219         _write_stream(filecontent, stream)
00220     finally:
00221         stream.close()
00222 
00223     _addLogEntry(request, 'ATTNEW', pagename, target)
00224 
00225     filesize = os.path.getsize(fpath)
00226     event = FileAttachedEvent(request, pagename, target, filesize)
00227     send_event(event)
00228 
00229     return target, filesize
00230 

Here is the call graph for this function:

Here is the caller graph for this function:

File attachment administration.

Browser for SystemAdmin macro. 

Definition at line 1104 of file AttachFile.py.

01104 
01105 def do_admin_browser(request):
01106     """ Browser for SystemAdmin macro. """
01107     from MoinMoin.util.dataset import TupleDataset, Column
01108     _ = request.getText
01109 
01110     data = TupleDataset()
01111     data.columns = [
01112         Column('page', label=('Page')),
01113         Column('file', label=('Filename')),
01114         Column('size', label=_('Size'), align='right'),
01115     ]
01116 
01117     # iterate over pages that might have attachments
01118     pages = request.rootpage.getPageList()
01119     for pagename in pages:
01120         # check for attachments directory
01121         page_dir = getAttachDir(request, pagename)
01122         if os.path.isdir(page_dir):
01123             # iterate over files of the page
01124             files = os.listdir(page_dir)
01125             for filename in files:
01126                 filepath = os.path.join(page_dir, filename)
01127                 data.addRow((
01128                     Page(request, pagename).link_to(request, querystr="action=AttachFile"),
01129                     wikiutil.escape(filename.decode(config.charset)),
01130                     os.path.getsize(filepath),
01131                 ))
01132 
01133     if data:
01134         from MoinMoin.widget.browser import DataBrowserWidget
01135 
01136         browser = DataBrowserWidget(request)
01137         browser.setData(data)
01138         return browser.render(method="GET")
01139 
01140     return ''
01141 

Here is the call graph for this function:

def MoinMoin.action.AttachFile.error_msg (   pagename,
  request,
  msg 
)

Definition at line 425 of file AttachFile.py.

00425 
00426 def error_msg(pagename, request, msg):
00427     msg = wikiutil.escape(msg)
00428     request.theme.add_msg(msg, "error")
00429     Page(request, pagename).send_page()
00430 

Here is the caller graph for this function:

def MoinMoin.action.AttachFile.execute (   pagename,
  request 
)

Web interface for file upload, viewing and deletion.

Main dispatcher for the 'AttachFile' action. 

Definition at line 499 of file AttachFile.py.

00499 
00500 def execute(pagename, request):
00501     """ Main dispatcher for the 'AttachFile' action. """
00502     _ = request.getText
00503 
00504     do = request.values.get('do', 'upload_form')
00505     handler = globals().get('_do_%s' % do)
00506     if handler:
00507         msg = handler(pagename, request)
00508     else:
00509         msg = _('Unsupported AttachFile sub-action: %s') % do
00510     if msg:
00511         error_msg(pagename, request, msg)
00512 

Here is the call graph for this function:

def MoinMoin.action.AttachFile.exists (   request,
  pagename,
  filename 
)
check if page <pagename> has a file <filename> attached 

Definition at line 156 of file AttachFile.py.

00156 
00157 def exists(request, pagename, filename):
00158     """ check if page <pagename> has a file <filename> attached """
00159     fpath = getFilename(request, pagename, filename)
00160     return os.path.exists(fpath)
00161 

Here is the call graph for this function:

def MoinMoin.action.AttachFile.get_action (   request,
  filename,
  do 
)

Definition at line 88 of file AttachFile.py.

00088 
00089 def get_action(request, filename, do):
00090     generic_do_mapping = {
00091         # do -> action
00092         'get': action_name,
00093         'view': action_name,
00094         'move': action_name,
00095         'del': action_name,
00096         'unzip': action_name,
00097         'install': action_name,
00098         'upload_form': action_name,
00099     }
00100     basename, ext = os.path.splitext(filename)
00101     do_mapping = request.cfg.extensions_mapping.get(ext, {})
00102     action = do_mapping.get(do, None)
00103     if action is None:
00104         # we have no special support for this,
00105         # look up whether we have generic support:
00106         action = generic_do_mapping.get(do, None)
00107     return action
00108 

Here is the caller graph for this function:

def MoinMoin.action.AttachFile.getAttachDir (   request,
  pagename,
  create = 0 
)
Get directory where attachments for page `pagename` are stored. 

Definition at line 64 of file AttachFile.py.

00064 
00065 def getAttachDir(request, pagename, create=0):
00066     """ Get directory where attachments for page `pagename` are stored. """
00067     if request.page and pagename == request.page.page_name:
00068         page = request.page # reusing existing page obj is faster
00069     else:
00070         page = Page(request, pagename)
00071     return page.getPagePath("attachments", check_create=create)
00072 

Here is the caller graph for this function:

def MoinMoin.action.AttachFile.getAttachUrl (   pagename,
  filename,
  request,
  addts = 0,
  do = 'get' 
)
Get URL that points to attachment `filename` of page `pagename`.
    For upload url, call with do='upload_form'.
    Returns the URL to do the specified "do" action or None,
    if this action is not supported.

Definition at line 109 of file AttachFile.py.

00109 
00110 def getAttachUrl(pagename, filename, request, addts=0, do='get'):
00111     """ Get URL that points to attachment `filename` of page `pagename`.
00112         For upload url, call with do='upload_form'.
00113         Returns the URL to do the specified "do" action or None,
00114         if this action is not supported.
00115     """
00116     action = get_action(request, filename, do)
00117     if action:
00118         url = request.href(pagename, action=action, do=do, target=filename)
00119         return url
00120 

Here is the call graph for this function:

Here is the caller graph for this function:

Get base path where page dirs for attachments are stored. 

Definition at line 59 of file AttachFile.py.

00059 
00060 def getBasePath(request):
00061     """ Get base path where page dirs for attachments are stored. """
00062     return request.rootpage.getPagePath('pages')
00063 

def MoinMoin.action.AttachFile.getFilename (   request,
  pagename,
  filename 
)
make complete pathfilename of file "name" attached to some page "pagename"
    @param request: request object
    @param pagename: name of page where the file is attached to (unicode)
    @param filename: filename of attached file (unicode)
    @rtype: string (in config.charset encoding)
    @return: complete path/filename of attached file

Definition at line 143 of file AttachFile.py.

00143 
00144 def getFilename(request, pagename, filename):
00145     """ make complete pathfilename of file "name" attached to some page "pagename"
00146         @param request: request object
00147         @param pagename: name of page where the file is attached to (unicode)
00148         @param filename: filename of attached file (unicode)
00149         @rtype: string (in config.charset encoding)
00150         @return: complete path/filename of attached file
00151     """
00152     if isinstance(filename, unicode):
00153         filename = filename.encode(config.charset)
00154     return os.path.join(getAttachDir(request, pagename, create=1), filename)
00155 

Here is the call graph for this function:

Here is the caller graph for this function:

def MoinMoin.action.AttachFile.getIndicator (   request,
  pagename 
)
Get an attachment indicator for a page (linked clip image) or
    an empty string if not attachments exist.

Definition at line 121 of file AttachFile.py.

00121 
00122 def getIndicator(request, pagename):
00123     """ Get an attachment indicator for a page (linked clip image) or
00124         an empty string if not attachments exist.
00125     """
00126     _ = request.getText
00127     attach_dir = getAttachDir(request, pagename)
00128     if not os.path.exists(attach_dir):
00129         return ''
00130 
00131     files = os.listdir(attach_dir)
00132     if not files:
00133         return ''
00134 
00135     fmt = request.formatter
00136     attach_count = _('[%d attachments]') % len(files)
00137     attach_icon = request.theme.make_icon('attach', vars={'attach_count': attach_count})
00138     attach_link = (fmt.url(1, request.href(pagename, action=action_name), rel='nofollow') +
00139                    attach_icon +
00140                    fmt.url(0))
00141     return attach_link
00142 

Here is the call graph for this function:

def MoinMoin.action.AttachFile.info (   pagename,
  request 
)
Generate snippet with info on the attachment for page `pagename`. 

Definition at line 168 of file AttachFile.py.

00168 
00169 def info(pagename, request):
00170     """ Generate snippet with info on the attachment for page `pagename`. """
00171     _ = request.getText
00172 
00173     attach_dir = getAttachDir(request, pagename)
00174     files = []
00175     if os.path.isdir(attach_dir):
00176         files = os.listdir(attach_dir)
00177     page = Page(request, pagename)
00178     link = page.url(request, {'action': action_name})
00179     attach_info = _('There are <a href="%(link)s">%(count)s attachment(s)</a> stored for this page.') % {
00180         'count': len(files),
00181         'link': wikiutil.escape(link)
00182         }
00183     return "\n<p>\n%s\n</p>\n" % attach_info
00184 

Here is the call graph for this function:

def MoinMoin.action.AttachFile.move_file (   request,
  pagename,
  new_pagename,
  attachment,
  new_attachment 
)

Definition at line 649 of file AttachFile.py.

00649 
00650 def move_file(request, pagename, new_pagename, attachment, new_attachment):
00651     _ = request.getText
00652 
00653     newpage = Page(request, new_pagename)
00654     if newpage.exists(includeDeleted=1) and request.user.may.write(new_pagename) and request.user.may.delete(pagename):
00655         new_attachment_path = os.path.join(getAttachDir(request, new_pagename,
00656                               create=1), new_attachment).encode(config.charset)
00657         attachment_path = os.path.join(getAttachDir(request, pagename),
00658                           attachment).encode(config.charset)
00659 
00660         if os.path.exists(new_attachment_path):
00661             upload_form(pagename, request,
00662                 msg=_("Attachment '%(new_pagename)s/%(new_filename)s' already exists.") % {
00663                     'new_pagename': new_pagename,
00664                     'new_filename': new_attachment})
00665             return
00666 
00667         if new_attachment_path != attachment_path:
00668             # move file
00669             filesys.rename(attachment_path, new_attachment_path)
00670             _addLogEntry(request, 'ATTDEL', pagename, attachment)
00671             _addLogEntry(request, 'ATTNEW', new_pagename, new_attachment)
00672             upload_form(pagename, request,
00673                         msg=_("Attachment '%(pagename)s/%(filename)s' moved to '%(new_pagename)s/%(new_filename)s'.") % {
00674                             'pagename': pagename,
00675                             'filename': attachment,
00676                             'new_pagename': new_pagename,
00677                             'new_filename': new_attachment})
00678         else:
00679             upload_form(pagename, request, msg=_("Nothing changed"))
00680     else:
00681         upload_form(pagename, request, msg=_("Page '%(new_pagename)s' does not exist or you don't have enough rights.") % {
00682             'new_pagename': new_pagename})
00683 

Here is the call graph for this function:

Here is the caller graph for this function:

def MoinMoin.action.AttachFile.remove_attachment (   request,
  pagename,
  target 
)
remove attachment <target> of page <pagename>

Definition at line 231 of file AttachFile.py.

00231 
00232 def remove_attachment(request, pagename, target):
00233     """ remove attachment <target> of page <pagename>
00234     """
00235     # replace illegal chars
00236     target = wikiutil.taintfilename(target)
00237 
00238     # get directory, do not create it
00239     attach_dir = getAttachDir(request, pagename, create=0)
00240     # remove file
00241     fpath = os.path.join(attach_dir, target).encode(config.charset)
00242     try:
00243         filesize = os.path.getsize(fpath)
00244         os.remove(fpath)
00245     except:
00246         # either it is gone already or we have no rights - not much we can do about it
00247         filesize = 0
00248     else:
00249         _addLogEntry(request, 'ATTDEL', pagename, target)
00250 
00251         event = FileRemovedEvent(request, pagename, target, filesize)
00252         send_event(event)
00253 
00254     return target, filesize
00255 

Here is the call graph for this function:

Here is the caller graph for this function:

def MoinMoin.action.AttachFile.send_link_rel (   request,
  pagename 
)

Create parts of the Web interface.

Definition at line 435 of file AttachFile.py.

00435 
00436 def send_link_rel(request, pagename):
00437     files = _get_files(request, pagename)
00438     for fname in files:
00439         url = getAttachUrl(pagename, fname, request, do='view')
00440         request.write(u'<link rel="Appendix" title="%s" href="%s">\n' % (
00441                       wikiutil.escape(fname, 1),
00442                       wikiutil.escape(url, 1)))

Here is the call graph for this function:

def MoinMoin.action.AttachFile.send_uploadform (   pagename,
  request 
)
Send the HTML code for the list of already stored attachments and
    the file upload form.

Definition at line 443 of file AttachFile.py.

00443 
00444 def send_uploadform(pagename, request):
00445     """ Send the HTML code for the list of already stored attachments and
00446         the file upload form.
00447     """
00448     _ = request.getText
00449 
00450     if not request.user.may.read(pagename):
00451         request.write('<p>%s</p>' % _('You are not allowed to view this page.'))
00452         return
00453 
00454     writeable = request.user.may.write(pagename)
00455 
00456     # First send out the upload new attachment form on top of everything else.
00457     # This avoids usability issues if you have to scroll down a lot to upload
00458     # a new file when the page already has lots of attachments:
00459     if writeable:
00460         request.write('<h2>' + _("New Attachment") + '</h2>')
00461         request.write("""
00462 <form action="%(url)s" method="POST" enctype="multipart/form-data">
00463 <dl>
00464 <dt>%(upload_label_file)s</dt>
00465 <dd><input type="file" name="file" size="50"></dd>
00466 <dt>%(upload_label_target)s</dt>
00467 <dd><input type="text" name="target" size="50" value="%(target)s"></dd>
00468 <dt>%(upload_label_overwrite)s</dt>
00469 <dd><input type="checkbox" name="overwrite" value="1" %(overwrite_checked)s></dd>
00470 </dl>
00471 %(textcha)s
00472 <p>
00473 <input type="hidden" name="action" value="%(action_name)s">
00474 <input type="hidden" name="do" value="upload">
00475 <input type="submit" value="%(upload_button)s">
00476 </p>
00477 </form>
00478 """ % {
00479     'url': request.href(pagename),
00480     'action_name': action_name,
00481     'upload_label_file': _('File to upload'),
00482     'upload_label_target': _('Rename to'),
00483     'target': wikiutil.escape(request.values.get('target', ''), 1),
00484     'upload_label_overwrite': _('Overwrite existing attachment of same name'),
00485     'overwrite_checked': ('', 'checked')[request.form.get('overwrite', '0') == '1'],
00486     'upload_button': _('Upload'),
00487     'textcha': TextCha(request).render(),
00488 })
00489 
00490     request.write('<h2>' + _("Attached Files") + '</h2>')
00491     request.write(_get_filelist(request, pagename))
00492 
00493     if not writeable:
00494         request.write('<p>%s</p>' % _('You are not allowed to attach a file to this page.'))

Here is the call graph for this function:

Here is the caller graph for this function:

def MoinMoin.action.AttachFile.send_viewfile (   pagename,
  request 
)

Definition at line 975 of file AttachFile.py.

00975 
00976 def send_viewfile(pagename, request):
00977     _ = request.getText
00978     fmt = request.html_formatter
00979 
00980     pagename, filename, fpath = _access_file(pagename, request)
00981     if not filename:
00982         return
00983 
00984     request.write('<h2>' + _("Attachment '%(filename)s'") % {'filename': filename} + '</h2>')
00985     # show a download link above the content
00986     label = _('Download')
00987     link = (fmt.url(1, getAttachUrl(pagename, filename, request, do='get'), css_class="download") +
00988             fmt.text(label) +
00989             fmt.url(0))
00990     request.write('%s<br><br>' % link)
00991 
00992     if filename.endswith('.tdraw') or filename.endswith('.adraw'):
00993         request.write(fmt.attachment_drawing(filename, ''))
00994         return
00995 
00996     mt = wikiutil.MimeType(filename=filename)
00997 
00998     # destinguishs if browser need a plugin in place
00999     if mt.major == 'image' and mt.minor in config.browser_supported_images:
01000         url = getAttachUrl(pagename, filename, request)
01001         request.write('<img src="%s" alt="%s">' % (
01002             wikiutil.escape(url, 1),
01003             wikiutil.escape(filename, 1)))
01004         return
01005     elif mt.major == 'text':
01006         ext = os.path.splitext(filename)[1]
01007         Parser = wikiutil.getParserForExtension(request.cfg, ext)
01008         if Parser is not None:
01009             try:
01010                 content = file(fpath, 'r').read()
01011                 content = wikiutil.decodeUnknownInput(content)
01012                 colorizer = Parser(content, request, filename=filename)
01013                 colorizer.format(request.formatter)
01014                 return
01015             except IOError:
01016                 pass
01017 
01018         request.write(request.formatter.preformatted(1))
01019         # If we have text but no colorizing parser we try to decode file contents.
01020         content = open(fpath, 'r').read()
01021         content = wikiutil.decodeUnknownInput(content)
01022         content = wikiutil.escape(content)
01023         request.write(request.formatter.text(content))
01024         request.write(request.formatter.preformatted(0))
01025         return
01026 
01027     try:
01028         package = packages.ZipPackage(request, fpath)
01029         if package.isPackage():
01030             request.write("<pre><b>%s</b>\n%s</pre>" % (_("Package script:"), wikiutil.escape(package.getScript())))
01031             return
01032 
01033         if zipfile.is_zipfile(fpath) and mt.minor == 'zip':
01034             zf = zipfile.ZipFile(fpath, mode='r')
01035             request.write("<pre>%-46s %19s %12s\n" % (_("File Name"), _("Modified")+" "*5, _("Size")))
01036             for zinfo in zf.filelist:
01037                 date = "%d-%02d-%02d %02d:%02d:%02d" % zinfo.date_time
01038                 request.write(wikiutil.escape("%-46s %s %12d\n" % (zinfo.filename, date, zinfo.file_size)))
01039             request.write("</pre>")
01040             return
01041     except RuntimeError:
01042         # We don't want to crash with a traceback here (an exception
01043         # here could be caused by an uploaded defective zip file - and
01044         # if we crash here, the user does not get a UI to remove the
01045         # defective zip file again).
01046         # RuntimeError is raised by zipfile stdlib module in case of
01047         # problems (like inconsistent slash and backslash usage in the
01048         # archive).
01049         logging.exception("An exception within zip file attachment handling occurred:")
01050         return
01051 
01052     from MoinMoin import macro
01053     from MoinMoin.parser.text import Parser
01054 
01055     macro.request = request
01056     macro.formatter = request.html_formatter
01057     p = Parser("##\n", request)
01058     m = macro.Macro(p)
01059 
01060     # use EmbedObject to view valid mime types
01061     if mt is None:
01062         request.write('<p>' + _("Unknown file type, cannot display this attachment inline.") + '</p>')
01063         link = (fmt.url(1, getAttachUrl(pagename, filename, request)) +
01064                 fmt.text(filename) +
01065                 fmt.url(0))
01066         request.write('For using an external program follow this link %s' % link)
01067         return
01068     request.write(m.execute('EmbedObject', u'target="%s", pagename="%s"' % (filename, pagename)))
01069     return
01070 

Here is the call graph for this function:

Here is the caller graph for this function:

def MoinMoin.action.AttachFile.size (   request,
  pagename,
  filename 
)
return file size of file attachment 

Definition at line 162 of file AttachFile.py.

00162 
00163 def size(request, pagename, filename):
00164     """ return file size of file attachment """
00165     fpath = getFilename(request, pagename, filename)
00166     return os.path.getsize(fpath)
00167 

Here is the call graph for this function:

Here is the caller graph for this function:

def MoinMoin.action.AttachFile.upload_form (   pagename,
  request,
  msg = '' 
)

Definition at line 517 of file AttachFile.py.

00517 
00518 def upload_form(pagename, request, msg=''):
00519     if msg:
00520         msg = wikiutil.escape(msg)
00521     _ = request.getText
00522 
00523     # Use user interface language for this generated page
00524     request.setContentLanguage(request.lang)
00525     request.theme.add_msg(msg, "dialog")
00526     request.theme.send_title(_('Attachments for "%(pagename)s"') % {'pagename': pagename}, pagename=pagename)
00527     request.write('<div id="content">\n') # start content div
00528     send_uploadform(pagename, request)
00529     request.write('</div>\n') # end content div
00530     request.theme.send_footer(pagename)
00531     request.theme.send_closing_html()
00532 

Here is the call graph for this function:

Here is the caller graph for this function:


Variable Documentation

tuple MoinMoin.action.AttachFile.action_name = __name__.split('.')

Definition at line 49 of file AttachFile.py.

Definition at line 36 of file AttachFile.py.