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