Attachment 'AttachFile.py'

Download

   1 # -*- coding: iso-8859-1 -*-
   2 """
   3     MoinMoin - AttachFile action
   4 
   5     This action lets a page have multiple attachment files.
   6     It creates a folder <data>/pages/<pagename>/attachments
   7     and keeps everything in there.
   8 
   9     Form values: action=Attachment
  10     1. with no 'do' key: returns file upload form
  11     2. do=attach: accept file upload and saves the file in
  12        ../attachment/pagename/
  13     3. /pagename/fname?action=Attachment&do=get[&mimetype=type]:
  14        return contents of the attachment file with the name fname.
  15     4. /pathname/fname, do=view[&mimetype=type]:create a page
  16        to view the content of the file
  17 
  18     To insert an attachment into the page, use the "attachment:" pseudo
  19     schema.  
  20 
  21     @copyright: 2001 by Ken Sugino (sugino@mediaone.net)
  22     @copyright: 2001-2004 by Jürgen Hermann <jh@web.de>
  23     @license: GNU GPL, see COPYING for details.
  24     2005 April R.Bauer zipfile code added
  25 """
  26 
  27 import os, mimetypes, time, urllib
  28 from MoinMoin import config, user, util, wikiutil
  29 from MoinMoin.Page import Page
  30 from MoinMoin.util import MoinMoinNoFooter, filesys, web
  31 import zipfile,string
  32 
  33 action_name = __name__.split('.')[-1]
  34 
  35 def htdocs_access(request):
  36     return isinstance(request.cfg.attachments, type({}))
  37 
  38 
  39 #############################################################################
  40 ### External interface - these are called from the core code
  41 #############################################################################
  42 
  43 def getBasePath(request):
  44     """ Get base path where page dirs for attachments are stored.
  45     """
  46     if htdocs_access(request):
  47         return request.cfg.attachments['dir']
  48     else:
  49         return request.rootpage.getPagePath('pages')
  50 
  51 
  52 def getAttachDir(request, pagename, create=0):
  53     """ Get directory where attachments for page `pagename` are stored.
  54     """
  55     if htdocs_access(request):
  56         # direct file access via webserver, from public htdocs area
  57         pagename = wikiutil.quoteWikinameFS(pagename)
  58         attach_dir = os.path.join(request.cfg.attachments['dir'], pagename, "attachments")
  59         if create and not os.path.isdir(attach_dir):
  60             filesys.makeDirs(attach_dir)
  61     else:
  62         # send file via CGI, from page storage area
  63         attach_dir = Page(request, pagename).getPagePath("attachments", check_create=create)
  64 
  65     return attach_dir
  66 
  67 
  68 def getAttachUrl(pagename, filename, request, addts=0, escaped=0):
  69     """ Get URL that points to attachment `filename` of page `pagename`.
  70 
  71         If 'addts' is true, a timestamp with the file's modification time
  72         is added, so that browsers reload a changed file.
  73     """
  74     if htdocs_access(request):
  75         # direct file access via webserver
  76         timestamp = ''
  77         if addts:
  78             try:
  79                 timestamp = '?ts=%s' % os.path.getmtime(
  80                     getFilename(request, pagename, filename))
  81             except IOError:
  82                 pass
  83 
  84         url = "%s/%s/attachments/%s%s" % (
  85             request.cfg.attachments['url'], wikiutil.quoteWikinameFS(pagename),
  86             urllib.quote(filename.encode(config.charset)), timestamp)
  87     else:
  88         # send file via CGI
  89         url = "%s/%s?action=%s&do=get&target=%s" % (
  90             request.getScriptname(), wikiutil.quoteWikinameURL(pagename),
  91             action_name, urllib.quote_plus(filename.encode(config.charset)))
  92     if escaped:
  93         url = wikiutil.escape(url)
  94     return url
  95 
  96 def getIndicator(request, pagename):
  97     """ Get an attachment indicator for a page (linked clip image) or
  98         an empty string if not attachments exist.
  99     """
 100     _ = request.getText
 101     attach_dir = getAttachDir(request, pagename)
 102     if not os.path.exists(attach_dir): return ''
 103 
 104     files = os.listdir(attach_dir)
 105     if not files: return ''
 106 
 107     attach_count = _('[%d attachments]') % len(files)
 108     attach_icon = request.theme.make_icon('attach', vars={ 'attach_count': attach_count })
 109     attach_link = wikiutil.link_tag(request,
 110         "%s?action=AttachFile" % wikiutil.quoteWikinameURL(pagename),
 111         attach_icon)
 112 
 113     return attach_link
 114 
 115 
 116 def getFilename(request, pagename, name):
 117     return os.path.join(getAttachDir(request, pagename), name).encode(config.charset)
 118 
 119 
 120 def info(pagename, request):
 121     """ Generate snippet with info on the attachment for page `pagename`.
 122     """
 123     _ = request.getText
 124 
 125     attach_dir = getAttachDir(request, pagename)
 126     files = []
 127     if os.path.isdir(attach_dir):
 128         files = os.listdir(attach_dir)
 129     page = Page(request, pagename)
 130     # TODO: remove escape=0 in 1.4
 131     link = page.url(request, {'action': 'AttachFile'}, escape=0)
 132     attach_info = _('There are <a href="%(link)s">%(count)s attachment(s)</a> stored for this page.', formatted=False) % {
 133         'count': len(files),
 134         'link': wikiutil.escape(link)
 135         }
 136     return "\n<p>\n%s\n</p>\n" % attach_info
 137 
 138 
 139 #############################################################################
 140 ### Internal helpers
 141 #############################################################################
 142 
 143 def _addLogEntry(request, action, pagename, filename):
 144     """ Add an entry to the edit log on uploads and deletes.
 145 
 146         `action` should be "ATTNEW" or "ATTDEL"
 147     """
 148     from MoinMoin.logfile import editlog
 149     t = wikiutil.timestamp2version(time.time())
 150     # urllib always return ascii
 151     fname = unicode(urllib.quote(filename.encode(config.charset)))
 152 
 153     # TODO: for now we simply write 2 logs, maybe better use some multilog stuff
 154     # Write to global log
 155     log = editlog.EditLog(request)
 156     log.add(request, t, 99999999, action, pagename, request.remote_addr, fname)
 157 
 158     # Write to local log
 159     log = editlog.EditLog(request, rootpagename=pagename)
 160     log.add(request, t, 99999999, action, pagename, request.remote_addr, fname)
 161 
 162 
 163 def _access_file(pagename, request):
 164     """ Check form parameter `target` and return a tuple of
 165         `(filename, filepath)` for an existing attachment.
 166 
 167         Return `(None, None)` if an error occurs.
 168     """
 169     _ = request.getText
 170 
 171     error = None
 172     if not request.form.get('target', [''])[0]:
 173         error = _("Filename of attachment not specified!")
 174     else:
 175         filename = wikiutil.taintfilename(request.form['target'][0])
 176         fpath = getFilename(request, pagename, filename)
 177 
 178         if os.path.isfile(fpath):
 179             return (filename, fpath)
 180         error = _("Attachment '%(filename)s' does not exist!") % {'filename': filename}
 181 
 182     error_msg(pagename, request, error)
 183     return (None, None)
 184 
 185 
 186 def _build_filelist(request, pagename, showheader, readonly):
 187     _ = request.getText
 188 
 189     # access directory
 190     attach_dir = getAttachDir(request, pagename)
 191     files = _get_files(request, pagename)
 192 
 193     str = ""
 194     if files:
 195         if showheader:
 196             str = str + _(
 197                 "To refer to attachments on a page, use '''{{{attachment:filename}}}''', \n"
 198                 "as shown below in the list of files. \n"
 199                 "Do '''NOT''' use the URL of the {{{[get]}}} link, \n"
 200                 "since this is subject to change and can break easily."
 201             )
 202         str = str + "<ul>"
 203 
 204         label_del = _("del")
 205         label_get = _("get")
 206         label_edit = _("edit")
 207         label_view = _("view")
 208         label_unzip = _("unzip")
 209 
 210         for file in files:
 211             fsize = float(os.stat(os.path.join(attach_dir,file).encode(config.charset))[6]) # in byte
 212             fsize = "%.1f" % (fsize / 1024)
 213             baseurl = request.getScriptname()
 214             action = action_name
 215             urlpagename = wikiutil.quoteWikinameURL(pagename)
 216             urlfile = urllib.quote_plus(file.encode(config.charset))
 217 
 218             base, ext = os.path.splitext(file)
 219             get_url = getAttachUrl(pagename, file, request, escaped=1)
 220             parmdict = {'baseurl': baseurl, 'urlpagename': urlpagename, 'action': action,
 221                         'urlfile': urlfile, 'label_del': label_del,
 222                         'base': base, 'label_edit': label_edit,
 223                         'label_view': label_view,
 224                         'label_unzip': label_unzip,
 225                         'get_url': get_url, 'label_get': label_get,
 226                         'file': file, 'fsize': fsize,
 227                         'pagename': pagename}
 228 
 229             del_link = ''
 230             if request.user.may.delete(pagename) and not readonly:
 231                 del_link = '<a href="%(baseurl)s/%(urlpagename)s' \
 232                     '?action=%(action)s&amp;do=del&amp;target=%(urlfile)s">%(label_del)s</a>&nbsp;| ' % parmdict
 233             if zipfile.is_zipfile(os.path.join(attach_dir,file))and request.user.may.read(pagename) and request.user.may.delete(pagename) and request.user.may.write(pagename):
 234                 viewlink='<a href="%(baseurl)s/%(urlpagename)s?action=%(action)s&amp;do=unzip&amp;target=%(urlfile)s">%(label_unzip)s</a>' %    parmdict
 235 
 236             elif ext == '.draw':
 237                 viewlink = '<a href="%(baseurl)s/%(urlpagename)s?action=%(action)s&amp;drawing=%(base)s">%(label_edit)s</a>' % parmdict
 238             else:
 239                 viewlink = '<a href="%(baseurl)s/%(urlpagename)s?action=%(action)s&amp;do=view&amp;target=%(urlfile)s">%(label_view)s</a>' % parmdict
 240 
 241             parmdict['viewlink'] = viewlink
 242             parmdict['del_link'] = del_link
 243             str = str + ('<li>[%(del_link)s'
 244                 '<a href="%(get_url)s">%(label_get)s</a>&nbsp;| %(viewlink)s]'
 245                 ' (%(fsize)s KB) attachment:<strong>%(file)s</strong></li>') % parmdict
 246         str = str + "</ul>"
 247     else:
 248         if showheader:
 249             str = '%s<p>%s</p>' % (str, _("No attachments stored for %(pagename)s") % {'pagename': pagename})
 250 
 251     return str
 252 
 253 
 254 def _get_files(request, pagename):
 255     attach_dir = getAttachDir(request, pagename)
 256     if os.path.isdir(attach_dir):
 257         files = map(lambda a: a.decode(config.charset), os.listdir(attach_dir))
 258         files.sort()
 259         return files
 260     return []
 261 
 262 
 263 def _get_filelist(request, pagename):
 264     return _build_filelist(request, pagename, 1, 0)
 265 
 266 
 267 def error_msg(pagename, request, msg):
 268     Page(request, pagename).send_page(request, msg=msg)
 269 
 270 
 271 #############################################################################
 272 ### Create parts of the Web interface
 273 #############################################################################
 274 
 275 def send_link_rel(request, pagename):
 276     files = _get_files(request, pagename)
 277     if len(files) > 0 and not htdocs_access(request):
 278         scriptName = request.getScriptname()
 279         pagename_quoted = wikiutil.quoteWikinameURL(pagename)
 280 
 281         for file in files:
 282             url = "%s/%s?action=%s&do=view&target=%s" % (
 283                 scriptName, pagename_quoted,
 284                 action_name, urllib.quote_plus(file.encode(config.charset)))
 285     
 286             request.write(u'<link rel="Appendix" title="%s" href="%s">\n' % (
 287                 wikiutil.escape(file), wikiutil.escape(url)))
 288 
 289 
 290 def send_hotdraw(pagename, request):
 291     _ = request.getText
 292 
 293     now = time.time()
 294     pubpath = request.cfg.url_prefix + "/applets/TWikiDrawPlugin"
 295     basename = request.form['drawing'][0]
 296     drawpath = getAttachUrl(pagename, basename + '.draw', request, escaped=1)
 297     pngpath = getAttachUrl(pagename, basename + '.png', request, escaped=1)
 298     querystr = {'action': 'AttachFile', 'ts': now}
 299     querystr = wikiutil.escape(web.makeQueryString(querystr))
 300     pagelink = '%s?%s' % (wikiutil.quoteWikinameURL(pagename), querystr)
 301     savelink = Page(request, pagename).url(request) # XXX include target filename param here for twisted
 302                                            # request, {'savename': request.form['drawing'][0]+'.draw'}
 303     #savelink = '/cgi-bin/dumpform.bat'
 304 
 305     if htdocs_access(request):
 306         timestamp = '?ts=%s' % now
 307     else:
 308         timestamp = '&amp;ts=%s' % now
 309 
 310     request.write('<h2>' + _("Edit drawing") + '</h2>')
 311     request.write("""
 312 <p>
 313 <img src="%(pngpath)s%(timestamp)s">
 314 <applet code="CH.ifa.draw.twiki.TWikiDraw.class"
 315         archive="%(pubpath)s/twikidraw.jar" width="640" height="480">
 316 <param name="drawpath" value="%(drawpath)s">
 317 <param name="pngpath"  value="%(pngpath)s">
 318 <param name="savepath" value="%(savelink)s">
 319 <param name="basename" value="%(basename)s">
 320 <param name="viewpath" value="%(pagelink)s">
 321 <param name="helppath" value="%(pagelink)s">
 322 <strong>NOTE:</strong> You need a Java enabled browser to edit the drawing example.
 323 </applet>
 324 </p>""" % {
 325     'pngpath': pngpath, 'timestamp': timestamp,
 326     'pubpath': pubpath, 'drawpath': drawpath,
 327     'savelink': savelink, 'pagelink': pagelink,
 328     'basename': basename
 329 })
 330 
 331 
 332 def send_uploadform(pagename, request):
 333     """ Send the HTML code for the list of already stored attachments and
 334         the file upload form.
 335     """
 336     _ = request.getText
 337     
 338     if not request.user.may.read(pagename):
 339         request.write('<p>%s</p>' % _('You are not allowed to view this page.'))
 340         return
 341 
 342     request.write('<h2>' + _("Attached Files") + '</h2>')
 343     request.write(_get_filelist(request, pagename))
 344 
 345     if not request.user.may.write(pagename):
 346         request.write('<p>%s</p>' % _('You are not allowed to attach a file to this page.'))
 347         return
 348 
 349     if request.form.get('drawing', [None])[0]:
 350         send_hotdraw(pagename, request)
 351         return
 352 
 353     request.write('<h2>' + _("New Attachment") + '</h2><p>' +
 354 _("""An upload will never overwrite an existing file. If there is a name
 355 conflict, you have to rename the file that you want to upload.
 356 Otherwise, if "Rename to" is left blank, the original filename will be used.""") + '</p>')
 357     request.write("""
 358 <form action="%(baseurl)s/%(pagename)s" method="POST" enctype="multipart/form-data">
 359 <dl>
 360 <dt>%(upload_label_file)s</dt>
 361 <dd><input type="file" name="file" size="50"></dd>
 362 <dt>%(upload_label_rename)s</dt>
 363 <dd><input type="text" name="rename" size="50" value="%(rename)s"></dd>
 364 </dl>
 365 <p>
 366 <input type="hidden" name="action" value="%(action_name)s">
 367 <input type="hidden" name="do" value="upload">
 368 <input type="submit" value="%(upload_button)s">
 369 </p>
 370 </form>
 371 """ % {
 372     'baseurl': request.getScriptname(),
 373     'pagename': wikiutil.quoteWikinameURL(pagename),
 374     'action_name': action_name,
 375     'upload_label_file': _('File to upload'),
 376     'upload_label_rename': _('Rename to'),
 377     'rename': request.form.get('rename', [''])[0],
 378     'upload_button': _('Upload'),
 379 })
 380 
 381 #<dt>%(upload_label_mime)s</dt>
 382 #<dd><input type="text" name="mime" size="50"></dd>
 383 #    'upload_label_mime': _('MIME Type (optional)'),
 384 
 385 
 386 #############################################################################
 387 ### Web interface for file upload, viewing and deletion
 388 #############################################################################
 389 
 390 def execute(pagename, request):
 391     """ Main dispatcher for the 'AttachFile' action.
 392     """
 393     _ = request.getText
 394 
 395     msg = None
 396     if action_name in request.cfg.excluded_actions:
 397         msg = _('File attachments are not allowed in this wiki!')
 398     elif request.form.has_key('filepath'):
 399         if request.user.may.write(pagename):
 400             save_drawing(pagename, request)
 401             request.http_headers()
 402             request.write("OK")
 403         else:
 404             msg = _('You are not allowed to save a drawing on this page.')
 405     elif not request.form.has_key('do'):
 406         upload_form(pagename, request)
 407     elif request.form['do'][0] == 'upload':
 408         if request.user.may.write(pagename):
 409             if request.form.has_key('file'):
 410                 do_upload(pagename, request)
 411             else:
 412                 # This might happen when trying to upload file names
 413                 # with non-ascii characters on Safari.
 414                 msg = _("No file content. Delete non ASCII characters from the file name and try again.")
 415         else:
 416             msg = _('You are not allowed to attach a file to this page.')
 417     elif request.form['do'][0] == 'del':
 418         if request.user.may.delete(pagename):
 419             del_file(pagename, request)
 420         else:
 421             msg = _('You are not allowed to delete attachments on this page.')
 422     elif request.form['do'][0] == 'get':
 423         if request.user.may.read(pagename):
 424             get_file(pagename, request)
 425         else:
 426             msg = _('You are not allowed to get attachments from this page.')
 427     elif request.form['do'][0] == 'unzip':
 428          if request.user.may.delete(pagename) and request.user.may.read(pagename) and request.user.may.write(pagename):
 429             unzip_file(pagename, request)
 430          else:
 431             msg = _('You are not allowed to unzip attachments of this page.')
 432     elif request.form['do'][0] == 'view':
 433         if request.user.may.read(pagename):
 434             view_file(pagename, request)
 435         else:
 436             msg = _('You are not allowed to view attachments of this page.')
 437     else:
 438         msg = _('Unsupported upload action: %s') % (request.form['do'][0],)
 439 
 440     if msg:
 441         error_msg(pagename, request, msg)
 442 
 443 
 444 def upload_form(pagename, request, msg=''):
 445     _ = request.getText
 446 
 447     request.http_headers()
 448     # Use user interface language for this generated page
 449     request.setContentLanguage(request.lang)
 450     wikiutil.send_title(request, _('Attachments for "%(pagename)s"') % {'pagename': pagename}, pagename=pagename, msg=msg)
 451     request.write('<div id="content">\n') # start content div
 452     send_uploadform(pagename, request)
 453     request.write('</div>\n') # end content div
 454     wikiutil.send_footer(request, pagename, showpage=1)
 455 
 456 
 457 def do_upload(pagename, request):
 458     _ = request.getText
 459 
 460     # make filename
 461     filename = None
 462     if request.form.has_key('file__filename__'):
 463         filename = request.form['file__filename__']
 464     rename = None
 465     if request.form.has_key('rename'):
 466         rename = request.form['rename'][0].strip()
 467 
 468     # if we use twisted, "rename" field is NOT optional, because we
 469     # can't access the client filename
 470     if rename:
 471         target = rename
 472     elif filename:
 473         target = filename
 474     else:
 475         error_msg(pagename, request, _("Filename of attachment not specified!"))
 476         return
 477 
 478     # get file content
 479     filecontent = request.form['file'][0]
 480 
 481     target = wikiutil.taintfilename(target)
 482 
 483     # set mimetype from extension, or from given mimetype
 484     #type, encoding = mimetypes.guess_type(target)
 485     #if not type:
 486     #    ext = None
 487     #    if request.form.has_key('mime'):
 488     #        ext = mimetypes.guess_extension(request.form['mime'][0])
 489     #    if not ext:
 490     #        type, encoding = mimetypes.guess_type(filename)
 491     #        if type:
 492     #            ext = mimetypes.guess_extension(type)
 493     #        else:
 494     #            ext = ''
 495     #    target = target + ext
 496 
 497     # get directory, and possibly create it
 498     attach_dir = getAttachDir(request, pagename, create=1)
 499     # save file
 500     fpath = os.path.join(attach_dir, target).encode(config.charset)
 501     if os.path.exists(fpath):
 502         msg = _("Attachment '%(target)s' (remote name '%(filename)s') already exists.") % {
 503             'target': target, 'filename': filename}
 504     else:
 505         stream = open(fpath, 'wb')
 506         try:
 507             stream.write(filecontent)
 508         finally:
 509             stream.close()
 510         os.chmod(fpath, 0666 & config.umask)
 511 
 512         bytes = len(filecontent)
 513         msg = _("Attachment '%(target)s' (remote name '%(filename)s')"
 514                 " with %(bytes)d bytes saved.") % {
 515                 'target': target, 'filename': filename, 'bytes': bytes}
 516         _addLogEntry(request, 'ATTNEW', pagename, target)
 517 
 518     # return attachment list
 519     upload_form(pagename, request, msg)
 520 
 521 
 522 def save_drawing(pagename, request):
 523 
 524     filename = request.form['filename'][0]
 525     filecontent = request.form['filepath'][0]
 526 
 527     # there should be no difference in filename parsing with or without
 528     # htdocs_access, cause the filename param is used
 529     basepath, basename = os.path.split(filename)
 530     basename, ext = os.path.splitext(basename)
 531 
 532     # get directory, and possibly create it
 533     attach_dir = getAttachDir(request, pagename, create=1)
 534 
 535     if ext == '.draw':
 536         _addLogEntry(request, 'ATTDRW', pagename, basename + ext)
 537 
 538     savepath = os.path.join(getAttachDir(request, pagename), basename + ext)
 539     if ext == '.map' and filecontent.strip()=='':
 540         # delete map file if it is empty
 541         os.unlink(savepath)
 542     else:
 543         file = open(savepath, 'wb')
 544         try:
 545             file.write(filecontent)
 546         finally:
 547             file.close()
 548 
 549     # touch attachment directory to invalidate cache if new map is saved
 550     if ext == '.map':
 551         os.utime(getAttachDir(request, pagename), None)
 552 
 553 def del_file(pagename, request):
 554     _ = request.getText
 555 
 556     filename, fpath = _access_file(pagename, request)
 557     if not filename: return # error msg already sent in _access_file
 558 
 559     # delete file
 560     os.remove(fpath)
 561     _addLogEntry(request, 'ATTDEL', pagename, filename)
 562 
 563     upload_form(pagename, request, msg=_("Attachment '%(filename)s' deleted.") % {'filename': filename})
 564 
 565 
 566 def get_file(pagename, request):
 567     import shutil
 568 
 569     filename, fpath = _access_file(pagename, request)
 570     if not filename: return # error msg already sent in _access_file
 571 
 572     # get mimetype
 573     type, enc = mimetypes.guess_type(filename)
 574     if not type:
 575         type = "application/octet-stream"
 576 
 577     # send header
 578     request.http_headers([
 579         "Content-Type: %s" % type,
 580         "Content-Length: %d" % os.path.getsize(fpath),
 581         # TODO: fix the encoding here, plain 8 bit is not allowed according to the RFCs
 582         # There is no solution that is compatible to IE except stripping non-ascii chars
 583         "Content-Disposition: inline; filename=\"%s\"" % filename.encode(config.charset),
 584     ])
 585 
 586     # send data
 587     shutil.copyfileobj(open(fpath, 'rb'), request, 8192)
 588 
 589     raise MoinMoinNoFooter
 590 
 591 def unzip_file(pagename, request):
 592     _ = request.getText
 593 
 594     filename, fpath = _access_file(pagename, request)
 595     if not filename: return # error msg already sent in _access_file
 596    
 597     if not request.user.may.delete(pagename):
 598         request.write('<p>%s</p>' % _('You are not allowed to unzip on this page.'))
 599         return
 600  
 601     attachment_path = getAttachDir(request, pagename)
 602     single_file_size=2.0*1024*1024
 603     attachments_file_space=200.0*1024*1024
 604 
 605     files = _get_files(request, pagename)
 606 
 607     msg=""
 608     if files:
 609         fsize =0.0
 610         for file in files:
 611             fsize += float(os.stat(os.path.join(attachment_path,file).encode(config.charset))[6]) # in byte
 612 
 613         avalaible_attachments_file_space=attachments_file_space-fsize
 614 
 615         target=request.form['target'][0]
 616         file=attachment_path+'/'+target
 617         if zipfile.is_zipfile(file):
 618             zf = zipfile.ZipFile(file)
 619             sum_size_over_all_valid_files=0.0
 620             for name in zf.namelist():
 621                 if ((string.find(name,'/') == -1) and (string.find(name,'\\') == -1)):
 622                     zi = zf.getinfo(name)
 623                     sum_size_over_all_valid_files+=zi.file_size
 624 
 625             if (sum_size_over_all_valid_files < avalaible_attachments_file_space):
 626                 valid_name=[]
 627                 for name in zf.namelist():
 628                     if ((string.find(name,'/') == -1) and (string.find(name,'\\') == -1)):
 629                         zi = zf.getinfo(name)
 630                         if (zi.file_size < (single_file_size)):
 631                             new_file=os.path.join(attachment_path, name).encode(config.charset)
 632                             if not os.path.exists(new_file):
 633                                 outfile = open(new_file, 'wb')
 634                                 outfile.write(zf.read(name))
 635                                 outfile.flush()
 636                                 outfile.close()
 637                                # it's not allowed to zip a zip file so this is dropped
 638                                 if zipfile.is_zipfile(new_file):
 639                                     os.unlink(new_file)
 640                                 else:
 641                                     valid_name.append(name)
 642                                     os.chmod(new_file, 0666 & config.umask)
 643                                     _addLogEntry(request, 'ATTNEW', pagename, new_file)
 644 
 645                 if (len(valid_name) > 0):
 646                     upload_form(pagename, request, msg=_("Attachment '%(filename)s' unziped.") %
 647                                      {'filename': target})
 648                 else:
 649                     upload_form(pagename, request, msg=_("Attachment '%(filename)s' not unziped because result to big or included zip file or file exists.") %
 650                                      {'filename': target})
 651             else:
 652                 upload_form(pagename, request, msg=_("Attachment '%(filename)s' could not be unziped because result to big (%(space)d kB missing).") %
 653                              {'filename': target,
 654                               'space':(sum_size_over_all_valid_files-avalaible_attachments_file_space)/1024})
 655 
 656         else:
 657             msg = _('file '+target+' is no zip file!')
 658 
 659     if msg:
 660         error_msg(pagename, request, msg)
 661 
 662 def send_viewfile(pagename, request):
 663     _ = request.getText
 664 
 665     filename, fpath = _access_file(pagename, request)
 666     if not filename: return
 667 
 668     request.write('<h2>' + _("Attachment '%(filename)s'") % {'filename': filename} + '</h2>')
 669 
 670     type, enc = mimetypes.guess_type(filename)
 671     if type:
 672         if type[:5] == 'image':
 673             timestamp = htdocs_access(request) and "?%s" % time.time() or ''
 674             request.write('<img src="%s%s" alt="%s">' % (
 675                 getAttachUrl(pagename, filename, request, escaped=1), timestamp, wikiutil.escape(filename, 1)))
 676             return
 677         elif type[:4] == 'text':
 678             # TODO: should use formatter here!
 679             request.write("<pre>")
 680             # Try to decode file contents. It may return junk, but we
 681             # don't have enough information on attachments.
 682             content = open(fpath, 'r').read()
 683             content = wikiutil.decodeUnknownInput(content)
 684             content = wikiutil.escape(content)
 685             request.write(content)
 686             request.write("</pre>")
 687             return
 688 
 689     request.write('<p>' + _("Unknown file type, cannot display this attachment inline.") + '</p>')
 690     request.write('<a href="%s">%s</a>' % (
 691         getAttachUrl(pagename, filename, request, escaped=1), wikiutil.escape(filename)))
 692 
 693 
 694 def view_file(pagename, request):
 695     _ = request.getText
 696 
 697     filename, fpath = _access_file(pagename, request)
 698     if not filename: return
 699     
 700     # send header & title
 701     request.http_headers()
 702     # Use user interface language for this generated page
 703     request.setContentLanguage(request.lang)
 704     title = _('attachment:%(filename)s of %(pagename)s', formatted=True) % {
 705         'filename': filename, 'pagename': pagename}
 706     wikiutil.send_title(request, title, pagename=pagename)
 707 
 708     # send body
 709     # TODO: use formatter startContent?
 710     request.write('<div id="content">\n') # start content div
 711     send_viewfile(pagename, request)
 712     send_uploadform(pagename, request)
 713     request.write('</div>\n') # end content div
 714 
 715     # send footer
 716     wikiutil.send_footer(request, pagename)
 717 
 718 
 719 #############################################################################
 720 ### File attachment administration
 721 #############################################################################
 722 
 723 def do_admin_browser(request):
 724     """ Browser for SystemAdmin macro.
 725     """
 726     from MoinMoin.util.dataset import TupleDataset, Column
 727     _ = request.getText
 728 
 729     data = TupleDataset()
 730     data.columns = [
 731         Column('page', label=('Page')),
 732         Column('file', label=('Filename')),
 733         Column('size',  label=_('Size'), align='right'),
 734         #Column('action', label=_('Action')),
 735     ]
 736 
 737     # iterate over pages that might have attachments
 738     pages = request.rootpage.getPageList()
 739     for pagename in pages:
 740         # check for attachments directory
 741         page_dir = getAttachDir(request, pagename)
 742         if os.path.isdir(page_dir):
 743             # iterate over files of the page
 744             files = os.listdir(page_dir)
 745             for filename in files:
 746                 filepath = os.path.join(page_dir, filename)
 747                 data.addRow((
 748                     Page(request, pagename).link_to(request, querystr="action=AttachFile"),
 749                     wikiutil.escape(filename.decode(config.charset)),
 750                     os.path.getsize(filepath),
 751                     # '',
 752                 ))
 753 
 754     if data:
 755         from MoinMoin.widget.browser import DataBrowserWidget
 756 
 757         browser = DataBrowserWidget(request)
 758         browser.setData(data)
 759         return browser.toHTML()
 760 
 761     return ''

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] (2005-08-05 11:29:52, 28.4 KB) [[attachment:AttachFile-1.3.5-rb.py]]
  • [get | view] (2005-08-05 11:28:28, 5.8 KB) [[attachment:AttachFile-1.3.5.patch]]
  • [get | view] (2005-11-23 21:34:41, 7.4 KB) [[attachment:AttachFile-1.3.5.unzipwithsubdir.patch]]
  • [get | view] (2005-11-23 22:32:32, 4.0 KB) [[attachment:AttachFile-1.5.zipsubdir.patch]]
  • [get | view] (2005-04-08 08:23:38, 28.1 KB) [[attachment:AttachFile.py]]
  • [get | view] (2005-04-30 20:43:59, 0.5 KB) [[attachment:UploadDir.zip]]
  • [get | view] (2005-04-08 08:40:55, 9.5 KB) [[attachment:patch_AttachFile.py]]
  • [get | view] (2005-04-07 20:25:41, 4.3 KB) [[attachment:unzip1.png]]
  • [get | view] (2005-04-07 20:25:54, 5.3 KB) [[attachment:unzip2.png]]
  • [get | view] (2005-04-07 20:26:07, 3.3 KB) [[attachment:unzip3a.png]]
  • [get | view] (2005-04-07 20:26:24, 17.3 KB) [[attachment:unzip3b.png]]
  • [get | view] (2005-04-07 20:26:37, 5.4 KB) [[attachment:unzip4.png]]
  • [get | view] (2005-04-07 20:26:49, 4.6 KB) [[attachment:unzip5.png]]
  • [get | view] (2005-04-07 20:27:01, 4.8 KB) [[attachment:unzip6.png]]
  • [get | view] (2005-04-07 20:27:15, 5.2 KB) [[attachment:unzip7.png]]
 All files | Selected Files: delete move to page copy to page

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