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