Attachment 'PageComment-081.py'
Download 1 # -*- coding: iso-8859-1 -*-
2 """
3 PageComment.py Version 0.81 2005. 10. 07.
4
5 This macro gives a form to post a new comment to the page and a list of the posted comments.
6
7 @copyright: 2005 by Seungik Lee <seungiklee<at>gmail.com> http://cds.icu.ac.kr/~silee/
8 @license: GPL
9
10 Usage: [[PageComment]]
11
12 Features:
13
14 Simple usage, just put [[PageComment]] on any page.
15 Lets anonymous users post a new comment with an input form.
16 Shows a list of the posted comments.
17 Support for comment deletion by given password.
18 Support for administrative action, e.g.,
19 - to delete a comment without entering a given password
20 - to restore a deleted comment
21 - to show the host IP address where a comment is posted
22
23
24 To do:
25
26 To handle the index overflow (*)
27 To pre-fill the author input with his/her login name (*)
28 To provide selective options for:
29 - formatting text or form (*)
30 - selecting columns to be shown (*)
31 - selecting a list of comments by a given ID not just page name (**)
32 To encrypt/decrypt the entered passwords (****)
33 To give more friendly message or instructions ;) (*)
34 To provide an option to remove a comment permanently (***)
35 To receive a confirm before deleting a comment (*)
36
37
38 Notes
39
40 'Gallery.py' developed by Simon Ryan has inspired this macro.
41
42
43 """
44
45 __author__ = "Seungik Lee"
46 __version__ = "0.81"
47
48 from MoinMoin import config, wikiutil
49 import string, os, StringIO, time
50 import codecs
51
52 class Globs:
53 # A quick place to plonk those shared variables
54 adminmsg=''
55 attachmentdir=''
56 pagename=''
57 admin=''
58 baseurl=''
59 welcome_msg='Please leave your comment for this page.'
60
61 def message(astring):
62 Globs.adminmsg='<font style="color: #aa0000; font-size: 0.9em;">PageComment: '+astring+'</font>\n'
63
64
65 def commentform(tmpauthor, tmptext, tmppasswd):
66 # A form for posting a new comment
67
68 html = [
69 u'<div id="commentform">',
70 u'<table id=commentform>',
71 u'<form action='+Globs.subname+' name=comment METHOD=POST>',
72 u'<tr><td>Name</td><td>Comment Text</td><td>Password</td><td></td></tr>',
73 u'<tr><td><input type=text size=10 maxlength=20 name=comauthor value="' + tmpauthor + '"></td>',
74 #u'<td><textarea name="comtext" rows="3" size="40">content</textarea></td>',
75 u'<td><input type=text size=50 maxlength=255 name=comtext value="' + tmptext + '"></td>',
76 u'<td><input type=password size=6 maxlength=10 name=compasswd value="' + tmppasswd + '"></td>',
77 u'<td><input type=submit value="POST"></td></tr>',
78 u'<input type=hidden value="show" name="action">',
79 u'</form>',
80 u'</table>',
81 u'</div>',
82 ]
83
84 return u'\n'.join(html) + u'\n'
85
86 def addcomment(macro, comauthor, comtext, compasswd):
87 # Add a comment with inputs
88
89 com_delimeter = Globs.com_delimeter
90 cfg = macro.request.cfg
91
92 try:
93 # If the data file exists
94 inx_file = codecs.open(Globs.attachmentdir+'/pagecommentsindex.txt','r+',encoding='utf-8')
95 except:
96 try:
97 # If the directory does not exist
98 if not os.path.isdir(Globs.attachmentdir):
99 # Create a attachments directory first
100 # Is conflict with moinmoin?
101 os.mkdir(Globs.attachmentdir)
102
103 inx_file = codecs.open(Globs.attachmentdir+'/pagecommentsindex.txt','w',encoding='utf-8')
104
105 except:
106 message('Failed to add the comment (unable to create an index file)')
107 return
108
109 try:
110 cur_index = inx_file.readline()
111 cur_index = int(cur_index)
112 except:
113 cur_index = 0
114
115 cur_index = cur_index + 1
116 cur_index = str(cur_index)
117
118 try:
119 out_file = codecs.open(Globs.attachmentdir+'/pagecomments.txt','a+',encoding='utf-8')
120 except:
121 message('Failed to add the comment (unable to create a data file)')
122 return
123
124 commentitem = [
125 'o',
126 com_delimeter,
127 cur_index,
128 com_delimeter,
129 convertdelimiter(comauthor),
130 com_delimeter,
131 convertdelimiter(comtext),
132 com_delimeter,
133 compasswd,
134 com_delimeter,
135 macro.request.user.host(),
136 com_delimeter,
137 time.strftime(cfg.datetime_fmt, time.localtime(time.time())),
138 #str('%s %s/%s %s:%s' % (cur_year, cur_month, cur_day, cur_time, cur_min)),
139 ]
140
141 commentitem = u''.join(commentitem)
142
143 out_file.write(commentitem + u'\r\n')
144 out_file.close()
145
146 inx_file.seek(0)
147 inx_file.write(cur_index)
148 inx_file.close()
149
150 message('The comment is added')
151
152 def showcomment():
153
154 html = ''
155 com_delimeter = Globs.com_delimeter
156
157 if not os.path.isfile(Globs.attachmentdir+'/pagecomments.txt'):
158 return ''
159
160 try:
161 com_file = codecs.open(Globs.attachmentdir+'/pagecomments.txt','r', encoding='utf-8')
162 except:
163 #return u'<div=commentlist><table><tr><td style="border: 0px;">no comments</td></tr></td></table></div>'
164 return ''
165
166 html = u'<div=commentlist><table width=100%>'
167
168 #cur_pos = 0
169
170 while 1:
171 in_line = com_file.readline()
172 #cur_pos = cur_pos + len(in_line.encode('utf-8'))
173
174 if in_line == "":
175 break
176
177 if in_line[0] == 'x':
178 if Globs.admin == 'true':
179 item_style = 'color: #c0c0c0;'
180 else:
181 continue
182 else:
183 item_style = ''
184
185 try:
186 in_line = in_line[:-2]
187 [flg_active, cur_index,comauthor,comtext,compasswd,comhost,comtime] = string.split(in_line,com_delimeter)
188 except:
189 message('Failed to show the comment (the data file may be corrupt)')
190 return ''
191
192 if Globs.admin == 'true':
193 signature = comhost + ' | ' + comtime
194 else:
195 signature = comtime
196
197 htmlcomment = [
198 u'<tr><td colspan=2 style="border: 0px; height:0.2px; background-color: #c0c0c0;"></td></tr>',
199 u'<tr><td style="border: 0px; width: 9em; vertical-align: top; ' + item_style + '">',
200 #u'(' + str(cur_pos) + ')',
201 converttext(comauthor),
202 u'</td><td style="border: 0px; ' + item_style + '">',
203 converttext(comtext),
204 u' <font style="font-size: 0.8em; color: #999999;">(',
205 signature,
206 u')</font>',
207 ]
208
209 htmlcomment = u'\n'.join(htmlcomment)
210
211 if Globs.admin == 'true' and flg_active == 'x':
212
213 htmlcomment2 = [
214 u'<font style="font-size: 0.9em;">',
215 u'<a style="color: #aa0000;" href="javascript: requesttorestore(\'' + cur_index + u'\');" alt="Restore">o</a>',
216 u'</font></td></tr>'
217 ]
218
219 else:
220
221 htmlcomment2 = [
222 u'<font style="font-size: 0.9em;">',
223 u'<a style="color: #aa0000;" href="javascript: requesttodelete(\'' + cur_index + u'\');" alt="Delete">x</a>',
224 u'</font></td></tr>'
225 ]
226
227 htmlcomment2 = u'\n'.join(htmlcomment2)
228 html = html + htmlcomment + htmlcomment2 + '\n'
229
230 com_file.close()
231
232 html = html + u'</table></div>'
233
234 return html
235
236
237 def execute(macro, args):
238
239 Globs.version=__version__
240
241 # Containers
242 formvals={}
243 try:
244 import wikiconfig
245 except:
246 wikiconfig=''
247
248 # Class variables need to be specifically set
249 Globs.attachmentdir=''
250 Globs.admin=''
251 Globs.adminmsg=''
252 Globs.pagename=''
253 Globs.com_delimeter = ' {||} '
254
255 # process arguments
256 if args:
257 sargs=string.split(args,',')
258 for item in sargs:
259 sitem=string.split(item,'=')
260 if len(sitem)==2:
261 key,value=sitem[0],sitem[1]
262
263 # Useful variables
264 Globs.baseurl=macro.request.getBaseURL()+'/'
265 if not Globs.pagename:
266 #Globs.pagename = string.replace(macro.formatter.page.page_name,'/','_2f')
267 Globs.pagename = macro.formatter.page.page_name
268 # This fixes the subpages bug. subname is now used instead of pagename when creating certain urls
269 Globs.subname = string.split(Globs.pagename,'/')[-1]
270 # Hmmm. A bug in moinmoin? underscores are getting escaped. These doubly escaped pagenames are even appearing in data/pages
271 try:
272 # Try the old MoinMoin-1.2.x way first
273 textdir=config.text_dir
274 pagepath = string.replace(wikiutil.getPagePath(Globs.pagename),'_5f','_')
275 except:
276 pagepath = macro.formatter.page.getPagePath()
277 Globs.attachmentdir = pagepath+'/attachments'
278
279 if args:
280 args=macro.request.getText(args)
281
282 for item in macro.form.items():
283 if not formvals.has_key(item[0]):
284 try:
285 formvals[item[0]]=item[1][0]
286 except AttributeError:
287 pass
288
289 # Figure out if we have delete privs
290 try:
291 # If a user can delete the page containing the PageComment, then they are considered a PageComment administrator
292 if macro.request.user.may.delete(macro.formatter.page.page_name):
293 Globs.admin='true'
294 except AttributeError:
295 pass
296
297 out=StringIO.StringIO()
298
299 comauthor = ''
300 comtext = ''
301 compasswd = ''
302
303 tmpauthor = ''
304 tmptext = ''
305 tmppasswd = ''
306
307 message(Globs.welcome_msg)
308
309 if formvals.has_key('comauthor') or formvals.has_key('comtext') or formvals.has_key('compasswd'):
310
311 all_input = 1
312
313 try:
314 comauthor = formvals['comauthor']
315 except:
316 comauthor = ''
317 all_input = 0
318
319 try:
320 comtext = formvals['comtext']
321 except:
322 comtext = ''
323 all_input = 0
324
325 try:
326 compasswd = formvals['compasswd']
327 except:
328 compasswd = ''
329 all_input = 0
330
331 try:
332 if all_input == 1:
333 addcomment(macro, comauthor, comtext, compasswd)
334 else:
335 message('Failed to add the comment (insufficient input)')
336
337 tmpauthor = comauthor
338 tmptext = comtext
339 tmppasswd = compasswd
340
341 except:
342 message('Failed to add the comment (internal error)')
343
344 tmpauthor = comauthor
345 tmptext = comtext
346 tmppasswd = compasswd
347
348 if formvals.has_key('delindex'):
349
350 try:
351 delindex = formvals['delindex']
352 delpasswd = formvals['delpasswd']
353 delaction = formvals['delaction']
354
355 deletecomment(macro, delindex, delpasswd, delaction)
356 except:
357 message('Failed to delete the comment (internal error or insufficient input)')
358
359 out.write(deleteform())
360 out.write(u'<table><tr><td style="border: 0px;">')
361 out.write(Globs.adminmsg)
362 out.write(commentform(tmpauthor, tmptext, tmppasswd))
363 out.write(u'</td></tr><tr><td style="border: 0px; height: 0.5em;"></td></tr><tr><td style="border: 0px;">')
364 out.write(showcomment())
365 out.write(u'</td></tr></table>')
366
367
368 out.seek(0)
369 # Finally output any administrative messages at the top followed by any generated content
370 return macro.formatter.rawHTML(
371 out.read()
372 )
373
374 def converttext(targettext):
375 # Converts some special characters of html to plain-text style
376 # What else to handle?
377
378 targettext = targettext.replace(u'&', '&')
379 targettext = targettext.replace(u'>', '>')
380 targettext = targettext.replace(u'<', '<')
381 targettext = targettext.replace(u'\n', '<br>')
382 targettext = targettext.replace(u'\t', ' ')
383 targettext = targettext.replace(u' ', ' ')
384
385 return targettext
386
387 def convertdelimiter(targettext):
388 # Converts delimeter to other string to avoid a crash
389
390 targettext = targettext.replace(Globs.com_delimeter, '(del)')
391
392 return targettext
393
394
395 def deleteform():
396 # Javascript codes for deleting or restoring a comment
397
398 html = [
399 '<script language=javascript>',
400 '<!--',
401 ]
402 html = '\n'.join(html)
403
404 if Globs.admin == 'true':
405 html2 = [
406 ' function requesttodelete(comindex) {',
407 ' document.delform.delindex.value = comindex;',
408 ' document.delform.delpasswd.value = "****";',
409 ' document.delform.delaction.value = "delete";',
410 ' document.delform.submit();',
411 ' }',
412 ' function requesttorestore(comindex) {',
413 ' document.delform.delindex.value = comindex;',
414 ' document.delform.delpasswd.value = "****";',
415 ' document.delform.delaction.value = "restore";',
416 ' document.delform.submit();',
417 ' }',
418 ]
419 html2 = '\n'.join(html2)
420 else:
421 html2 = [
422 ' function requesttodelete(comindex) {',
423 ' var passwd = prompt("Enter password:", "");',
424 ' if(!(passwd == "" || passwd == null)) {',
425 ' document.delform.delindex.value = comindex;',
426 ' document.delform.delpasswd.value = passwd;',
427 ' document.delform.delaction.value = "delete";',
428 ' document.delform.submit();',
429 ' }',
430 ' }',
431 ]
432 html2 = '\n'.join(html2)
433
434 html3 = [
435 '//-->',
436 '</script>',
437 '<form name="delform" action="'+Globs.subname+'" METHOD="post"> <input type=hidden value="show" name="action">',
438 '<input name="delpasswd" type="hidden" value="****"><input name="delindex" type="hidden" value=""><input name="delaction" type="hidden" value=""> </form>',
439 ]
440 html3 = '\n'.join(html3)
441
442 return '\n' + html + '\n' + html2 + '\n' + html3
443
444 def deletecomment(macro, delindex, delpasswd, delaction):
445 # Deletes or restores a comment with give index and password
446
447 html = ''
448 com_delimeter = Globs.com_delimeter
449
450 try:
451 com_file = codecs.open(Globs.attachmentdir+'/pagecomments.txt','r+',encoding='utf-8')
452 except:
453 message('No such comment')
454 return
455
456 delindex = int(delindex)
457
458 if delindex < 1:
459 message('No such comment')
460 return
461
462 cur_byte = 0
463 selectedcomment = ''
464
465 for x in range(delindex):
466 cur_byte = cur_byte + len(selectedcomment.encode('utf-8'))
467 selectedcomment = com_file.readline()
468
469 if selectedcomment == "":
470 message('No such comment')
471 return
472
473 if selectedcomment[0] == 'x' and delaction == 'delete':
474 message('The comment is already deleted')
475 return
476
477 if Globs.admin == 'true' and selectedcomment[0] == 'o' and delaction == 'restore':
478 message('The comment is already restored')
479 return
480
481 selectedcomment = selectedcomment[:-2]
482 [flg_active, cur_index,comauthor,comtext,compasswd,comhost,comtime] = string.split(selectedcomment,com_delimeter)
483
484 if Globs.admin != 'true' and compasswd != delpasswd:
485 message('Failed to delete the comment (incorrect password)')
486 return
487
488 com_file.seek(cur_byte)
489 if Globs.admin == 'true' and delaction == 'restore':
490 com_file.write(u'o')
491 message('The comment is restored')
492 else:
493 com_file.write(u'x')
494 message('The comment is deleted')
495
496 com_file.close()
497
498 return
499
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.