Attachment 'PageComment2-090.py'

Download

   1 # -*- coding: iso-8859-1 -*-
   2 """
   3     PageComment2.py  Version 0.90  Nov. 18, 2005
   4                                                                                                            
   5     This macro gives a form to post a new comment to the page and shows 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: [[PageComment2]]
  11 
  12     Features:
  13         
  14         Simple usage, just put [[PageComment2]] 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 
  21     Parameters:
  22         public: if the list of comments is shown to public users
  23             - public=1; default, all list is shown to all users including anonymous users
  24             - public=0; shown to only admin users (who has the page delete privilege)
  25             
  26         countonly: returns the number of the comments posted to this page
  27             - countonly=0; default, normal form (input form; list of comments)
  28             - countonly=1; just return the number of comments. e.g., 'There are [[PageComments(countonly=1)]] comments here'
  29     
  30     Change Log
  31 
  32         Nov. 18, 2005 - Version 0.90 (Release 2)
  33             - No text data file support any more: Comment is stored in the sub wiki page.
  34             - (does not compatible with Release 1: PageComment.py)
  35             - Custom icon (smiley) can be inserted
  36             - Pre-fill the name input field with his/her login name
  37             - Logs at add/remove comments
  38             - Added some parameters    
  39         Oct. 08, 2005 - Version 0.82
  40             - Changed the directory the data file stored to be secured
  41         
  42         Oct. 07, 2005 - Version 0.81 
  43             - Unicode encoding related bugs in deletecomment function are patched. 
  44             - Instruction bugs are patched. 
  45         
  46         Oct. 06, 2005 - Version 0.80 
  47             - The initial version is released.
  48 
  49 
  50     Notes
  51         
  52         'Gallery.py' developed by Simon Ryan has inspired this macro.
  53    
  54 
  55 """
  56 
  57 from MoinMoin import wikiutil
  58 import StringIO, time, re
  59 from MoinMoin.Page import Page
  60 from MoinMoin.PageEditor import PageEditor
  61 from MoinMoin.parser import wiki
  62 
  63 
  64 class Globs:
  65     # A quick place to plonk those shared variables
  66     adminmsg = ''
  67     datapagename = ''
  68     pagename = ''
  69     subname = ''
  70     admin = ''
  71     macro = ''
  72     defaultacl = ''
  73     defaulticon = ':)'
  74     
  75 class Params:
  76     public = 1
  77     countonly = 0
  78 
  79 def execute(macro, args):
  80 
  81     # INITIALIZATION ----------------------------------------
  82     setglobalvalues(macro)
  83     
  84     getparams(args)
  85     
  86     if Params.countonly:
  87         html = len(fetchcomments())
  88         return macro.formatter.rawHTML('%s' % html)
  89     
  90     # internal variables
  91     request = macro.request
  92     datapagename = Globs.datapagename
  93     
  94     _ = request.getText
  95     
  96     # form vals
  97     comicon = Globs.defaulticon
  98     comauthor = ''
  99     comtext = ''
 100     compasswd = ''
 101     comrev = 0
 102     
 103     action = macro.form.get('commentaction', [''])[0]
 104     
 105     if action == 'addcomment':
 106     
 107         # process form input for comment add
 108         form_fields = {'comicon': Globs.defaulticon, 'comauthor': '', 'comtext': '', 'compasswd': '', 'comrev': 0}
 109         required_fields = {'comauthor': _('Name'), 'comtext': _('Text'), 'compasswd': _('Password'), 'comrev': 'Rev. #'}
 110         
 111         formvals, missingfields = getforminput(macro.form, form_fields, required_fields)
 112         
 113         comicon = formvals['comicon']
 114         comauthor = formvals['comauthor']
 115         comtext = formvals['comtext']
 116         compasswd = formvals['compasswd']
 117         comrev = formvals['comrev']
 118     
 119         if not len(missingfields) == len(required_fields):
 120             if not missingfields:
 121                 addcomment(macro, comicon, comauthor, comtext, compasswd, comrev)
 122                 
 123                 comicon = Globs.defaulticon
 124                 comauthor = ''
 125                 comtext = ''
 126                 compasswd = ''
 127                 comrev = ''
 128             else:
 129                 message( _('Required attribute "%(attrname)s" missing') % { 'attrname': u', '.join(missingfields) } )
 130     
 131     elif action == 'delcomment':
 132     
 133         # process form input for comment delete
 134         form_fields = {'delkey': '', 'delpasswd': ''}
 135         required_fields = {'delkey': 'Comment Key', 'delpasswd': 'Password'}
 136         
 137         formvals, missingfields = getforminput(macro.form, form_fields, required_fields)
 138         
 139         delkey = formvals['delkey']
 140         delpasswd = formvals['delpasswd']
 141         
 142         if not len(missingfields) == len(required_fields):
 143             if not missingfields:
 144                 deletecomment(macro, delkey, delpasswd)
 145             else:
 146                 message( _('Required attribute "%(attrname)s" missing') % { 'attrname': u', '.join(missingfields) } )
 147     
 148     # format output
 149     html = []
 150     
 151     html.append(u'<a name="pagecomment">')
 152     html.append(u'<table class="pagecomment">')
 153     html.append(u'<tr><td style="border: 0px;">')
 154     html.append(u'<font style="color: #aa0000;">%s</font>' % Globs.adminmsg)
 155     html.append(commentform(comauthor, comtext, compasswd, comicon, comrev))
 156     html.append(u'</td></tr>')
 157     
 158     if Params.public or Globs.admin:
 159         html.append(deleteform())
 160         html.append(u'<tr><td style="border: 0px;">')
 161         html.append(showcomment())
 162         html.append(u'</td></tr>')
 163     else:
 164         html.append(u'<tr><td style="text-align: center; border: 0px; font-size: 0.8em; color: #aaaaaa;">(The posted comments are shown to administrators only.)</td></tr>')
 165 
 166     html.append(u'</table>')
 167     
 168     return macro.formatter.rawHTML(u'\n'.join(html))
 169 
 170 def getforminput(form, inputfields, requiredfields):
 171     
 172     formvals = {}
 173     missingfields = []
 174     
 175     for item in inputfields.keys():
 176         if (not form.has_key(item)) and (item in requiredfields):
 177             missingfields.append(requiredfields[item])
 178         formvals[item] = form.get(item, [inputfields[item]])[0]
 179         
 180     return formvals, missingfields
 181 
 182 def getparams(args):
 183     # process arguments
 184     
 185     params = {}
 186     if args:
 187         # Arguments are comma delimited key=value pairs
 188         sargs = args.split(',')
 189     
 190         for item in sargs:
 191             sitem = item.split('=')
 192         
 193             if len(sitem) == 2:
 194                 key, value = sitem[0], sitem[1]
 195                 params[key.strip()] = value.strip()
 196 
 197     try:
 198         Params.public = int(params.get('public', 1))
 199     except ValueError:
 200         Params.public = 1
 201 
 202     try:
 203         Params.countonly = int(params.get('countonly', 0))
 204     except ValueError:
 205         Params.countonly = 0
 206 
 207 def setglobalvalues(macro):
 208     
 209     # Global variables
 210     Globs.pagename = macro.formatter.page.page_name
 211     Globs.subname = Globs.pagename.split('/')[-1]
 212     Globs.macro = macro
 213     Globs.datapagename = u'%s/%s' % (Globs.pagename, 'PageCommentData')
 214     Globs.defaultacl = u'#acl All:'
 215     
 216     # Figure out if we have delete privs
 217     try:
 218         if macro.request.user.may.delete(Globs.datapagename):
 219             Globs.admin = 'true'
 220     except AttributeError:
 221         pass
 222 
 223 
 224 def message(astring):
 225     Globs.adminmsg += u'%s<br>\n' % astring
 226 
 227 
 228 def commentform(tmpauthor, tmptext, tmppasswd, tmpicon, comrev):
 229     # A form for posting a new comment
 230     request = Globs.macro.request
 231     datapagename = Globs.datapagename
 232     _ = request.getText
 233     
 234     pg = Page( request, datapagename )
 235     
 236     if pg.exists():
 237         comrev = pg.current_rev()
 238     else:
 239         comrev = 0
 240     
 241     if request.user.valid:
 242         html1 = [
 243             u'<input type="hidden" value="%s" name="comauthor">' % request.user.name,
 244 		    u'<input type="hidden" value="*" name="compasswd">',
 245             u'<tr><td>%s: %s</td>' % (_('Name'), request.user.name),
 246     		u'<td>%s: ****</td>' % _('Password'),
 247     		]
 248     else:
 249         html1 = [
 250             u'<tr><td>%s: <input type="text" size="10" maxlength="20" name="comauthor" value="%s"></td>' % (_('Name'), tmpauthor),
 251     		u'<td>%s: <input type="password" size="6" maxlength="10" name="compasswd" value="%s"></td>' % (_('Password'), tmppasswd),
 252     		]
 253     
 254     html1 = u'\n'.join(html1)
 255     html2 = [
 256         u'<div id="commentform">',
 257         u'<form action="%s#pagecomment" name="comment" METHOD="POST">' % Globs.subname,
 258         u'<table class="addcommentform">',
 259         u'%s' % html1,
 260 		u'<td>Smiley: <input type="text" size="4" maxlength="4" name="comicon" value="%s"></td></tr>' % tmpicon,
 261 		u'<tr><td colspan="3"><textarea name="comtext" rows="2" cols="60">%s</textarea></td></tr>' % tmptext,
 262         u'<tr><td colspan="3" align="center"><input type="submit" value="%s"></td></tr>' % _('Save'),
 263 		u'</table>',
 264 		u'<input type="hidden" value="show" name="action">',
 265 		u'<input type="hidden" value="%s" name="comrev">' % comrev,
 266 		u'<input type="hidden" value="addcomment" name="commentaction">',
 267 		u'</form>',
 268 		u'</div>',
 269         ]
 270     
 271     
 272     return u'\n'.join(html2)
 273       
 274 def addcomment(macro, comicon, comauthor, comtext, compasswd, comrev):
 275     # Add a comment with inputs
 276     
 277     request = Globs.macro.request
 278     cfg = request.cfg
 279     _ = request.getText
 280     
 281     datapagename = Globs.datapagename
 282     
 283     pg = PageEditor( request, datapagename )
 284     pagetext = pg.get_raw_body()
 285     
 286     comicon = convertdelimiter(comicon)
 287     comtext = convertdelimiter(comtext)
 288     
 289     if request.user.valid:
 290         comloginuser = 'TRUE'
 291         comauthor = request.user.name
 292     else:
 293         comloginuser = ''
 294         comauthor = convertdelimiter(comauthor)
 295     
 296     newcomment = [
 297         u'{{{',
 298         u'%s' % comicon,
 299         u'%s' % comauthor,
 300         u'%s' % time.strftime(cfg.datetime_fmt, time.localtime(time.time())),
 301         u'',
 302         u'%s' % comtext.strip(),
 303         u'}}}',
 304         u'##PASSWORD %s' % compasswd,
 305         u'##LOGINUSER %s' % comloginuser,
 306         ]
 307         
 308     newpagetext = u'%s\n\n%s' % (pagetext, u'\n'.join(newcomment))
 309 
 310     if not pg.exists():
 311         action = 'SAVENEW'
 312         defaultacl = Globs.defaultacl
 313         newpagetext = u'%s\n%s' % (defaultacl, newpagetext)
 314     else:
 315         action = 'SAVE'
 316 
 317     newpagetext = pg.normalizeText( newpagetext )
 318     
 319     comment = 'New comment by "%s"' % comauthor
 320     pg._write_file(newpagetext, action, u'Modified by PageComment macro')
 321     addLogEntry(request, 'COMNEW', Globs.pagename, comment)
 322     
 323     # message(_('The comment is added'))
 324     message(_('Thank you for your changes. Your attention to detail is appreciated.'))
 325         
 326 
 327 def showcomment():
 328     
 329     request = Globs.macro.request
 330     _ = request.getText
 331     
 332     commentlist = fetchcomments()
 333     
 334     html = []
 335     cur_index = 0
 336     linestyle = u'border-width: 0px; border-top-width: 1px; vertical-align: top; font-size: 0.9em;'
 337     
 338     html.append(u'<div id="commentlist"><table width="100%" class="commentlist">')
 339     
 340     for item in commentlist:
 341         if Globs.admin or (item['loginuser'] and request.user.valid and request.user.name == item['name']):
 342             htmlcommentdel = [
 343                 u' <font style="font-size: 0.9em;">',
 344                 u'<a style="color: #aa0000;" href="javascript: requesttodeleteadmin(\'%s\');" title="%s">X</a>' % (item['key'], _('Delete')),
 345                 u'</font>',
 346                 ]
 347         elif item['loginuser']:
 348             htmlcommentdel = []
 349 
 350         else:
 351             htmlcommentdel = [
 352                 u' <font style="font-size: 0.9em;">',
 353                 u'<a style="color: #aa0000;" href="javascript: requesttodelete(\'%s\');" title="%s">X</a>' % (item['key'], _('Delete')),
 354                 u'</font>',
 355                 ]
 356         
 357         htmlcomment = [
 358             u'<tr><td class="commenticon" style="%s width: 20px;">%s</td>' % (linestyle, getparsedtext(item['icon'])),
 359             u'<td class="commentauthor" style="%s"' % linestyle,
 360             u'>%s</td>' % converttext(item['name']),
 361             u'<td class="commenttext" style="%s">%s</td>' % (linestyle, converttext(item['text'])),
 362             u'<td class="commentdate" style="%s text-align: right; font-size: 0.8em; line-height: 10px;">%s %s</td></tr>' % (linestyle, item['date'], u''.join(htmlcommentdel)),
 363             ]
 364         
 365         html.append(u'\n'.join(htmlcomment))
 366     
 367     html.append(u'</table></div>')
 368     
 369     return u'\n'.join(html)
 370 
 371 
 372 def getparsedtext(targettext):
 373     request = Globs.macro.request
 374     formatter = Globs.macro.formatter
 375     
 376     out = StringIO.StringIO()
 377     request.redirect(out)
 378     wikiizer = wiki.Parser(targettext, request)
 379     wikiizer.format(formatter)
 380     parsedtext = out.getvalue()
 381     request.redirect()
 382     del out
 383     
 384     return parsedtext
 385 
 386 
 387 def converttext(targettext):
 388     # Converts some special characters of html to plain-text style
 389     # What else to handle?
 390 
 391     targettext = targettext.strip()
 392     targettext = targettext.replace(u'&', '&amp')
 393     targettext = targettext.replace(u'>', '&gt;')
 394     targettext = targettext.replace(u'<', '&lt;')
 395     targettext = targettext.replace(u'\n', '<br>')
 396     targettext = targettext.replace(u'"', '&#34;')
 397     targettext = targettext.replace(u'\t', '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;')
 398     targettext = targettext.replace(u'  ', '&nbsp;&nbsp;')
 399 
 400     return targettext
 401     
 402 def convertdelimiter(targettext):
 403     # Converts delimeter to other string to avoid a crash
 404     
 405     targettext = targettext.replace('{{{', '{ { {')
 406     targettext = targettext.replace('}}}', '} } }')
 407     
 408     return targettext
 409 
 410     
 411 def deleteform():
 412     # Javascript codes for deleting or restoring a comment
 413     
 414     request = Globs.macro.request
 415     _ = request.getText
 416     
 417     htmlresult = []
 418     
 419     html = [
 420         '<script language="javascript">',
 421         '<!--',
 422         ]
 423     htmlresult.append(u'\n'.join(html))
 424            
 425     html = [    
 426         '  function requesttodeleteadmin(comkey) {',
 427         '      if (confirm("%s")) {;' % _('Really delete this page?'),
 428         '          document.delform.delkey.value = comkey;',
 429         '          document.delform.delpasswd.value = "****";',
 430         '          document.delform.submit();',
 431         '      }',
 432         '  }',
 433         '  function requesttodelete(comkey) {',
 434         '      var passwd = prompt("%s:", "");' % _('Please specify a password!'),
 435         '      if(!(passwd == "" || passwd == null)) {',
 436         '          document.delform.delkey.value = comkey;',
 437         '          document.delform.delpasswd.value = passwd;',
 438         '          document.delform.submit();',
 439         '      }',
 440         '  }',
 441         ]
 442     
 443     htmlresult.append(u'\n'.join(html))
 444                 
 445     html = [
 446         '//-->',
 447         '</script>',
 448         '<form name="delform" action="%s#pagecomment" METHOD="post">' % Globs.subname,
 449         '<input type=hidden value="show" name="action">',
 450         '<input name="delpasswd" type="hidden" value="****">',
 451         '<input name="delkey" type="hidden" value="">',
 452         '<input type="hidden" name="commentaction" value="delcomment">',
 453         '</form>',
 454         ]
 455     htmlresult.append(u'\n'.join(html))
 456 
 457     return u'\n'.join(htmlresult)
 458 
 459 
 460 def filtercomment(index='', name='', passwd=''):
 461     
 462     # filter by index
 463     if index:
 464         filteredlist1 = fetchcomments(index, index)
 465     else:
 466         filteredlist1 = fetchcomments()
 467     
 468     # filter by name
 469     filteredlist2 = []
 470     if name:
 471         for item in filteredlist1:
 472             if name == item['name']:
 473                 filteredlist2.append(item)
 474     else:
 475         filteredlist2 = filteredlist1
 476     
 477     # filter by password
 478     filteredlist3 = []
 479     if passwd:
 480         for item in filteredlist2:
 481             if passwd == item['passwd']:
 482                 filteredlist3.append(item)
 483     else:
 484         filteredlist3 = filteredlist2
 485 
 486     return filteredlist3
 487         
 488 
 489 def fetchcomments(startindex=1, endindex=9999):
 490     
 491     commentlist = []
 492     
 493     request = Globs.macro.request
 494     formatter = Globs.macro.formatter
 495     datapagename = Globs.datapagename
 496 
 497     pg = Page( request, datapagename )
 498     pagetext = pg.get_raw_body()
 499     
 500     regex = r'^(#acl\s*.*)$'
 501     pattern = re.compile(regex, re.UNICODE + re.MULTILINE + re.IGNORECASE)
 502     pagetext = pattern.sub('', pagetext)
 503     
 504     regex = ur"""
 505 ^[\{]{3}\n
 506 ^(?P<icon>[^\n]*)\n
 507 ^(?P<name>[^\n]*)\n
 508 ^(?P<date>[^\n]*)[\n]+
 509 ^(?P<text>\s*.*?[^}]+)[\}]{3}\n
 510 ^[#]{2}PASSWORD[ ](?P<passwd>[^\n]*)\n
 511 ^[#]{2}LOGINUSER[ ](?P<loginuser>[^\n]*)[\n$]+"""
 512 
 513     pattern = re.compile(regex, re.UNICODE + re.MULTILINE + re.VERBOSE)
 514     commentitems = pattern.findall(pagetext)
 515     
 516     cur_index = 0
 517     for item in commentitems:
 518         comment = {}
 519         cur_index += 1
 520         
 521         if cur_index < startindex:
 522             continue
 523         
 524         comment['index'] = cur_index
 525         comment['icon'] = item[0]
 526         comment['name'] = item[1]
 527         comment['text'] = item[3]
 528         comment['date'] = item[2]
 529         comment['passwd'] = item[4]
 530         comment['loginuser'] = item[5]
 531         
 532         # experimental
 533         comment['key'] = comment['date'].strip()
 534         
 535         commentlist.append(comment)
 536         
 537         if cur_index >= endindex:
 538             break
 539 
 540     return commentlist
 541 
 542 def deletecomment(macro, delkey, delpasswd):
 543     # Deletes a comment with given index and password
 544     
 545     request = Globs.macro.request
 546     formatter = Globs.macro.formatter
 547     datapagename = Globs.datapagename
 548     _ = request.getText
 549 
 550     pg = PageEditor( request, datapagename )
 551     pagetext = pg.get_raw_body()
 552     
 553     regex = ur"""
 554 (?P<comblock>^[\{]{3}\n
 555 ^(?P<icon>[^\n]*)\n
 556 ^(?P<name>[^\n]*)\n
 557 ^(?P<date>[^\n]*)[\n]+
 558 ^(?P<text>\s*.*?[^}]+)[\}]{3}\n
 559 ^[#]{2}PASSWORD[ ](?P<passwd>[^\n]*)\n
 560 ^[#]{2}LOGINUSER[ ](?P<loginuser>[^\n]*)[\n$]+)"""
 561 
 562     pattern = re.compile(regex, re.UNICODE + re.MULTILINE + re.VERBOSE)
 563     commentitems = pattern.findall(pagetext)
 564     
 565     for item in commentitems:
 566         
 567         if delkey == item[3].strip():
 568             comauthor = item[2]
 569             if Globs.admin or (request.user.valid and request.user.name == comauthor) or delpasswd == item[5]:
 570                 newpagetext = pagetext.replace(item[0], '', 1)
 571                 
 572                 action = 'SAVE'
 573                 comment = 'comment deleted by "%s"' % comauthor
 574                 pg._write_file(newpagetext, action, u'Modified by PageComment macro')
 575                 addLogEntry(request, 'COMDEL', Globs.pagename, comment)
 576                 
 577                 message(_('The comment is deleted'))
 578                 
 579                 return
 580             else:
 581                 message(_('Sorry, wrong password.'))
 582                 return
 583                 
 584     message(_('No such comment'))
 585 
 586     
 587 def addLogEntry(request, action, pagename, msg):
 588     # Add an entry to the edit log on adding comments.
 589     from MoinMoin.logfile import editlog
 590     t = wikiutil.timestamp2version(time.time())
 591     msg = unicode(msg)
 592 
 593     # TODO: for now we simply write 2 logs, maybe better use some multilog stuff
 594     # Write to global log
 595     log = editlog.EditLog(request)
 596     log.add(request, t, 99999999, action, pagename, request.remote_addr, msg)
 597 
 598     # Write to local log
 599     log = editlog.EditLog(request, rootpagename=pagename)
 600     log.add(request, t, 99999999, action, pagename, request.remote_addr, msg)

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.
  • [get | view] (2008-04-30 13:57:43, 45.4 KB) [[attachment:PageComment2-0.981-only_logged_in_user.py]]
  • [get | view] (2005-11-18 08:51:22, 19.1 KB) [[attachment:PageComment2-090.py]]
  • [get | view] (2005-11-18 16:50:49, 21.6 KB) [[attachment:PageComment2-091.py]]
  • [get | view] (2005-11-19 06:31:25, 21.8 KB) [[attachment:PageComment2-092.py]]
  • [get | view] (2005-11-20 07:24:45, 26.0 KB) [[attachment:PageComment2-094.py]]
  • [get | view] (2005-11-20 08:56:20, 26.1 KB) [[attachment:PageComment2-095.py]]
  • [get | view] (2005-11-29 20:06:02, 31.4 KB) [[attachment:PageComment2-096.py]]
  • [get | view] (2006-01-05 08:41:48, 44.6 KB) [[attachment:PageComment2-097.py]]
  • [get | view] (2006-04-17 13:35:45, 45.4 KB) [[attachment:PageComment2-098.py]]
  • [get | view] (2006-05-02 00:31:43, 45.4 KB) [[attachment:PageComment2-0981.py]]
  • [get | view] (2008-01-29 19:38:32, 45.4 KB) [[attachment:PageComment2-099-moin16.py]]
  • [get | view] (2007-05-01 15:51:21, 46.5 KB) [[attachment:PageComment2-099-rg.py]]
  • [get | view] (2008-02-19 19:41:42, 45.4 KB) [[attachment:PageComment2-0991-moin16.py]]
  • [get | view] (2008-02-27 14:38:41, 45.8 KB) [[attachment:PageComment2-0992-moin16.py]]
  • [get | view] (2011-07-19 13:47:24, 47.7 KB) [[attachment:PageComment2-0993-moin193.py]]
  • [get | view] (2007-05-01 15:52:09, 1.6 KB) [[attachment:PageComment2-981-099-rg.diff.gz]]
  • [get | view] (2005-11-29 19:59:21, 42.1 KB) [[attachment:snap-pagecomment2.jpg]]
 All files | Selected Files: delete move to page copy to page

You are not allowed to attach a file to this page.