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&do=del&target=%(urlfile)s">%(label_del)s</a> | ' % 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&do=unzip&target=%(urlfile)s">%(label_unzip)s</a>' % parmdict
235
236 elif ext == '.draw':
237 viewlink = '<a href="%(baseurl)s/%(urlpagename)s?action=%(action)s&drawing=%(base)s">%(label_edit)s</a>' % parmdict
238 else:
239 viewlink = '<a href="%(baseurl)s/%(urlpagename)s?action=%(action)s&do=view&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> | %(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 = '&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.You are not allowed to attach a file to this page.