Attachment 'GuestPass-1.0.py'
Download 1 # -*- coding: iso-8859-1 -*-
2 """
3 MoinMoin - GuestPass action Version 1.0, 6.01.2013
4
5 @copyright: 2013 Ian Riley <ian.riley@internode.on.net>
6
7 incorporting code from:
8
9 @copyright: 2001 by Ken Sugino (sugino@mediaone.net),
10 2001-2004 by Juergen Hermann <jh@web.de>,
11 2005 MoinMoin:AlexanderSchremmer,
12 2005 DiegoOngaro at ETSZONE (diego@etszone.com),
13 2005-2010 MoinMoin:ReimarBauer,
14 2007-2008 MoinMoin:ThomasWaldmann
15
16 @license: GNU GPL, see COPYING for details.
17 """
18
19 import datetime, os, time
20 from MoinMoin import wikiutil, user
21 from MoinMoin.Page import Page
22
23 action_name = __name__.split('.')[-1]
24 actname = {'actname': action_name}
25 action = action_name.upper()
26
27 def doCancel(pagename, request, msg = '', msg_class=None):
28 thispage = Page(request, pagename)
29 request.theme.add_msg(msg, msg_class)
30 thispage.send_page()
31 return
32
33 def createPass(pagename, request, passlog):
34 _ = request.getText
35 thispage = Page(request, pagename)
36 user = request.user
37
38 try:
39 all_may_read = thispage.getACL(request).may(request,'All','read')
40 except:
41 all_may_read = True # Fail safe position. Not sure if needed.
42
43 if user.valid and user.may.write(pagename) and not all_may_read:
44 pass_time_usecs = wikiutil.timestamp2version(time.time())
45 username = user.name
46 guestpass = passlog.add(request, pass_time_usecs, action,
47 pagename, username)
48 msg = _('%(link)s guest pass created with expiry in %(expiry)s.') % {
49 'link' : guestpass.link_to(request),
50 'expiry': guestpass.expiry_days()}
51 request.theme.add_msg(msg, 'dialog')
52 elif all_may_read:
53 msg = _('A guest pass cannot be created for a public page.')
54 request.theme.add_msg(msg, 'info')
55 else:
56 msg = _('You are not allowed to create a guest pass on this page.')
57 request.theme.add_msg(msg, 'error')
58 thispage.send_page()
59 return
60
61 def retirePass(pagename, request, passlog, passcodes):
62 _ = request.getText
63 thispage = Page(request, pagename)
64 user = request.user
65
66 cnurret = False
67 created = False
68 retired = 0
69 msg =''
70
71 if not type(passcodes) is list:
72 passcodes = [passcode]
73
74 passlogPath = request.rootpage.getPagePath('pass-log', isfile=1)
75
76 for passcode in passcodes:
77 guestpass = getPass(pagename, request, passlog, passcode)
78 #shouldn't need to check guestpass as it comes from HTML form
79 current = guestpass.is_current()
80 created = user.id == guestpass.userid
81
82 if current and created:
83 pass_time_usecs = wikiutil.timestamp2version(time.time())
84 username = user.name
85 passlog.add(request, pass_time_usecs, action, pagename, username,
86 passcode=passcode, lifespan=0)
87 retired += 1
88 else:
89 if not current and not created:
90 msg = _('Guest pass invalid.')
91 if not current and created:
92 msg = _('Guest pass expired or already retired.')
93 if current and not created:
94 msg = _('You are not allowed to retire a guest pass on this page.')
95 request.theme.add_msg(msg, 'error')
96 thispage.send_page()
97 return
98 if retired == 0:
99 msg = _('No guest pass could be retired.')
100 elif retired == 1:
101 msg = _('Guest pass retired.')
102 elif retired > 1:
103 msg = _('Guest passes retired.')
104 request.theme.add_msg(msg, 'info')
105 thispage.send_page()
106 return
107
108 def getPass(pagename, request, passlog, passcode):
109 passlogPath = request.rootpage.getPagePath('pass-log', isfile=1)
110 guestpass = False
111 if passcode and os.path.exists(passlogPath):
112 passlog.set_filter(pagename=pagename, passcode=passcode)
113 for guestpass in passlog.reverse():
114 return guestpass # latest record only
115
116 def doPass(pagename, request, passlog, passcode):
117 _ = request.getText
118 from MoinMoin.widget.dialog import Dialog
119 thispage = Page(request, pagename)
120
121 if request.user.may.read(pagename): # ignore guestpass action
122 thispage.send_page()
123 return
124
125 guestpass = getPass(pagename, request, passlog, passcode)
126
127 if guestpass and guestpass.is_read_allowed(request):
128 msg = _('%(link)s viewed with guest pass expiring in %(expiry)s.') % {
129 'link' : guestpass.link_to(request),
130 'expiry': guestpass.expiry_days()}
131 request.theme.add_msg(Dialog(request, content=msg), 'dialog')
132 send_special = True
133 else:
134 msg = _('Invalid or expired guest pass.')
135 request.theme.add_msg(msg, 'error')
136 send_special = False
137 thispage.send_page(send_special=send_special)
138 return
139
140 def _send_viewFile(pagename, request, fpath, filename, passcode):
141 """Modified AttachFile.send_viewfile()
142
143 To send views of files attached to a guest pass accessed page.
144
145 Modified not to perform user and file checking. This is done by the caller.
146
147 """
148 import zipfile
149 from MoinMoin import config, packages
150 from MoinMoin.action.AttachFile import getAttachUrl
151 _ = request.getText
152 fmt = request.html_formatter
153
154 querystr={'action': action_name, 'pass': passcode, 'show': filename, 'do': 'get' }
155 download_link = Page(request, pagename).link_to(request, querystr=querystr,
156 css_class='download', text=_('Download'))
157
158 request.write('<h2>' + _("Attachment '%(filename)s'") % {'filename': filename} + '</h2>')
159 request.write('%s<br><br>' % download_link)
160
161 if filename.endswith('.tdraw') or filename.endswith('.adraw'):
162 request.write(fmt.attachment_drawing(filename, ''))
163 return
164
165 mt = wikiutil.MimeType(filename=filename)
166
167 # destinguishs if browser need a plugin in place
168 if mt.major == 'image' and mt.minor in config.browser_supported_images:
169 url = Page(request, pagename).url(request, querystr=querystr)
170 request.write('<img src="%s" alt="%s">' % (
171 url,
172 #wikiutil.escape(url, 1),
173 wikiutil.escape(filename, 1)))
174 return
175 elif mt.major == 'text':
176 ext = os.path.splitext(filename)[1]
177 Parser = wikiutil.getParserForExtension(request.cfg, ext)
178 if Parser is not None:
179 try:
180 content = file(fpath, 'r').read()
181 content = wikiutil.decodeUnknownInput(content)
182 colorizer = Parser(content, request, filename=filename)
183 colorizer.format(request.formatter)
184 return
185 except IOError:
186 pass
187
188 request.write(request.formatter.preformatted(1))
189 # text but no colorizing parser try to decode file contents
190 content = open(fpath, 'r').read()
191 content = wikiutil.decodeUnknownInput(content)
192 content = wikiutil.escape(content)
193 request.write(request.formatter.text(content))
194 request.write(request.formatter.preformatted(0))
195 return
196
197 try:
198 package = packages.ZipPackage(request, fpath)
199 if package.isPackage():
200 request.write("<pre><b>%s</b>\n%s</pre>" % (_("Package script:"),
201 wikiutil.escape(package.getScript())))
202 return
203
204 if zipfile.is_zipfile(fpath) and mt.minor == 'zip':
205 zf = zipfile.ZipFile(fpath, mode='r')
206 request.write("<pre>%-46s %19s %12s\n" % (_("File Name"),
207 _("Modified")+" "*5,
208 _("Size")))
209 for zinfo in zf.filelist:
210 date = "%d-%02d-%02d %02d:%02d:%02d" % zinfo.date_time
211 request.write(wikiutil.escape("%-46s %s %12d\n" % (
212 zinfo.filename, date, zinfo.file_size)))
213 request.write("</pre>")
214 return
215 except RuntimeError:
216 logging.exception("An exception within zip file attachment handling occurred:")
217 return
218
219 #not what we are prepared to show, so just force the do=get.
220 _get_File(pagename, request, fpath, filename, passcode)
221
222 return
223
224 def _view_File(pagename, request, fpath, filename, passcode):
225 """Modified AttachFile._do_view()
226
227 To view files attached to a guest pass accessed page.
228
229 Modified not to perform user and file checking. This is done by the caller.
230
231 """
232 _ = request.getText
233 request.formatter.page = Page(request, pagename)
234
235 request.setContentLanguage(request.lang)
236 title = _('attachment:%(filename)s of %(pagename)s') % {
237 'filename': filename, 'pagename': pagename}
238 request.theme.send_title(title, pagename=pagename)
239 request.write(request.formatter.startContent())
240 _send_viewFile(pagename, request, fpath, filename, passcode)
241 request.write(request.formatter.endContent())
242 request.theme.send_footer(pagename)
243 request.theme.send_closing_html()
244
245 def _get_File(pagename, request, fpath, filename, passcode):
246 """Modified AttachFile._do_get()
247
248 To get files attached to a guest pass accessed page.
249
250 Modified not to perform user and file checking. This is done by the caller.
251
252 """
253 from werkzeug import http_date
254 from MoinMoin import config
255
256 timestamp = datetime.datetime.fromtimestamp(os.path.getmtime(fpath))
257 if_modified = request.if_modified_since
258 if if_modified and if_modified >= timestamp:
259 request.status_code = 304
260 else:
261 mt = wikiutil.MimeType(filename=filename)
262 content_type = mt.content_type()
263 mime_type = mt.mime_type()
264 filename_enc = filename.encode(config.charset)
265 dangerous = mime_type in request.cfg.mimetypes_xss_protect
266 content_dispo = dangerous and 'attachment' or 'inline'
267
268 now = time.time()
269 request.headers['Date'] = http_date(now)
270 request.headers['Content-Type'] = content_type
271 request.headers['Last-Modified'] = http_date(timestamp)
272 request.headers['Expires'] = http_date(now - 365 * 24 * 3600)
273 request.headers['Content-Length'] = os.path.getsize(fpath)
274 content_dispo_string = '%s; filename="%s"' % (content_dispo, filename_enc)
275 request.headers['Content-Disposition'] = content_dispo_string
276 request.send_file(open(fpath, 'rb'))
277
278 def showAttachment(pagename, request, passlog, passcode, filename):
279 from MoinMoin.action.AttachFile import getFilename
280 _ = request.getText
281 thispage = Page(request, pagename)
282
283 do = request.values.get('do', 'view')
284 if do not in ['get', 'view', ]:
285 do = 'view' # the soft option
286
287 guestpass = getPass(pagename, request, passlog, passcode)
288
289 if guestpass and guestpass.is_read_allowed(request):
290 msg = ''
291 if not filename:
292 msg = _('Filename of attachment not specified.')
293 else:
294 filename = wikiutil.taintfilename(filename)
295 fpath = getFilename(request, pagename, filename)
296 if not os.path.isfile(fpath):
297 msg = _('Attachment "%(filename)s" does not exist.') % {
298 'filename': filename}
299 if msg:
300 request.theme.add_msg(msg, 'error')
301 thispage.send_page(send_special=True)
302 else:
303 if do == 'get':
304 _get_File(pagename, request, fpath, filename, passcode)
305 else:
306 _view_File(pagename, request, fpath, filename, passcode)
307 else:
308 msg = _('Invalid or expired guest pass.')
309 request.theme.add_msg(msg, 'error')
310 success = False
311 thispage.send_page()
312 return
313
314 def offerPass(pagename, request, passlog):
315 from MoinMoin.widget.dialog import Dialog
316 _ = request.getText
317 thispage = Page(request, pagename)
318 user = request.user
319 try:
320 all_may_read = thispage.getACL(request).may(request,'All','read')
321 except:
322 all_may_read = True # Fail safe position. Not sure if needed.
323
324 if user.valid and user.may.write(pagename) and not all_may_read:
325 # get all current guest passes for this page and user
326 passlogPath = request.rootpage.getPagePath('pass-log', isfile=1)
327 html_passes = []
328 expired = []
329 passlog.set_filter(pagename=pagename, userid=user.id)
330 for guestpass in passlog.reverse():
331 if guestpass.is_current() and not guestpass.passcode in expired:
332 html_passes.append(
333 ('<option value="%(passcode)s">' +
334 'Created %(dated)s expiring %(expiry)s' +
335 '</option>') % {'passcode': guestpass.passcode,
336 'dated' : guestpass.dated(user),
337 'expiry' : guestpass.expiry_days()})
338 else:
339 expired.append(guestpass.passcode)
340 n = min([3, len(html_passes)])
341 d = {'url': request.href(pagename),
342 'create_title' : _('Create guest pass'),
343 'create_action': action_name, # &create=1
344 'create_button': _('Create'),
345 'retire_title' : _('Retire guest passes'),
346 'retire_action': action_name, # &create=-1&pass=passcodes
347 'retire_button': _('Retire'),
348 'cancel_button': _('Cancel'),
349 'pagename' : pagename,
350 'ticket' : wikiutil.createTicket(request),
351 'option_passes': ' '.join(html_passes),
352 'len' : n, }
353 # include create form always
354 formhtml = '''
355 <form method="POST" action="%(url)s">
356 <strong>%(create_title)s</strong><br><br>
357 <input type="hidden" name="action" value="%(create_action)s">
358 <input type="hidden" name="create" value="1">
359 <input type="hidden" name="ticket" value="%(ticket)s">
360 <input type="submit" name="create" value="%(create_button)s">
361 <input type="submit" name="cancel" value="%(cancel_button)s"><br><br>
362 </form>''' % d
363 if html_passes:
364 # only include retire form if some existing passes
365 formhtml += '''
366 <form method="POST" action="%(url)s">
367 <strong>%(retire_title)s</strong><br><br>
368 <input type="hidden" name="action" value="%(retire_action)s">
369 <input type="hidden" name="create" value="-1">
370 <input type="hidden" name="ticket" value="%(ticket)s">
371 Your currently valid guest passes for %(pagename)s<br>
372 <select name="pass" size="%(len)s" multiple>
373 %(option_passes)s
374 </select><br><br>
375 Select guest pass then retire, if no longer needed.<br><br>
376 <input type="submit" name="retire" value="%(retire_button)s">
377 <input type="submit" name="cancel" value="%(cancel_button)s"><br><br>
378 </form>''' % d
379 request.theme.add_msg(Dialog(request, content=formhtml), 'dialog')
380 elif all_may_read:
381 msg = _('A guest pass cannot be created for a public page.')
382 request.theme.add_msg(msg, 'info')
383 else:
384 msg = _('You are not allowed to create a guest pass on this page.')
385 request.theme.add_msg(msg, 'error')
386 thispage.send_page()
387 return
388
389 def execute(pagename, request):
390 _ = request.getText
391 passlogPath = request.rootpage.getPagePath('pass-log', isfile=1)
392 PassLog = wikiutil.importWikiPlugin(request.cfg, 'logfile', 'passlog', 'PassLog')
393 passlog = PassLog(request, filename=passlogPath, uid_override=None)
394
395 post = request.method == 'POST'
396 ticket = request.values.get('ticket', False)
397 ticket_ok = ticket and (not(ticket) or wikiutil.checkTicket(request, ticket))
398 passcode = request.values.get('pass', '')
399 passcodes = request.values.getlist('pass')
400 create = int(request.values.get('create', False))
401 show = request.values.get('show', False)
402 cancel = request.values.get('cancel', False)
403
404 c = [post, bool(ticket), ticket_ok, bool(passcodes),
405 create, bool(show), bool(cancel), ]
406 if c == [0, 0, 0, 0, 0, 0, 0, ]:
407 offerPass(pagename, request, passlog)
408 elif c == [0, 0, 0, 1, 0, 0, 0, ]:
409 doPass(pagename, request, passlog, passcode)
410 elif c == [0, 0, 0, 1, 0, 1, 0, ]:
411 showAttachment(pagename, request, passlog, passcode, show)
412 elif c == [1, 1, 1, 0, 1, 0, 0, ]:
413 createPass(pagename, request, passlog)
414 elif c == [1, 1, 1, 1,-1, 0, 0, ]:
415 retirePass(pagename, request, passlog, passcodes)
416 elif c == [0, 0, 0, 0, 0, 1, 0, ]:
417 showAttachment(pagename, request, passlog, passcode, show)
418 elif c[0:2] == [1, 1, 0, ]:
419 msg = _('Use %(actname)s action only interactive interface') % actname
420 doCancel(pagename, request, msg, 'error')
421 elif c[6]:
422 msg = _('%(actname)s action cancelled.') % actname
423 doCancel(pagename, request, msg, 'info')
424 else:
425 msg = _('%(actname)s values invalid, action cancelled') % actname
426 doCancel(pagename, request, msg, 'error')
You are not allowed to attach a file to this page.