|
tw-public@0
|
1 |
# -*- coding: iso-8859-1 -*-
|
|
tw-public@0
|
2 |
"""
|
|
tw-public@0
|
3 |
MoinMoin - AttachFile action
|
|
tw-public@0
|
4 |
|
|
tw-public@0
|
5 |
This action lets a page have multiple attachment files.
|
|
tw-public@0
|
6 |
It creates a folder <data>/pages/<pagename>/attachments
|
|
tw-public@0
|
7 |
and keeps everything in there.
|
|
tw-public@0
|
8 |
|
|
tw-public@0
|
9 |
Form values: action=Attachment
|
|
tw-public@0
|
10 |
1. with no 'do' key: returns file upload form
|
|
tw-public@0
|
11 |
2. do=attach: accept file upload and saves the file in
|
|
tw-public@0
|
12 |
../attachment/pagename/
|
|
tw-public@0
|
13 |
3. /pagename/fname?action=Attachment&do=get[&mimetype=type]:
|
|
tw-public@0
|
14 |
return contents of the attachment file with the name fname.
|
|
tw-public@0
|
15 |
4. /pathname/fname, do=view[&mimetype=type]:create a page
|
|
tw-public@0
|
16 |
to view the content of the file
|
|
tw-public@0
|
17 |
|
|
tw@3073
|
18 |
To link to an attachment, use [[attachment:file.txt]],
|
|
tw@3073
|
19 |
to embed an attachment, use {{attachment:file.png}}.
|
|
tw-public@0
|
20 |
|
|
tw@1911
|
21 |
@copyright: 2001 by Ken Sugino (sugino@mediaone.net),
|
|
tw@1918
|
22 |
2001-2004 by Juergen Hermann <jh@web.de>,
|
|
tw@1911
|
23 |
2005 MoinMoin:AlexanderSchremmer,
|
|
tw@1911
|
24 |
2005 DiegoOngaro at ETSZONE (diego@etszone.com),
|
|
rb@1952
|
25 |
2005-2007 MoinMoin:ReimarBauer,
|
|
tw@3149
|
26 |
2007-2008 MoinMoin:ThomasWaldmann
|
|
tw-public@0
|
27 |
@license: GNU GPL, see COPYING for details.
|
|
tw-public@0
|
28 |
"""
|
|
tw-public@0
|
29 |
|
|
tw@2846
|
30 |
import os, time, zipfile, mimetypes, errno
|
|
grzywacz@2490
|
31 |
|
|
tw@3591
|
32 |
from MoinMoin import log
|
|
tw@3591
|
33 |
logging = log.getLogger(__name__)
|
|
tw@3591
|
34 |
|
|
tw@1791
|
35 |
from MoinMoin import config, wikiutil, packages
|
|
tw-public@0
|
36 |
from MoinMoin.Page import Page
|
|
tw@1115
|
37 |
from MoinMoin.util import filesys, timefuncs
|
|
tw@2983
|
38 |
from MoinMoin.security.textcha import TextCha
|
|
grzywacz@2106
|
39 |
from MoinMoin.events import FileAttachedEvent, send_event
|
|
tw-public@0
|
40 |
|
|
tw-public@0
|
41 |
action_name = __name__.split('.')[-1]
|
|
tw-public@0
|
42 |
|
|
tw-public@0
|
43 |
#############################################################################
|
|
tw-public@0
|
44 |
### External interface - these are called from the core code
|
|
tw-public@0
|
45 |
#############################################################################
|
|
tw-public@0
|
46 |
|
|
alex@691
|
47 |
class AttachmentAlreadyExists(Exception):
|
|
alex@691
|
48 |
pass
|
|
alex@691
|
49 |
|
|
tw@3150
|
50 |
|
|
tw-public@0
|
51 |
def getBasePath(request):
|
|
tw@3150
|
52 |
""" Get base path where page dirs for attachments are stored. """
|
|
tw@3149
|
53 |
return request.rootpage.getPagePath('pages')
|
|
tw-public@0
|
54 |
|
|
tw-public@0
|
55 |
|
|
tw-public@0
|
56 |
def getAttachDir(request, pagename, create=0):
|
|
tw@3150
|
57 |
""" Get directory where attachments for page `pagename` are stored. """
|
|
tw@3149
|
58 |
if request.page and pagename == request.page.page_name:
|
|
tw@3149
|
59 |
page = request.page # reusing existing page obj is faster
|
|
tw-public@0
|
60 |
else:
|
|
tw@3149
|
61 |
page = Page(request, pagename)
|
|
tw@3149
|
62 |
return page.getPagePath("attachments", check_create=create)
|
|
tw-public@0
|
63 |
|
|
tw@3150
|
64 |
|
|
MoinMoinBugs/BrokenAttachmentPaths">Florian@143
|
65 |
def absoluteName(url, pagename):
|
|
tw@281
|
66 |
""" Get (pagename, filename) of an attachment: link
|
|
tw@281
|
67 |
@param url: PageName/filename.ext or filename.ext (unicode)
|
|
tw@281
|
68 |
@param pagename: name of the currently processed page (unicode)
|
|
tw@281
|
69 |
@rtype: tuple of unicode
|
|
tw@281
|
70 |
@return: PageName, filename.ext
|
|
MoinMoinBugs/BrokenAttachmentPaths">Florian@143
|
71 |
"""
|
|
tw@3084
|
72 |
url = wikiutil.AbsPageName(pagename, url)
|
|
tw@281
|
73 |
pieces = url.split(u'/')
|
|
tw@281
|
74 |
if len(pieces) == 1:
|
|
MoinMoinBugs/BrokenAttachmentPaths">Florian@143
|
75 |
return pagename, pieces[0]
|
|
MoinMoinBugs/BrokenAttachmentPaths">Florian@143
|
76 |
else:
|
|
Florian@145
|
77 |
return u"/".join(pieces[:-1]), pieces[-1]
|
|
tw-public@0
|
78 |
|
|
tw@3150
|
79 |
|
|
tw@3149
|
80 |
def attachUrl(request, pagename, filename=None, **kw):
|
|
tw@3149
|
81 |
# filename is not used yet, but should be used later to make a sub-item url
|
|
tw@3149
|
82 |
if kw:
|
|
tw@3149
|
83 |
qs = '?%s' % wikiutil.makeQueryString(kw, want_unicode=False)
|
|
tw-public@0
|
84 |
else:
|
|
tw@3149
|
85 |
qs = ''
|
|
tw@3149
|
86 |
return "%s/%s%s" % (request.getScriptname(), wikiutil.quoteWikinameURL(pagename), qs)
|
|
tw@3150
|
87 |
|
|
tw@3152
|
88 |
|
|
tw@3149
|
89 |
def getAttachUrl(pagename, filename, request, addts=0, escaped=0, do='get', drawing='', upload=False):
|
|
tw@3149
|
90 |
""" Get URL that points to attachment `filename` of page `pagename`. """
|
|
tw@3149
|
91 |
if upload:
|
|
tw@3149
|
92 |
if not drawing:
|
|
tw@3149
|
93 |
url = attachUrl(request, pagename, filename,
|
|
rb@3267
|
94 |
rename=wikiutil.taintfilename(filename), action=action_name)
|
|
tw@3149
|
95 |
else:
|
|
tw@3149
|
96 |
url = attachUrl(request, pagename, filename,
|
|
rb@3267
|
97 |
rename=wikiutil.taintfilename(filename), drawing=drawing, action=action_name)
|
|
tw@3149
|
98 |
else:
|
|
tw@3149
|
99 |
if not drawing:
|
|
tw@3149
|
100 |
url = attachUrl(request, pagename, filename,
|
|
tw@3149
|
101 |
target=filename, action=action_name, do=do)
|
|
tw@3149
|
102 |
else:
|
|
tw@3149
|
103 |
url = attachUrl(request, pagename, filename,
|
|
tw@3149
|
104 |
drawing=drawing, action=action_name)
|
|
tw-public@0
|
105 |
if escaped:
|
|
tw-public@0
|
106 |
url = wikiutil.escape(url)
|
|
tw-public@0
|
107 |
return url
|
|
tw-public@0
|
108 |
|
|
tw@2701
|
109 |
|
|
tw-public@0
|
110 |
def getIndicator(request, pagename):
|
|
tw-public@0
|
111 |
""" Get an attachment indicator for a page (linked clip image) or
|
|
tw-public@0
|
112 |
an empty string if not attachments exist.
|
|
tw-public@0
|
113 |
"""
|
|
tw-public@0
|
114 |
_ = request.getText
|
|
tw-public@0
|
115 |
attach_dir = getAttachDir(request, pagename)
|
|
tw@1920
|
116 |
if not os.path.exists(attach_dir):
|
|
tw@1920
|
117 |
return ''
|
|
tw-public@0
|
118 |
|
|
tw-public@0
|
119 |
files = os.listdir(attach_dir)
|
|
tw@1920
|
120 |
if not files:
|
|
tw@1920
|
121 |
return ''
|
|
tw-public@0
|
122 |
|
|
tw@3149
|
123 |
fmt = request.formatter
|
|
tw-public@0
|
124 |
attach_count = _('[%d attachments]') % len(files)
|
|
tw@889
|
125 |
attach_icon = request.theme.make_icon('attach', vars={'attach_count': attach_count})
|
|
tw@3149
|
126 |
attach_link = (fmt.url(1, attachUrl(request, pagename, action=action_name), rel='nofollow') +
|
|
tw@3149
|
127 |
attach_icon +
|
|
tw@3149
|
128 |
fmt.url(0))
|
|
tw-public@0
|
129 |
return attach_link
|
|
tw-public@0
|
130 |
|
|
tw-public@0
|
131 |
|
|
tw@281
|
132 |
def getFilename(request, pagename, filename):
|
|
tw@281
|
133 |
""" make complete pathfilename of file "name" attached to some page "pagename"
|
|
tw@281
|
134 |
@param request: request object
|
|
tw@281
|
135 |
@param pagename: name of page where the file is attached to (unicode)
|
|
tw@281
|
136 |
@param filename: filename of attached file (unicode)
|
|
tw@281
|
137 |
@rtype: string (in config.charset encoding)
|
|
tw@281
|
138 |
@return: complete path/filename of attached file
|
|
tw@281
|
139 |
"""
|
|
tw@1911
|
140 |
if isinstance(filename, unicode):
|
|
tw@1911
|
141 |
filename = filename.encode(config.charset)
|
|
rb@2885
|
142 |
return os.path.join(getAttachDir(request, pagename, create=1), filename)
|
|
tw-public@0
|
143 |
|
|
tw@3150
|
144 |
|
|
tw@1910
|
145 |
def exists(request, pagename, filename):
|
|
tw@1910
|
146 |
""" check if page <pagename> has a file <filename> attached """
|
|
tw@1910
|
147 |
fpath = getFilename(request, pagename, filename)
|
|
tw@1910
|
148 |
return os.path.exists(fpath)
|
|
tw-public@0
|
149 |
|
|
tw@3150
|
150 |
|
|
tw@1911
|
151 |
def size(request, pagename, filename):
|
|
tw@1911
|
152 |
""" return file size of file attachment """
|
|
tw@1911
|
153 |
fpath = getFilename(request, pagename, filename)
|
|
tw@1911
|
154 |
return os.path.getsize(fpath)
|
|
tw@1911
|
155 |
|
|
tw@3150
|
156 |
|
|
tw-public@0
|
157 |
def info(pagename, request):
|
|
tw@3150
|
158 |
""" Generate snippet with info on the attachment for page `pagename`. """
|
|
tw-public@0
|
159 |
_ = request.getText
|
|
tw-public@0
|
160 |
|
|
tw-public@0
|
161 |
attach_dir = getAttachDir(request, pagename)
|
|
tw-public@0
|
162 |
files = []
|
|
tw-public@0
|
163 |
if os.path.isdir(attach_dir):
|
|
tw-public@0
|
164 |
files = os.listdir(attach_dir)
|
|
tw-public@0
|
165 |
page = Page(request, pagename)
|
|
tw@3149
|
166 |
link = page.url(request, {'action': action_name})
|
|
tw@3122
|
167 |
attach_info = _('There are <a href="%(link)s">%(count)s attachment(s)</a> stored for this page.') % {
|
|
tw-public@0
|
168 |
'count': len(files),
|
|
tw-public@0
|
169 |
'link': wikiutil.escape(link)
|
|
tw-public@0
|
170 |
}
|
|
tw-public@0
|
171 |
return "\n<p>\n%s\n</p>\n" % attach_info
|
|
tw-public@0
|
172 |
|
|
tw@3150
|
173 |
|
|
tw@3568
|
174 |
def _write_stream(content, stream, bufsize=8192):
|
|
tw@3603
|
175 |
if hasattr(content, 'read'): # looks file-like
|
|
tw@3568
|
176 |
import shutil
|
|
tw@3568
|
177 |
shutil.copyfileobj(content, stream, bufsize)
|
|
tw@3603
|
178 |
elif isinstance(content, str):
|
|
tw@3603
|
179 |
stream.write(content)
|
|
tw@3603
|
180 |
else:
|
|
tw@3603
|
181 |
logging.error("unsupported content object: %r" % content)
|
|
tw@3603
|
182 |
raise
|
|
tw@3568
|
183 |
|
|
tw@1791
|
184 |
def add_attachment(request, pagename, target, filecontent, overwrite=0):
|
|
tw@3568
|
185 |
""" save <filecontent> to an attachment <target> of page <pagename>
|
|
tw@3568
|
186 |
|
|
tw@3568
|
187 |
filecontent can be either a str (in memory file content),
|
|
tw@3568
|
188 |
or an open file object (file content in e.g. a tempfile).
|
|
tw@3568
|
189 |
"""
|
|
rb@1797
|
190 |
_ = request.getText
|
|
rb@1797
|
191 |
|
|
tw@3150
|
192 |
# replace illegal chars
|
|
alex@691
|
193 |
target = wikiutil.taintfilename(target)
|
|
tw-public@0
|
194 |
|
|
alex@691
|
195 |
# get directory, and possibly create it
|
|
alex@691
|
196 |
attach_dir = getAttachDir(request, pagename, create=1)
|
|
alex@691
|
197 |
# save file
|
|
alex@691
|
198 |
fpath = os.path.join(attach_dir, target).encode(config.charset)
|
|
tw@1765
|
199 |
exists = os.path.exists(fpath)
|
|
tw@1765
|
200 |
if exists and not overwrite:
|
|
tw@3591
|
201 |
raise AttachmentAlreadyExists
|
|
alex@691
|
202 |
else:
|
|
tw@1765
|
203 |
if exists:
|
|
tw@1765
|
204 |
try:
|
|
tw@1765
|
205 |
os.remove(fpath)
|
|
tw@1765
|
206 |
except:
|
|
tw@1765
|
207 |
pass
|
|
alex@691
|
208 |
stream = open(fpath, 'wb')
|
|
alex@691
|
209 |
try:
|
|
tw@3568
|
210 |
_write_stream(filecontent, stream)
|
|
alex@691
|
211 |
finally:
|
|
alex@691
|
212 |
stream.close()
|
|
alex@691
|
213 |
|
|
alex@691
|
214 |
_addLogEntry(request, 'ATTNEW', pagename, target)
|
|
tw@2286
|
215 |
|
|
tw@3568
|
216 |
filesize = os.path.getsize(fpath)
|
|
tw@3568
|
217 |
event = FileAttachedEvent(request, pagename, target, filesize)
|
|
grzywacz@2490
|
218 |
send_event(event)
|
|
fpletz@1473
|
219 |
|
|
tw@3568
|
220 |
return target, filesize
|
|
tw@802
|
221 |
|
|
alex@520
|
222 |
|
|
tw-public@0
|
223 |
#############################################################################
|
|
tw-public@0
|
224 |
### Internal helpers
|
|
tw-public@0
|
225 |
#############################################################################
|
|
tw-public@0
|
226 |
|
|
tw-public@0
|
227 |
def _addLogEntry(request, action, pagename, filename):
|
|
tw-public@0
|
228 |
""" Add an entry to the edit log on uploads and deletes.
|
|
tw-public@0
|
229 |
|
|
tw-public@0
|
230 |
`action` should be "ATTNEW" or "ATTDEL"
|
|
tw-public@0
|
231 |
"""
|
|
tw-public@0
|
232 |
from MoinMoin.logfile import editlog
|
|
tw-public@0
|
233 |
t = wikiutil.timestamp2version(time.time())
|
|
tw@101
|
234 |
fname = wikiutil.url_quote(filename, want_unicode=True)
|
|
tw-public@0
|
235 |
|
|
tw-public@0
|
236 |
# Write to global log
|
|
tw-public@0
|
237 |
log = editlog.EditLog(request)
|
|
tw-public@0
|
238 |
log.add(request, t, 99999999, action, pagename, request.remote_addr, fname)
|
|
tw-public@0
|
239 |
|
|
tw-public@0
|
240 |
# Write to local log
|
|
tw-public@0
|
241 |
log = editlog.EditLog(request, rootpagename=pagename)
|
|
tw-public@0
|
242 |
log.add(request, t, 99999999, action, pagename, request.remote_addr, fname)
|
|
tw-public@0
|
243 |
|
|
tw-public@0
|
244 |
|
|
tw-public@0
|
245 |
def _access_file(pagename, request):
|
|
tw-public@0
|
246 |
""" Check form parameter `target` and return a tuple of
|
|
tw@3169
|
247 |
`(pagename, filename, filepath)` for an existing attachment.
|
|
tw-public@0
|
248 |
|
|
tw@3169
|
249 |
Return `(pagename, None, None)` if an error occurs.
|
|
tw-public@0
|
250 |
"""
|
|
tw-public@0
|
251 |
_ = request.getText
|
|
tw-public@0
|
252 |
|
|
tw-public@0
|
253 |
error = None
|
|
tw-public@0
|
254 |
if not request.form.get('target', [''])[0]:
|
|
tw-public@0
|
255 |
error = _("Filename of attachment not specified!")
|
|
tw-public@0
|
256 |
else:
|
|
tw-public@0
|
257 |
filename = wikiutil.taintfilename(request.form['target'][0])
|
|
tw-public@0
|
258 |
fpath = getFilename(request, pagename, filename)
|
|
tw-public@0
|
259 |
|
|
tw-public@0
|
260 |
if os.path.isfile(fpath):
|
|
tw@3169
|
261 |
return (pagename, filename, fpath)
|
|
tw-public@0
|
262 |
error = _("Attachment '%(filename)s' does not exist!") % {'filename': filename}
|
|
tw-public@0
|
263 |
|
|
tw-public@0
|
264 |
error_msg(pagename, request, error)
|
|
tw@3169
|
265 |
return (pagename, None, None)
|
|
tw-public@0
|
266 |
|
|
tw-public@0
|
267 |
|
|
tw@1653
|
268 |
def _build_filelist(request, pagename, showheader, readonly, mime_type='*'):
|
|
tw-public@0
|
269 |
_ = request.getText
|
|
tw@3149
|
270 |
fmt = request.html_formatter
|
|
tw-public@0
|
271 |
|
|
tw-public@0
|
272 |
# access directory
|
|
tw-public@0
|
273 |
attach_dir = getAttachDir(request, pagename)
|
|
tw-public@0
|
274 |
files = _get_files(request, pagename)
|
|
tw-public@0
|
275 |
|
|
tw@1653
|
276 |
if mime_type != '*':
|
|
tw@1653
|
277 |
files = [fname for fname in files if mime_type == mimetypes.guess_type(fname)[0]]
|
|
tw@1653
|
278 |
|
|
tw@3151
|
279 |
html = []
|
|
tw-public@0
|
280 |
if files:
|
|
tw-public@0
|
281 |
if showheader:
|
|
tw@3151
|
282 |
html.append(fmt.rawHTML(_(
|
|
tw@3151
|
283 |
"To refer to attachments on a page, use '''{{{attachment:filename}}}''', \n"
|
|
tw-public@0
|
284 |
"as shown below in the list of files. \n"
|
|
tw-public@0
|
285 |
"Do '''NOT''' use the URL of the {{{[get]}}} link, \n"
|
|
tw@3122
|
286 |
"since this is subject to change and can break easily.",
|
|
tw@3122
|
287 |
wiki=True
|
|
tw@3151
|
288 |
)))
|
|
tw-public@0
|
289 |
|
|
tw-public@0
|
290 |
label_del = _("del")
|
|
R@1608
|
291 |
label_move = _("move")
|
|
tw-public@0
|
292 |
label_get = _("get")
|
|
tw-public@0
|
293 |
label_edit = _("edit")
|
|
tw-public@0
|
294 |
label_view = _("view")
|
|
MoinMoin:PackageInstaller and zip support.">alex@80
|
295 |
label_unzip = _("unzip")
|
|
MoinMoin:PackageInstaller and zip support.">alex@80
|
296 |
label_install = _("install")
|
|
tw-public@0
|
297 |
|
|
tw@3151
|
298 |
html.append(fmt.bullet_list(1))
|
|
tw-public@0
|
299 |
for file in files:
|
|
rb@1985
|
300 |
mt = wikiutil.MimeType(filename=file)
|
|
tw@3151
|
301 |
fullpath = os.path.join(attach_dir, file).encode(config.charset)
|
|
tw@3151
|
302 |
st = os.stat(fullpath)
|
|
tw-public@0
|
303 |
base, ext = os.path.splitext(file)
|
|
tw@3151
|
304 |
parmdict = {'file': wikiutil.escape(file),
|
|
tw@3151
|
305 |
'fsize': "%.1f" % (float(st.st_size) / 1024),
|
|
tw@3151
|
306 |
'fmtime': request.user.getFormattedDateTime(st.st_mtime),
|
|
tw@3151
|
307 |
}
|
|
tw-public@0
|
308 |
|
|
tw@3151
|
309 |
links = []
|
|
tw@3151
|
310 |
may_delete = request.user.may.delete(pagename)
|
|
tw@3151
|
311 |
if may_delete and not readonly:
|
|
tw@3151
|
312 |
links.append(fmt.url(1, getAttachUrl(pagename, file, request, do='del')) +
|
|
tw@3151
|
313 |
fmt.text(label_del) +
|
|
tw@3151
|
314 |
fmt.url(0))
|
|
tw@3149
|
315 |
|
|
tw@3151
|
316 |
if may_delete and not readonly:
|
|
tw@3151
|
317 |
links.append(fmt.url(1, getAttachUrl(pagename, file, request, do='move')) +
|
|
tw@3149
|
318 |
fmt.text(label_move) +
|
|
tw@3151
|
319 |
fmt.url(0))
|
|
tw@3151
|
320 |
|
|
tw@3151
|
321 |
links.append(fmt.url(1, getAttachUrl(pagename, file, request)) +
|
|
tw@3151
|
322 |
fmt.text(label_get) +
|
|
tw@3151
|
323 |
fmt.url(0))
|
|
tw@3149
|
324 |
|
|
tw-public@0
|
325 |
if ext == '.draw':
|
|
tw@3151
|
326 |
links.append(fmt.url(1, getAttachUrl(pagename, file, request, drawing=base)) +
|
|
tw@3149
|
327 |
fmt.text(label_edit) +
|
|
tw@3149
|
328 |
fmt.url(0))
|
|
tw-public@0
|
329 |
else:
|
|
tw@3151
|
330 |
links.append(fmt.url(1, getAttachUrl(pagename, file, request, do='view')) +
|
|
tw@3149
|
331 |
fmt.text(label_view) +
|
|
tw@3149
|
332 |
fmt.url(0))
|
|
tw-public@0
|
333 |
|
|
tw@3705
|
334 |
try:
|
|
tw@3705
|
335 |
is_zipfile = zipfile.is_zipfile(fullpath)
|
|
tw@3705
|
336 |
if is_zipfile:
|
|
tw@3705
|
337 |
is_package = packages.ZipPackage(request, fullpath).isPackage()
|
|
tw@3705
|
338 |
if is_package and request.user.isSuperUser():
|
|
tw@3705
|
339 |
links.append(fmt.url(1, getAttachUrl(pagename, file, request, do='install')) +
|
|
tw@3705
|
340 |
fmt.text(label_install) +
|
|
tw@3705
|
341 |
fmt.url(0))
|
|
tw@3705
|
342 |
elif (not is_package and mt.minor == 'zip' and
|
|
tw@3705
|
343 |
may_delete and
|
|
tw@3705
|
344 |
request.user.may.read(pagename) and
|
|
tw@3705
|
345 |
request.user.may.write(pagename)):
|
|
tw@3705
|
346 |
links.append(fmt.url(1, getAttachUrl(pagename, file, request, do='unzip')) +
|
|
tw@3705
|
347 |
fmt.text(label_unzip) +
|
|
tw@3705
|
348 |
fmt.url(0))
|
|
tw@3705
|
349 |
except RuntimeError:
|
|
tw@3705
|
350 |
# We don't want to crash with a traceback here (an exception
|
|
tw@3705
|
351 |
# here could be caused by an uploaded defective zip file - and
|
|
tw@3705
|
352 |
# if we crash here, the user does not get a UI to remove the
|
|
tw@3705
|
353 |
# defective zip file again).
|
|
tw@3705
|
354 |
# RuntimeError is raised by zipfile stdlib module in case of
|
|
tw@3705
|
355 |
# problems (like inconsistent slash and backslash usage in the
|
|
tw@3705
|
356 |
# archive).
|
|
tw@3705
|
357 |
logging.exception("An exception within zip file attachment handling occurred:")
|
|
MoinMoin:PackageInstaller and zip support.">alex@80
|
358 |
|
|
tw@3151
|
359 |
html.append(fmt.listitem(1))
|
|
tw@3151
|
360 |
html.append("[%s]" % " | ".join(links))
|
|
tw@3151
|
361 |
html.append(" (%(fmtime)s, %(fsize)s KB) [[attachment:%(file)s]]" % parmdict)
|
|
tw@3151
|
362 |
html.append(fmt.listitem(0))
|
|
tw@3151
|
363 |
html.append(fmt.bullet_list(0))
|
|
tw@3149
|
364 |
|
|
tw-public@0
|
365 |
else:
|
|
tw-public@0
|
366 |
if showheader:
|
|
tw@3151
|
367 |
html.append(fmt.paragraph(1))
|
|
tw@3151
|
368 |
html.append(fmt.text(_("No attachments stored for %(pagename)s") % {
|
|
tw@3270
|
369 |
'pagename': pagename}))
|
|
tw@3151
|
370 |
html.append(fmt.paragraph(0))
|
|
tw-public@0
|
371 |
|
|
tw@3151
|
372 |
return ''.join(html)
|
|
tw-public@0
|
373 |
|
|
tw-public@0
|
374 |
|
|
tw-public@0
|
375 |
def _get_files(request, pagename):
|
|
tw-public@0
|
376 |
attach_dir = getAttachDir(request, pagename)
|
|
tw-public@0
|
377 |
if os.path.isdir(attach_dir):
|
|
tw@1866
|
378 |
files = [fn.decode(config.charset) for fn in os.listdir(attach_dir)]
|
|
tw-public@0
|
379 |
files.sort()
|
|
tw@3150
|
380 |
else:
|
|
tw@3150
|
381 |
files = []
|
|
tw@3150
|
382 |
return files
|
|
tw-public@0
|
383 |
|
|
tw-public@0
|
384 |
|
|
tw-public@0
|
385 |
def _get_filelist(request, pagename):
|
|
tw-public@0
|
386 |
return _build_filelist(request, pagename, 1, 0)
|
|
tw-public@0
|
387 |
|
|
tw@3150
|
388 |
|
|
tw-public@0
|
389 |
def error_msg(pagename, request, msg):
|
|
alex@2966
|
390 |
request.theme.add_msg(msg, "error")
|
|
alex@2966
|
391 |
Page(request, pagename).send_page()
|
|
tw-public@0
|
392 |
|
|
tw-public@0
|
393 |
|
|
tw-public@0
|
394 |
#############################################################################
|
|
tw-public@0
|
395 |
### Create parts of the Web interface
|
|
tw-public@0
|
396 |
#############################################################################
|
|
tw-public@0
|
397 |
|
|
tw-public@0
|
398 |
def send_link_rel(request, pagename):
|
|
tw-public@0
|
399 |
files = _get_files(request, pagename)
|
|
tw@3149
|
400 |
for fname in files:
|
|
tw@3149
|
401 |
url = getAttachUrl(pagename, fname, request, do='view', escaped=1)
|
|
tw@3149
|
402 |
request.write(u'<link rel="Appendix" title="%s" href="%s">\n' % (
|
|
tw@3149
|
403 |
wikiutil.escape(fname), url))
|
|
tw-public@0
|
404 |
|
|
tw-public@0
|
405 |
|
|
tw-public@0
|
406 |
def send_hotdraw(pagename, request):
|
|
tw-public@0
|
407 |
_ = request.getText
|
|
tw-public@0
|
408 |
|
|
tw-public@0
|
409 |
now = time.time()
|
|
tw@1318
|
410 |
pubpath = request.cfg.url_prefix_static + "/applets/TWikiDrawPlugin"
|
|
tw-public@0
|
411 |
basename = request.form['drawing'][0]
|
|
tw-public@0
|
412 |
drawpath = getAttachUrl(pagename, basename + '.draw', request, escaped=1)
|
|
tw-public@0
|
413 |
pngpath = getAttachUrl(pagename, basename + '.png', request, escaped=1)
|
|
tw@3149
|
414 |
pagelink = attachUrl(request, pagename, '', action=action_name, ts=now)
|
|
tw-public@0
|
415 |
helplink = Page(request, "HelpOnActions/AttachFile").url(request)
|
|
tw@3149
|
416 |
savelink = attachUrl(request, pagename, '', action=action_name, do='savedrawing')
|
|
tw@623
|
417 |
#savelink = Page(request, pagename).url(request) # XXX include target filename param here for twisted
|
|
tw-public@0
|
418 |
# request, {'savename': request.form['drawing'][0]+'.draw'}
|
|
tw-public@0
|
419 |
#savelink = '/cgi-bin/dumpform.bat'
|
|
tw-public@0
|
420 |
|
|
tw@3149
|
421 |
timestamp = '&ts=%s' % now
|
|
tw-public@0
|
422 |
|
|
tw-public@0
|
423 |
request.write('<h2>' + _("Edit drawing") + '</h2>')
|
|
tw-public@0
|
424 |
request.write("""
|
|
tw-public@0
|
425 |
<p>
|
|
tw-public@0
|
426 |
<img src="%(pngpath)s%(timestamp)s">
|
|
tw-public@0
|
427 |
<applet code="CH.ifa.draw.twiki.TWikiDraw.class"
|
|
tw-public@0
|
428 |
archive="%(pubpath)s/twikidraw.jar" width="640" height="480">
|
|
tw-public@0
|
429 |
<param name="drawpath" value="%(drawpath)s">
|
|
tw-public@0
|
430 |
<param name="pngpath" value="%(pngpath)s">
|
|
tw-public@0
|
431 |
<param name="savepath" value="%(savelink)s">
|
|
tw-public@0
|
432 |
<param name="basename" value="%(basename)s">
|
|
tw-public@0
|
433 |
<param name="viewpath" value="%(pagelink)s">
|
|
tw-public@0
|
434 |
<param name="helppath" value="%(helplink)s">
|
|
tw-public@0
|
435 |
<strong>NOTE:</strong> You need a Java enabled browser to edit the drawing example.
|
|
tw-public@0
|
436 |
</applet>
|
|
tw-public@0
|
437 |
</p>""" % {
|
|
tw-public@0
|
438 |
'pngpath': pngpath, 'timestamp': timestamp,
|
|
tw-public@0
|
439 |
'pubpath': pubpath, 'drawpath': drawpath,
|
|
tw-public@0
|
440 |
'savelink': savelink, 'pagelink': pagelink, 'helplink': helplink,
|
|
tw@4235
|
441 |
'basename': wikiutil.escape(basename),
|
|
tw-public@0
|
442 |
})
|
|
tw-public@0
|
443 |
|
|
tw-public@0
|
444 |
|
|
tw-public@0
|
445 |
def send_uploadform(pagename, request):
|
|
tw-public@0
|
446 |
""" Send the HTML code for the list of already stored attachments and
|
|
tw-public@0
|
447 |
the file upload form.
|
|
tw-public@0
|
448 |
"""
|
|
tw-public@0
|
449 |
_ = request.getText
|
|
tw-public@0
|
450 |
|
|
tw-public@0
|
451 |
if not request.user.may.read(pagename):
|
|
tw-public@0
|
452 |
request.write('<p>%s</p>' % _('You are not allowed to view this page.'))
|
|
tw-public@0
|
453 |
return
|
|
tw-public@0
|
454 |
|
|
tw@1635
|
455 |
writeable = request.user.may.write(pagename)
|
|
tw-public@0
|
456 |
|
|
tw@1635
|
457 |
# First send out the upload new attachment form on top of everything else.
|
|
tw@1635
|
458 |
# This avoids usability issues if you have to scroll down a lot to upload
|
|
tw@1635
|
459 |
# a new file when the page already has lots of attachments:
|
|
tw@1635
|
460 |
if writeable:
|
|
tw@3586
|
461 |
request.write('<h2>' + _("New Attachment") + '</h2>')
|
|
tw@1635
|
462 |
request.write("""
|
|
tw-public@0
|
463 |
<form action="%(baseurl)s/%(pagename)s" method="POST" enctype="multipart/form-data">
|
|
tw-public@0
|
464 |
<dl>
|
|
tw-public@0
|
465 |
<dt>%(upload_label_file)s</dt>
|
|
tw-public@0
|
466 |
<dd><input type="file" name="file" size="50"></dd>
|
|
tw-public@0
|
467 |
<dt>%(upload_label_rename)s</dt>
|
|
tw-public@0
|
468 |
<dd><input type="text" name="rename" size="50" value="%(rename)s"></dd>
|
|
tw@1765
|
469 |
<dt>%(upload_label_overwrite)s</dt>
|
|
tw@1765
|
470 |
<dd><input type="checkbox" name="overwrite" value="1" %(overwrite_checked)s></dd>
|
|
tw-public@0
|
471 |
</dl>
|
|
tw@2983
|
472 |
%(textcha)s
|
|
tw-public@0
|
473 |
<p>
|
|
tw-public@0
|
474 |
<input type="hidden" name="action" value="%(action_name)s">
|
|
tw-public@0
|
475 |
<input type="hidden" name="do" value="upload">
|
|
tw-public@0
|
476 |
<input type="submit" value="%(upload_button)s">
|
|
tw-public@0
|
477 |
</p>
|
|
tw-public@0
|
478 |
</form>
|
|
tw-public@0
|
479 |
""" % {
|
|
tw-public@0
|
480 |
'baseurl': request.getScriptname(),
|
|
tw-public@0
|
481 |
'pagename': wikiutil.quoteWikinameURL(pagename),
|
|
tw-public@0
|
482 |
'action_name': action_name,
|
|
tw-public@0
|
483 |
'upload_label_file': _('File to upload'),
|
|
tw-public@0
|
484 |
'upload_label_rename': _('Rename to'),
|
|
tw@4235
|
485 |
'rename': wikiutil.escape(request.form.get('rename', [''])[0], 1),
|
|
tw@1765
|
486 |
'upload_label_overwrite': _('Overwrite existing attachment of same name'),
|
|
tw@1765
|
487 |
'overwrite_checked': ('', 'checked')[request.form.get('overwrite', ['0'])[0] == '1'],
|
|
tw-public@0
|
488 |
'upload_button': _('Upload'),
|
|
tw@2983
|
489 |
'textcha': TextCha(request).render(),
|
|
tw-public@0
|
490 |
})
|
|
tw-public@0
|
491 |
|
|
tw@1635
|
492 |
request.write('<h2>' + _("Attached Files") + '</h2>')
|
|
tw@1635
|
493 |
request.write(_get_filelist(request, pagename))
|
|
tw@1635
|
494 |
|
|
tw@1635
|
495 |
if not writeable:
|
|
tw@1635
|
496 |
request.write('<p>%s</p>' % _('You are not allowed to attach a file to this page.'))
|
|
tw@1635
|
497 |
|
|
tw@1635
|
498 |
if writeable and request.form.get('drawing', [None])[0]:
|
|
tw@1635
|
499 |
send_hotdraw(pagename, request)
|
|
tw@1635
|
500 |
|
|
tw-public@0
|
501 |
|
|
tw-public@0
|
502 |
#############################################################################
|
|
tw-public@0
|
503 |
### Web interface for file upload, viewing and deletion
|
|
tw-public@0
|
504 |
#############################################################################
|
|
tw-public@0
|
505 |
|
|
tw-public@0
|
506 |
def execute(pagename, request):
|
|
tw@3150
|
507 |
""" Main dispatcher for the 'AttachFile' action. """
|
|
tw-public@0
|
508 |
_ = request.getText
|
|
tw-public@0
|
509 |
|
|
tw@3152
|
510 |
do = request.form.get('do', ['upload_form'])
|
|
tw@3152
|
511 |
handler = globals().get('_do_%s' % do[0])
|
|
tw@3152
|
512 |
if handler:
|
|
tw@3152
|
513 |
msg = handler(pagename, request)
|
|
tw-public@0
|
514 |
else:
|
|
tw@3164
|
515 |
msg = _('Unsupported AttachFile sub-action: %s') % (wikiutil.escape(do[0]), )
|
|
tw-public@0
|
516 |
if msg:
|
|
tw-public@0
|
517 |
error_msg(pagename, request, msg)
|
|
tw-public@0
|
518 |
|
|
tw@3150
|
519 |
|
|
tw@3152
|
520 |
def _do_upload_form(pagename, request):
|
|
tw@3152
|
521 |
upload_form(pagename, request)
|
|
tw@3152
|
522 |
|
|
tw@3152
|
523 |
|
|
tw-public@0
|
524 |
def upload_form(pagename, request, msg=''):
|
|
tw-public@0
|
525 |
_ = request.getText
|
|
tw-public@0
|
526 |
|
|
tw@1068
|
527 |
request.emit_http_headers()
|
|
tw-public@0
|
528 |
# Use user interface language for this generated page
|
|
tw-public@0
|
529 |
request.setContentLanguage(request.lang)
|
|
alex@2966
|
530 |
request.theme.add_msg(msg, "dialog")
|
|
alex@2966
|
531 |
request.theme.send_title(_('Attachments for "%(pagename)s"') % {'pagename': pagename}, pagename=pagename)
|
|
tw-public@0
|
532 |
request.write('<div id="content">\n') # start content div
|
|
tw-public@0
|
533 |
send_uploadform(pagename, request)
|
|
tw-public@0
|
534 |
request.write('</div>\n') # end content div
|
|
tw@616
|
535 |
request.theme.send_footer(pagename)
|
|
tw@617
|
536 |
request.theme.send_closing_html()
|
|
tw-public@0
|
537 |
|
|
tw@3150
|
538 |
|
|
tw@3591
|
539 |
def preprocess_filename(filename):
|
|
tw@3591
|
540 |
""" preprocess the filename we got from upload form,
|
|
tw@3591
|
541 |
strip leading drive and path (IE misbehaviour)
|
|
tw@3591
|
542 |
"""
|
|
tw@3591
|
543 |
if filename and len(filename) > 1 and (filename[1] == ':' or filename[0] == '\\'): # C:.... or \path... or \\server\...
|
|
tw@3591
|
544 |
bsindex = filename.rfind('\\')
|
|
tw@3591
|
545 |
if bsindex >= 0:
|
|
tw@3591
|
546 |
filename = filename[bsindex+1:]
|
|
tw@3591
|
547 |
return filename
|
|
tw@3591
|
548 |
|
|
tw@3591
|
549 |
|
|
tw@3152
|
550 |
def _do_upload(pagename, request):
|
|
tw-public@0
|
551 |
_ = request.getText
|
|
tw@3152
|
552 |
# Currently we only check TextCha for upload (this is what spammers ususally do),
|
|
tw@3152
|
553 |
# but it could be extended to more/all attachment write access
|
|
tw@3152
|
554 |
if not TextCha(request).check_answer_from_form():
|
|
tw@3152
|
555 |
return _('TextCha: Wrong answer! Go back and try again...')
|
|
tw@3152
|
556 |
|
|
tw@3591
|
557 |
form = request.form
|
|
tw@3591
|
558 |
overwrite = form.get('overwrite', [u'0'])[0]
|
|
tw@3152
|
559 |
try:
|
|
tw@3152
|
560 |
overwrite = int(overwrite)
|
|
tw@3152
|
561 |
except:
|
|
tw@3152
|
562 |
overwrite = 0
|
|
tw@3152
|
563 |
|
|
tw@3595
|
564 |
if not request.user.may.write(pagename):
|
|
tw@3152
|
565 |
return _('You are not allowed to attach a file to this page.')
|
|
tw@3152
|
566 |
|
|
tw@3595
|
567 |
if overwrite and not request.user.may.delete(pagename):
|
|
tw@3595
|
568 |
return _('You are not allowed to overwrite a file attachment of this page.')
|
|
tw@3595
|
569 |
|
|
tw@3591
|
570 |
filename = form.get('file__filename__')
|
|
tw@3591
|
571 |
rename = form.get('rename', [u''])[0].strip()
|
|
tw@3591
|
572 |
if rename:
|
|
tw@3591
|
573 |
target = rename
|
|
tw@3591
|
574 |
else:
|
|
tw@3591
|
575 |
target = filename
|
|
tw@3591
|
576 |
|
|
tw@3591
|
577 |
target = preprocess_filename(target)
|
|
tw@3591
|
578 |
target = wikiutil.clean_input(target)
|
|
tw@3591
|
579 |
|
|
tw@3591
|
580 |
if not target:
|
|
tw@3591
|
581 |
return _("Filename of attachment not specified!")
|
|
tw@3591
|
582 |
|
|
tw@3591
|
583 |
# get file content
|
|
tw@3591
|
584 |
filecontent = request.form.get('file', [None])[0]
|
|
tw@3591
|
585 |
if filecontent is None:
|
|
tw@3152
|
586 |
# This might happen when trying to upload file names
|
|
tw@3152
|
587 |
# with non-ascii characters on Safari.
|
|
tw@3152
|
588 |
return _("No file content. Delete non ASCII characters from the file name and try again.")
|
|
tw-public@0
|
589 |
|
|
alex@691
|
590 |
# add the attachment
|
|
alex@691
|
591 |
try:
|
|
tw@3568
|
592 |
target, bytes = add_attachment(request, pagename, target, filecontent, overwrite=overwrite)
|
|
tw-public@0
|
593 |
msg = _("Attachment '%(target)s' (remote name '%(filename)s')"
|
|
tw-public@0
|
594 |
" with %(bytes)d bytes saved.") % {
|
|
tw-public@0
|
595 |
'target': target, 'filename': filename, 'bytes': bytes}
|
|
alex@691
|
596 |
except AttachmentAlreadyExists:
|
|
alex@691
|
597 |
msg = _("Attachment '%(target)s' (remote name '%(filename)s') already exists.") % {
|
|
alex@691
|
598 |
'target': target, 'filename': filename}
|
|
tw-public@0
|
599 |
|
|
tw-public@0
|
600 |
# return attachment list
|
|
tw-public@0
|
601 |
upload_form(pagename, request, msg)
|
|
tw-public@0
|
602 |
|
|
tw-public@0
|
603 |
|
|
tw@3152
|
604 |
def _do_savedrawing(pagename, request):
|
|
tw@3180
|
605 |
_ = request.getText
|
|
tw@3180
|
606 |
|
|
tw@3152
|
607 |
if not request.user.may.write(pagename):
|
|
tw@3152
|
608 |
return _('You are not allowed to save a drawing on this page.')
|
|
tw-public@0
|
609 |
|
|
tw-public@0
|
610 |
filename = request.form['filename'][0]
|
|
tw-public@0
|
611 |
filecontent = request.form['filepath'][0]
|
|
tw-public@0
|
612 |
|
|
tw-public@0
|
613 |
basepath, basename = os.path.split(filename)
|
|
tw-public@0
|
614 |
basename, ext = os.path.splitext(basename)
|
|
tw-public@0
|
615 |
|
|
tw-public@0
|
616 |
# get directory, and possibly create it
|
|
tw-public@0
|
617 |
attach_dir = getAttachDir(request, pagename, create=1)
|
|
tw@3572
|
618 |
savepath = os.path.join(attach_dir, basename + ext)
|
|
tw@3572
|
619 |
|
|
tw-public@0
|
620 |
if ext == '.draw':
|
|
tw-public@0
|
621 |
_addLogEntry(request, 'ATTDRW', pagename, basename + ext)
|
|
tw@3603
|
622 |
filecontent = filecontent.read() # read file completely into memory
|
|
tw@889
|
623 |
filecontent = filecontent.replace("\r", "")
|
|
tw@3572
|
624 |
elif ext == '.map':
|
|
tw@3603
|
625 |
filecontent = filecontent.read() # read file completely into memory
|
|
tw@3572
|
626 |
filecontent = filecontent.strip()
|
|
tw-public@0
|
627 |
|
|
tw@3572
|
628 |
if filecontent:
|
|
tw@3572
|
629 |
# filecontent is either a file or a non-empty string
|
|
tw@3572
|
630 |
stream = open(savepath, 'wb')
|
|
tw@3572
|
631 |
try:
|
|
tw@3572
|
632 |
_write_stream(filecontent, stream)
|
|
tw@3572
|
633 |
finally:
|
|
tw@3572
|
634 |
stream.close()
|
|
tw@3572
|
635 |
else:
|
|
tw@3572
|
636 |
# filecontent is empty string (e.g. empty map file), delete the target file
|
|
tw@2846
|
637 |
try:
|
|
tw@2846
|
638 |
os.unlink(savepath)
|
|
tw@2846
|
639 |
except OSError, err:
|
|
tw@2846
|
640 |
if err.errno != errno.ENOENT: # no such file
|
|
tw@2846
|
641 |
raise
|
|
tw-public@0
|
642 |
|
|
tw-public@0
|
643 |
# touch attachment directory to invalidate cache if new map is saved
|
|
tw-public@0
|
644 |
if ext == '.map':
|
|
tw@3572
|
645 |
os.utime(attach_dir, None)
|
|
tw-public@0
|
646 |
|
|
tw@3152
|
647 |
request.emit_http_headers()
|
|
tw@3152
|
648 |
request.write("OK")
|
|
tw@3150
|
649 |
|
|
tw@3152
|
650 |
|
|
tw@3152
|
651 |
def _do_del(pagename, request):
|
|
tw-public@0
|
652 |
_ = request.getText
|
|
tw-public@0
|
653 |
|
|
tw@3152
|
654 |
pagename, filename, fpath = _access_file(pagename, request)
|
|
tw@3152
|
655 |
if not request.user.may.delete(pagename):
|
|
tw@3152
|
656 |
return _('You are not allowed to delete attachments on this page.')
|
|
tw@1920
|
657 |
if not filename:
|
|
tw@1920
|
658 |
return # error msg already sent in _access_file
|
|
tw-public@0
|
659 |
|
|
tw-public@0
|
660 |
# delete file
|
|
tw-public@0
|
661 |
os.remove(fpath)
|
|
tw-public@0
|
662 |
_addLogEntry(request, 'ATTDEL', pagename, filename)
|
|
tw-public@0
|
663 |
|
|
fpletz@1479
|
664 |
if request.cfg.xapian_search:
|
|
fpletz@1479
|
665 |
from MoinMoin.search.Xapian import Index
|
|
fpletz@1479
|
666 |
index = Index(request)
|
|
fpletz@1479
|
667 |
if index.exists:
|
|
fpletz@1479
|
668 |
index.remove_item(pagename, filename)
|
|
fpletz@1479
|
669 |
|
|
tw-public@0
|
670 |
upload_form(pagename, request, msg=_("Attachment '%(filename)s' deleted.") % {'filename': filename})
|
|
tw-public@0
|
671 |
|
|
tw@3150
|
672 |
|
|
R@1608
|
673 |
def move_file(request, pagename, new_pagename, attachment, new_attachment):
|
|
R@1608
|
674 |
_ = request.getText
|
|
R@1608
|
675 |
|
|
tw@1609
|
676 |
newpage = Page(request, new_pagename)
|
|
R@1608
|
677 |
if newpage.exists(includeDeleted=1) and request.user.may.write(new_pagename) and request.user.may.delete(pagename):
|
|
tw@1609
|
678 |
new_attachment_path = os.path.join(getAttachDir(request, new_pagename,
|
|
R@1608
|
679 |
create=1), new_attachment).encode(config.charset)
|
|
tw@1609
|
680 |
attachment_path = os.path.join(getAttachDir(request, pagename),
|
|
R@1608
|
681 |
attachment).encode(config.charset)
|
|
R@1608
|
682 |
|
|
R@1608
|
683 |
if os.path.exists(new_attachment_path):
|
|
tw@3171
|
684 |
upload_form(pagename, request,
|
|
tw@3171
|
685 |
msg=_("Attachment '%(new_pagename)s/%(new_filename)s' already exists.") % {
|
|
tw@3171
|
686 |
'new_pagename': new_pagename,
|
|
tw@3171
|
687 |
'new_filename': new_attachment})
|
|
R@1608
|
688 |
return
|
|
R@1608
|
689 |
|
|
R@1608
|
690 |
if new_attachment_path != attachment_path:
|
|
tw@3171
|
691 |
# move file
|
|
R@1608
|
692 |
filesys.rename(attachment_path, new_attachment_path)
|
|
R@1608
|
693 |
_addLogEntry(request, 'ATTDEL', pagename, attachment)
|
|
R@1608
|
694 |
_addLogEntry(request, 'ATTNEW', new_pagename, new_attachment)
|
|
tw@3171
|
695 |
upload_form(pagename, request,
|
|
tw@3171
|
696 |
msg=_("Attachment '%(pagename)s/%(filename)s' moved to '%(new_pagename)s/%(new_filename)s'.") % {
|
|
tw@3171
|
697 |
'pagename': pagename,
|
|
tw@3171
|
698 |
'filename': attachment,
|
|
tw@3171
|
699 |
'new_pagename': new_pagename,
|
|
tw@3171
|
700 |
'new_filename': new_attachment})
|
|
R@1608
|
701 |
else:
|
|
R@1608
|
702 |
upload_form(pagename, request, msg=_("Nothing changed"))
|
|
R@1608
|
703 |
else:
|
|
tw@3171
|
704 |
upload_form(pagename, request, msg=_("Page '%(new_pagename)s' does not exist or you don't have enough rights.") % {
|
|
tw@3171
|
705 |
'new_pagename': new_pagename})
|
|
R@1608
|
706 |
|
|
tw@3150
|
707 |
|
|
tw@3152
|
708 |
def _do_attachment_move(pagename, request):
|
|
R@1608
|
709 |
_ = request.getText
|
|
tw@3152
|
710 |
|
|
tw@3152
|
711 |
if 'cancel' in request.form:
|
|
tw@3152
|
712 |
return _('Move aborted!')
|
|
tw@3152
|
713 |
if not wikiutil.checkTicket(request, request.form['ticket'][0]):
|
|
tw@3152
|
714 |
return _('Please use the interactive user interface to move attachments!')
|
|
tw@3152
|
715 |
if not request.user.may.delete(pagename):
|
|
tw@3152
|
716 |
return _('You are not allowed to move attachments from this page.')
|
|
tw@3152
|
717 |
|
|
tw@1868
|
718 |
if 'newpagename' in request.form:
|
|
R@1608
|
719 |
new_pagename = request.form.get('newpagename')[0]
|
|
R@1608
|
720 |
else:
|
|
tw@3171
|
721 |
upload_form(pagename, request, msg=_("Move aborted because new page name is empty."))
|
|
tw@1868
|
722 |
if 'newattachmentname' in request.form:
|
|
R@1608
|
723 |
new_attachment = request.form.get('newattachmentname')[0]
|
|
R@1608
|
724 |
if new_attachment != wikiutil.taintfilename(new_attachment):
|
|
tw@1662
|
725 |
upload_form(pagename, request, msg=_("Please use a valid filename for attachment '%(filename)s'.") % {
|
|
R@1608
|
726 |
'filename': new_attachment})
|
|
R@1608
|
727 |
return
|
|
R@1608
|
728 |
else:
|
|
tw@3171
|
729 |
upload_form(pagename, request, msg=_("Move aborted because new attachment name is empty."))
|
|
R@1608
|
730 |
|
|
R@1608
|
731 |
attachment = request.form.get('oldattachmentname')[0]
|
|
R@1608
|
732 |
move_file(request, pagename, new_pagename, attachment, new_attachment)
|
|
R@1608
|
733 |
|
|
tw@3150
|
734 |
|
|
tw@3152
|
735 |
def _do_move(pagename, request):
|
|
R@1608
|
736 |
_ = request.getText
|
|
R@1608
|
737 |
|
|
tw@3152
|
738 |
pagename, filename, fpath = _access_file(pagename, request)
|
|
tw@3152
|
739 |
if not request.user.may.delete(pagename):
|
|
tw@3152
|
740 |
return _('You are not allowed to move attachments from this page.')
|
|
tw@1920
|
741 |
if not filename:
|
|
tw@1920
|
742 |
return # error msg already sent in _access_file
|
|
R@1608
|
743 |
|
|
R@1608
|
744 |
# move file
|
|
tw@3149
|
745 |
d = {'action': action_name,
|
|
MoinMoinBugs/MoveAttachmentNotWorkingWithModPython (thanks to Boleslaw Kulbabinski) (ported from 1.6)">rb@3040
|
746 |
'baseurl': request.getScriptname(),
|
|
R@1608
|
747 |
'do': 'attachment_move',
|
|
tw@1610
|
748 |
'ticket': wikiutil.createTicket(request),
|
|
R@1608
|
749 |
'pagename': pagename,
|
|
MoinMoinBugs/MoveAttachmentNotWorkingWithModPython (thanks to Boleslaw Kulbabinski) (ported from 1.6)">rb@3040
|
750 |
'pagename_quoted': wikiutil.quoteWikinameURL(pagename),
|
|
R@1608
|
751 |
'attachment_name': filename,
|
|
R@1608
|
752 |
'move': _('Move'),
|
|
R@1608
|
753 |
'cancel': _('Cancel'),
|
|
R@1608
|
754 |
'newname_label': _("New page name"),
|
|
R@1608
|
755 |
'attachment_label': _("New attachment name"),
|
|
R@1608
|
756 |
}
|
|
R@1608
|
757 |
formhtml = '''
|
|
MoinMoinBugs/MoveAttachmentNotWorkingWithModPython (thanks to Boleslaw Kulbabinski) (ported from 1.6)">rb@3040
|
758 |
<form action="%(baseurl)s/%(pagename_quoted)s" method="POST">
|
|
R@1608
|
759 |
<input type="hidden" name="action" value="%(action)s">
|
|
R@1608
|
760 |
<input type="hidden" name="do" value="%(do)s">
|
|
R@1608
|
761 |
<input type="hidden" name="ticket" value="%(ticket)s">
|
|
R@1608
|
762 |
<table>
|
|
R@1608
|
763 |
<tr>
|
|
R@1608
|
764 |
<td class="label"><label>%(newname_label)s</label></td>
|
|
R@1608
|
765 |
<td class="content">
|
|
rb@2708
|
766 |
<input type="text" name="newpagename" value="%(pagename)s" size="80">
|
|
R@1608
|
767 |
</td>
|
|
R@1608
|
768 |
</tr>
|
|
R@1608
|
769 |
<tr>
|
|
R@1608
|
770 |
<td class="label"><label>%(attachment_label)s</label></td>
|
|
R@1608
|
771 |
<td class="content">
|
|
rb@2708
|
772 |
<input type="text" name="newattachmentname" value="%(attachment_name)s" size="80">
|
|
R@1608
|
773 |
</td>
|
|
R@1608
|
774 |
</tr>
|
|
R@1608
|
775 |
<tr>
|
|
R@1608
|
776 |
<td></td>
|
|
R@1608
|
777 |
<td class="buttons">
|
|
R@1608
|
778 |
<input type="hidden" name="oldattachmentname" value="%(attachment_name)s">
|
|
R@1608
|
779 |
<input type="submit" name="move" value="%(move)s">
|
|
R@1608
|
780 |
<input type="submit" name="cancel" value="%(cancel)s">
|
|
R@1608
|
781 |
</td>
|
|
R@1608
|
782 |
</tr>
|
|
R@1608
|
783 |
</table>
|
|
R@1608
|
784 |
</form>''' % d
|
|
R@1608
|
785 |
thispage = Page(request, pagename)
|
|
alex@2966
|
786 |
request.theme.add_msg(formhtml, "dialog")
|
|
alex@2966
|
787 |
return thispage.send_page()
|
|
tw-public@0
|
788 |
|
|
tw@3150
|
789 |
|
|
tw@3152
|
790 |
def _do_get(pagename, request):
|
|
tw@3180
|
791 |
_ = request.getText
|
|
tw@3180
|
792 |
|
|
tw@3152
|
793 |
pagename, filename, fpath = _access_file(pagename, request)
|
|
tw@3152
|
794 |
if not request.user.may.read(pagename):
|
|
tw@3152
|
795 |
return _('You are not allowed to get attachments from this page.')
|
|
tw@801
|
796 |
if not filename:
|
|
tw@801
|
797 |
return # error msg already sent in _access_file
|
|
tw-public@0
|
798 |
|
|
tw@1115
|
799 |
timestamp = timefuncs.formathttpdate(int(os.path.getmtime(fpath)))
|
|
tw@1115
|
800 |
if request.if_modified_since == timestamp:
|
|
tw@1115
|
801 |
request.emit_http_headers(["Status: 304 Not modified"])
|
|
tw@1115
|
802 |
else:
|
|
tw@1115
|
803 |
mt = wikiutil.MimeType(filename=filename)
|
|
tw@1548
|
804 |
content_type = mt.content_type()
|
|
tw@1548
|
805 |
mime_type = mt.mime_type()
|
|
tw@1548
|
806 |
|
|
tw@1548
|
807 |
# TODO: fix the encoding here, plain 8 bit is not allowed according to the RFCs
|
|
tw@1548
|
808 |
# There is no solution that is compatible to IE except stripping non-ascii chars
|
|
tw@1548
|
809 |
filename_enc = filename.encode(config.charset)
|
|
tw@1548
|
810 |
|
|
tw@1548
|
811 |
# for dangerous files (like .html), when we are in danger of cross-site-scripting attacks,
|
|
tw@1548
|
812 |
# we just let the user store them to disk ('attachment').
|
|
tw@1548
|
813 |
# For safe files, we directly show them inline (this also works better for IE).
|
|
tw@1548
|
814 |
dangerous = mime_type in request.cfg.mimetypes_xss_protect
|
|
tw@1548
|
815 |
content_dispo = dangerous and 'attachment' or 'inline'
|
|
tw@1548
|
816 |
|
|
tw@1115
|
817 |
request.emit_http_headers([
|
|
tw@1548
|
818 |
'Content-Type: %s' % content_type,
|
|
rb@1798
|
819 |
'Last-Modified: %s' % timestamp,
|
|
tw@1548
|
820 |
'Content-Length: %d' % os.path.getsize(fpath),
|
|
tw@1548
|
821 |
'Content-Disposition: %s; filename="%s"' % (content_dispo, filename_enc),
|
|
tw@1115
|
822 |
])
|
|
tw-public@0
|
823 |
|
|
tw@1115
|
824 |
# send data
|
|
tw@3553
|
825 |
request.send_file(open(fpath, 'rb'))
|
|
tw-public@0
|
826 |
|
|
tw@3150
|
827 |
|
|
tw@3152
|
828 |
def _do_install(pagename, request):
|
|
MoinMoin:PackageInstaller and zip support.">alex@80
|
829 |
_ = request.getText
|
|
MoinMoin:PackageInstaller and zip support.">alex@80
|
830 |
|
|
tw@3152
|
831 |
pagename, target, targetpath = _access_file(pagename, request)
|
|
tw@3152
|
832 |
if not request.user.isSuperUser():
|
|
tw@3152
|
833 |
return _('You are not allowed to install files.')
|
|
MoinMoin:PackageInstaller and zip support.">alex@80
|
834 |
if not target:
|
|
MoinMoin:PackageInstaller and zip support.">alex@80
|
835 |
return
|
|
MoinMoin:PackageInstaller and zip support.">alex@80
|
836 |
|
|
MoinMoin:PackageInstaller and zip support.">alex@80
|
837 |
package = packages.ZipPackage(request, targetpath)
|
|
MoinMoin:PackageInstaller and zip support.">alex@80
|
838 |
|
|
MoinMoin:PackageInstaller and zip support.">alex@80
|
839 |
if package.isPackage():
|
|
MoinMoin:PackageInstaller and zip support.">alex@80
|
840 |
if package.installPackage():
|
|
tw@889
|
841 |
msg = _("Attachment '%(filename)s' installed.") % {'filename': wikiutil.escape(target)}
|
|
MoinMoin:PackageInstaller and zip support.">alex@80
|
842 |
else:
|
|
tw@889
|
843 |
msg = _("Installation of '%(filename)s' failed.") % {'filename': wikiutil.escape(target)}
|
|
tw@3149
|
844 |
if package.msg:
|
|
tw@3149
|
845 |
msg += "<br><pre>%s</pre>" % wikiutil.escape(package.msg)
|
|
MoinMoin:PackageInstaller and zip support.">alex@80
|
846 |
else:
|
|
johannes@2293
|
847 |
msg = _('The file %s is not a MoinMoin package file.') % wikiutil.escape(target)
|
|
MoinMoin:PackageInstaller and zip support.">alex@80
|
848 |
|
|
MoinMoin:PackageInstaller and zip support.">alex@80
|
849 |
upload_form(pagename, request, msg=msg)
|
|
MoinMoin:PackageInstaller and zip support.">alex@80
|
850 |
|
|
tw@3150
|
851 |
|
|
tw@3700
|
852 |
def _do_unzip(pagename, request, overwrite=False):
|
|
MoinMoin:PackageInstaller and zip support.">alex@80
|
853 |
_ = request.getText
|
|
tw@3700
|
854 |
pagename, filename, fpath = _access_file(pagename, request)
|
|
MoinMoin:PackageInstaller and zip support.">alex@80
|
855 |
|
|
tw@3152
|
856 |
if not (request.user.may.delete(pagename) and request.user.may.read(pagename) and request.user.may.write(pagename)):
|
|
tw@3152
|
857 |
return _('You are not allowed to unzip attachments of this page.')
|
|
tw@3700
|
858 |
|
|
tw@116
|
859 |
if not filename:
|
|
tw@116
|
860 |
return # error msg already sent in _access_file
|
|
MoinMoin:PackageInstaller and zip support.">alex@80
|
861 |
|
|
tw@3712
|
862 |
try:
|
|
tw@3712
|
863 |
if not zipfile.is_zipfile(fpath):
|
|
tw@3712
|
864 |
return _('The file %(filename)s is not a .zip file.') % {'filename': filename}
|
|
tw@116
|
865 |
|
|
tw@3712
|
866 |
# determine how which attachment names we have and how much space each is occupying
|
|
tw@3712
|
867 |
curr_fsizes = dict([(f, size(request, pagename, f)) for f in _get_files(request, pagename)])
|
|
MoinMoin:PackageInstaller and zip support.">alex@80
|
868 |
|
|
tw@3712
|
869 |
# Checks for the existance of one common prefix path shared among
|
|
tw@3712
|
870 |
# all files in the zip file. If this is the case, remove the common prefix.
|
|
tw@3712
|
871 |
# We also prepare a dict of the new filenames->filesizes.
|
|
tw@3712
|
872 |
zip_path_sep = '/' # we assume '/' is as zip standard suggests
|
|
tw@3712
|
873 |
fname_index = None
|
|
tw@3712
|
874 |
mapping = []
|
|
tw@3712
|
875 |
new_fsizes = {}
|
|
tw@3712
|
876 |
zf = zipfile.ZipFile(fpath)
|
|
tw@3712
|
877 |
for zi in zf.infolist():
|
|
tw@3712
|
878 |
name = zi.filename
|
|
tw@3712
|
879 |
if not name.endswith(zip_path_sep): # a file (not a directory)
|
|
tw@3712
|
880 |
if fname_index is None:
|
|
tw@3712
|
881 |
fname_index = name.rfind(zip_path_sep) + 1
|
|
tw@3712
|
882 |
path = name[:fname_index]
|
|
tw@3712
|
883 |
if (name.rfind(zip_path_sep) + 1 != fname_index # different prefix len
|
|
tw@3712
|
884 |
or
|
|
tw@3712
|
885 |
name[:fname_index] != path): # same len, but still different
|
|
tw@3712
|
886 |
mapping = [] # zip is not acceptable
|
|
tw@3712
|
887 |
break
|
|
tw@3712
|
888 |
if zi.file_size >= request.cfg.unzip_single_file_size: # file too big
|
|
tw@3712
|
889 |
mapping = [] # zip is not acceptable
|
|
tw@3712
|
890 |
break
|
|
tw@3712
|
891 |
finalname = name[fname_index:] # remove common path prefix
|
|
tw@3712
|
892 |
finalname = finalname.decode(config.charset, 'replace') # replaces trash with \uFFFD char
|
|
tw@3712
|
893 |
mapping.append((name, finalname))
|
|
tw@3712
|
894 |
new_fsizes[finalname] = zi.file_size
|
|
MoinMoin:PackageInstaller and zip support.">alex@80
|
895 |
|
|
tw@3712
|
896 |
# now we either have an empty mapping (if the zip is not acceptable),
|
|
tw@3712
|
897 |
# an identity mapping (no subdirs in zip, just all flat), or
|
|
tw@3712
|
898 |
# a mapping (origname, finalname) where origname is the zip member filename
|
|
tw@3712
|
899 |
# (including some prefix path) and finalname is a simple filename.
|
|
tw@889
|
900 |
|
|
tw@3712
|
901 |
# calculate resulting total file size / count after unzipping:
|
|
tw@3712
|
902 |
if overwrite:
|
|
tw@3712
|
903 |
curr_fsizes.update(new_fsizes)
|
|
tw@3712
|
904 |
total = curr_fsizes
|
|
tw@3712
|
905 |
else:
|
|
tw@3712
|
906 |
new_fsizes.update(curr_fsizes)
|
|
tw@3712
|
907 |
total = new_fsizes
|
|
tw@3712
|
908 |
total_count = len(total)
|
|
tw@3712
|
909 |
total_size = sum(total.values())
|
|
MoinMoin:PackageInstaller and zip support.">alex@80
|
910 |
|
|
tw@3712
|
911 |
if not mapping:
|
|
tw@3712
|
912 |
msg = _("Attachment '%(filename)s' not unzipped because some files in the zip "
|
|
tw@3712
|
913 |
"are either not in the same directory or exceeded the single file size limit (%(maxsize_file)d kB)."
|
|
tw@3712
|
914 |
) % {'filename': filename,
|
|
tw@3712
|
915 |
'maxsize_file': request.cfg.unzip_single_file_size / 1000, }
|
|
tw@3712
|
916 |
elif total_size > request.cfg.unzip_attachments_space:
|
|
tw@3712
|
917 |
msg = _("Attachment '%(filename)s' not unzipped because it would have exceeded "
|
|
tw@3712
|
918 |
"the per page attachment storage size limit (%(size)d kB).") % {
|
|
tw@3712
|
919 |
'filename': filename,
|
|
tw@3712
|
920 |
'size': request.cfg.unzip_attachments_space / 1000, }
|
|
tw@3712
|
921 |
elif total_count > request.cfg.unzip_attachments_count:
|
|
tw@3712
|
922 |
msg = _("Attachment '%(filename)s' not unzipped because it would have exceeded "
|
|
tw@3712
|
923 |
"the per page attachment count limit (%(count)d).") % {
|
|
tw@3712
|
924 |
'filename': filename,
|
|
tw@3712
|
925 |
'count': request.cfg.unzip_attachments_count, }
|
|
MoinMoin:PackageInstaller and zip support.">alex@80
|
926 |
else:
|
|
tw@3712
|
927 |
not_overwritten = []
|
|
tw@3712
|
928 |
for origname, finalname in mapping:
|
|
tw@3712
|
929 |
try:
|
|
tw@3712
|
930 |
# Note: reads complete zip member file into memory. ZipFile does not offer block-wise reading:
|
|
tw@3712
|
931 |
add_attachment(request, pagename, finalname, zf.read(origname), overwrite)
|
|
tw@3712
|
932 |
except AttachmentAlreadyExists:
|
|
tw@3712
|
933 |
not_overwritten.append(finalname)
|
|
tw@3712
|
934 |
if not_overwritten:
|
|
tw@3712
|
935 |
msg = _("Attachment '%(filename)s' partially unzipped (did not overwrite: %(filelist)s).") % {
|
|
tw@3712
|
936 |
'filename': filename,
|
|
tw@3712
|
937 |
'filelist': ', '.join(not_overwritten), }
|
|
tw@3712
|
938 |
else:
|
|
tw@3712
|
939 |
msg = _("Attachment '%(filename)s' unzipped.") % {'filename': filename}
|
|
tw@3712
|
940 |
except RuntimeError, err:
|
|
tw@3712
|
941 |
# We don't want to crash with a traceback here (an exception
|
|
tw@3712
|
942 |
# here could be caused by an uploaded defective zip file - and
|
|
tw@3712
|
943 |
# if we crash here, the user does not get a UI to remove the
|
|
tw@3712
|
944 |
# defective zip file again).
|
|
tw@3712
|
945 |
# RuntimeError is raised by zipfile stdlib module in case of
|
|
tw@3712
|
946 |
# problems (like inconsistent slash and backslash usage in the
|
|
tw@3712
|
947 |
# archive).
|
|
tw@3712
|
948 |
logging.exception("An exception within zip file attachment handling occurred:")
|
|
tw@3712
|
949 |
msg = _("A severe error occurred:") + ' ' + str(err)
|
|
MoinMoin:PackageInstaller and zip support.">alex@80
|
950 |
|
|
MoinMoin:PackageInstaller and zip support.">alex@80
|
951 |
upload_form(pagename, request, msg=wikiutil.escape(msg))
|
|
MoinMoin:PackageInstaller and zip support.">alex@80
|
952 |
|
|
tw@3150
|
953 |
|
|
tw-public@0
|
954 |
def send_viewfile(pagename, request):
|
|
tw-public@0
|
955 |
_ = request.getText
|
|
tw@3149
|
956 |
fmt = request.html_formatter
|
|
tw-public@0
|
957 |
|
|
tw@3169
|
958 |
pagename, filename, fpath = _access_file(pagename, request)
|
|
tw@1920
|
959 |
if not filename:
|
|
tw@1920
|
960 |
return
|
|
tw-public@0
|
961 |
|
|
tw-public@0
|
962 |
request.write('<h2>' + _("Attachment '%(filename)s'") % {'filename': filename} + '</h2>')
|
|
tw@2736
|
963 |
# show a download link above the content
|
|
tw@2736
|
964 |
label = _('Download')
|
|
moindev@3293
|
965 |
link = (fmt.url(1, getAttachUrl(pagename, filename, request, do='get'), css_class="download") +
|
|
tw@3149
|
966 |
fmt.text(label) +
|
|
tw@3149
|
967 |
fmt.url(0))
|
|
tw@3149
|
968 |
request.write('%s<br><br>' % link)
|
|
tw-public@0
|
969 |
|
|
tw@801
|
970 |
mt = wikiutil.MimeType(filename=filename)
|
|
rb@3307
|
971 |
|
|
rb@3338
|
972 |
# destinguishs if browser need a plugin in place
|
|
rb@3338
|
973 |
if mt.major == 'image' and mt.minor in config.browser_supported_images:
|
|
tw@3149
|
974 |
request.write('<img src="%s" alt="%s">' % (
|
|
tw@3149
|
975 |
getAttachUrl(pagename, filename, request, escaped=1),
|
|
tw@3149
|
976 |
wikiutil.escape(filename, 1)))
|
|
tw@801
|
977 |
return
|
|
tw@801
|
978 |
elif mt.major == 'text':
|
|
rb@1989
|
979 |
ext = os.path.splitext(filename)[1]
|
|
rb@1989
|
980 |
Parser = wikiutil.getParserForExtension(request.cfg, ext)
|
|
rb@1989
|
981 |
if Parser is not None:
|
|
rb@1989
|
982 |
try:
|
|
rb@1989
|
983 |
content = file(fpath, 'r').read()
|
|
rb@1989
|
984 |
content = wikiutil.decodeUnknownInput(content)
|
|
rb@1989
|
985 |
colorizer = Parser(content, request, filename=filename)
|
|
rb@1989
|
986 |
colorizer.format(request.formatter)
|
|
rb@1989
|
987 |
return
|
|
rb@1989
|
988 |
except IOError:
|
|
rb@1989
|
989 |
pass
|
|
rb@1989
|
990 |
|
|
tw@1833
|
991 |
request.write(request.formatter.preformatted(1))
|
|
tw@2286
|
992 |
# If we have text but no colorizing parser we try to decode file contents.
|
|
tw@801
|
993 |
content = open(fpath, 'r').read()
|
|
tw@801
|
994 |
content = wikiutil.decodeUnknownInput(content)
|
|
tw@801
|
995 |
content = wikiutil.escape(content)
|
|
tw@1833
|
996 |
request.write(request.formatter.text(content))
|
|
tw@1833
|
997 |
request.write(request.formatter.preformatted(0))
|
|
tw@801
|
998 |
return
|
|
tw-public@0
|
999 |
|
|
tw@3706
|
1000 |
try:
|
|
tw@3706
|
1001 |
package = packages.ZipPackage(request, fpath)
|
|
tw@3706
|
1002 |
if package.isPackage():
|
|
tw@3706
|
1003 |
request.write("<pre><b>%s</b>\n%s</pre>" % (_("Package script:"), wikiutil.escape(package.getScript())))
|
|
tw@3706
|
1004 |
return
|
|
MoinMoin:PackageInstaller and zip support.">alex@80
|
1005 |
|
|
tw@3706
|
1006 |
if zipfile.is_zipfile(fpath) and mt.minor == 'zip':
|
|
tw@3706
|
1007 |
zf = zipfile.ZipFile(fpath, mode='r')
|
|
tw@3706
|
1008 |
request.write("<pre>%-46s %19s %12s\n" % (_("File Name"), _("Modified")+" "*5, _("Size")))
|
|
tw@3706
|
1009 |
for zinfo in zf.filelist:
|
|
tw@3706
|
1010 |
date = "%d-%02d-%02d %02d:%02d:%02d" % zinfo.date_time
|
|
tw@3706
|
1011 |
request.write(wikiutil.escape("%-46s %s %12d\n" % (zinfo.filename, date, zinfo.file_size)))
|
|
tw@3706
|
1012 |
request.write("</pre>")
|
|
tw@3706
|
1013 |
return
|
|
tw@3706
|
1014 |
except RuntimeError:
|
|
tw@3706
|
1015 |
# We don't want to crash with a traceback here (an exception
|
|
tw@3706
|
1016 |
# here could be caused by an uploaded defective zip file - and
|
|
tw@3706
|
1017 |
# if we crash here, the user does not get a UI to remove the
|
|
tw@3706
|
1018 |
# defective zip file again).
|
|
tw@3706
|
1019 |
# RuntimeError is raised by zipfile stdlib module in case of
|
|
tw@3706
|
1020 |
# problems (like inconsistent slash and backslash usage in the
|
|
tw@3706
|
1021 |
# archive).
|
|
tw@3706
|
1022 |
logging.exception("An exception within zip file attachment handling occurred:")
|
|
MoinMoin:PackageInstaller and zip support.">alex@80
|
1023 |
return
|
|
MoinMoin:PackageInstaller and zip support.">alex@80
|
1024 |
|
|
rb@3266
|
1025 |
from MoinMoin import macro
|
|
rb@3266
|
1026 |
from MoinMoin.parser.text import Parser
|
|
rb@3266
|
1027 |
|
|
rb@1952
|
1028 |
macro.request = request
|
|
rb@1952
|
1029 |
macro.formatter = request.html_formatter
|
|
rb@3266
|
1030 |
p = Parser("##\n", request)
|
|
rb@3266
|
1031 |
m = macro.Macro(p)
|
|
rb@1952
|
1032 |
|
|
tw@2286
|
1033 |
# use EmbedObject to view valid mime types
|
|
rb@1967
|
1034 |
if mt is None:
|
|
rb@1952
|
1035 |
request.write('<p>' + _("Unknown file type, cannot display this attachment inline.") + '</p>')
|
|
tw@3149
|
1036 |
link = (fmt.url(1, getAttachUrl(pagename, filename, request)) +
|
|
tw@3149
|
1037 |
fmt.text(filename) +
|
|
tw@3149
|
1038 |
fmt.url(0))
|
|
tw@3149
|
1039 |
request.write('For using an external program follow this link %s' % link)
|
|
rb@1952
|
1040 |
return
|
|
rb@3266
|
1041 |
request.write(m.execute('EmbedObject', u'target=%s, pagename=%s' % (filename, pagename)))
|
|
rb@1952
|
1042 |
return
|
|
tw-public@0
|
1043 |
|
|
tw-public@0
|
1044 |
|
|
tw@3152
|
1045 |
def _do_view(pagename, request):
|
|
tw-public@0
|
1046 |
_ = request.getText
|
|
tw-public@0
|
1047 |
|
|
tw@3152
|
1048 |
orig_pagename = pagename
|
|
tw@3152
|
1049 |
pagename, filename, fpath = _access_file(pagename, request)
|
|
tw@3152
|
1050 |
if not request.user.may.read(pagename):
|
|
tw@3152
|
1051 |
return _('You are not allowed to view attachments of this page.')
|
|
tw@1920
|
1052 |
if not filename:
|
|
tw@1920
|
1053 |
return
|
|
tw-public@0
|
1054 |
|
|
tw-public@0
|
1055 |
# send header & title
|
|
tw@1068
|
1056 |
request.emit_http_headers()
|
|
tw-public@0
|
1057 |
# Use user interface language for this generated page
|
|
tw-public@0
|
1058 |
request.setContentLanguage(request.lang)
|
|
tw@3122
|
1059 |
title = _('attachment:%(filename)s of %(pagename)s') % {
|
|
tw-public@0
|
1060 |
'filename': filename, 'pagename': pagename}
|
|
tw@616
|
1061 |
request.theme.send_title(title, pagename=pagename)
|
|
tw-public@0
|
1062 |
|
|
tw-public@0
|
1063 |
# send body
|
|
tw@1833
|
1064 |
request.write(request.formatter.startContent())
|
|
tw@3169
|
1065 |
send_viewfile(orig_pagename, request)
|
|
tw-public@0
|
1066 |
send_uploadform(pagename, request)
|
|
tw@1833
|
1067 |
request.write(request.formatter.endContent())
|
|
tw-public@0
|
1068 |
|
|
tw@616
|
1069 |
request.theme.send_footer(pagename)
|
|
tw@617
|
1070 |
request.theme.send_closing_html()
|
|
tw-public@0
|
1071 |
|
|
tw@3151
|
1072 |
|
|
tw-public@0
|
1073 |
#############################################################################
|
|
tw-public@0
|
1074 |
### File attachment administration
|
|
tw-public@0
|
1075 |
#############################################################################
|
|
tw-public@0
|
1076 |
|
|
tw-public@0
|
1077 |
def do_admin_browser(request):
|
|
tw@3150
|
1078 |
""" Browser for SystemAdmin macro. """
|
|
tw-public@0
|
1079 |
from MoinMoin.util.dataset import TupleDataset, Column
|
|
tw-public@0
|
1080 |
_ = request.getText
|
|
tw-public@0
|
1081 |
|
|
tw-public@0
|
1082 |
data = TupleDataset()
|
|
tw-public@0
|
1083 |
data.columns = [
|
|
tw-public@0
|
1084 |
Column('page', label=('Page')),
|
|
tw-public@0
|
1085 |
Column('file', label=('Filename')),
|
|
tw@889
|
1086 |
Column('size', label=_('Size'), align='right'),
|
|
tw-public@0
|
1087 |
]
|
|
tw-public@0
|
1088 |
|
|
tw-public@0
|
1089 |
# iterate over pages that might have attachments
|
|
tw-public@0
|
1090 |
pages = request.rootpage.getPageList()
|
|
tw-public@0
|
1091 |
for pagename in pages:
|
|
tw-public@0
|
1092 |
# check for attachments directory
|
|
tw-public@0
|
1093 |
page_dir = getAttachDir(request, pagename)
|
|
tw-public@0
|
1094 |
if os.path.isdir(page_dir):
|
|
tw-public@0
|
1095 |
# iterate over files of the page
|
|
tw-public@0
|
1096 |
files = os.listdir(page_dir)
|
|
tw-public@0
|
1097 |
for filename in files:
|
|
tw-public@0
|
1098 |
filepath = os.path.join(page_dir, filename)
|
|
tw-public@0
|
1099 |
data.addRow((
|
|
tw-public@0
|
1100 |
Page(request, pagename).link_to(request, querystr="action=AttachFile"),
|
|
tw-public@0
|
1101 |
wikiutil.escape(filename.decode(config.charset)),
|
|
tw-public@0
|
1102 |
os.path.getsize(filepath),
|
|
tw-public@0
|
1103 |
))
|
|
tw-public@0
|
1104 |
|
|
tw-public@0
|
1105 |
if data:
|
|
tw-public@0
|
1106 |
from MoinMoin.widget.browser import DataBrowserWidget
|
|
tw-public@0
|
1107 |
|
|
tw-public@0
|
1108 |
browser = DataBrowserWidget(request)
|
|
tw-public@0
|
1109 |
browser.setData(data)
|
|
tw-public@0
|
1110 |
return browser.toHTML()
|
|
tw-public@0
|
1111 |
|
|
tw-public@0
|
1112 |
return ''
|
|
tw-public@0
|
1113 |
|