# -*- coding: iso-8859-1 -*-
"""
    PageComment2.py  Version 0.94  Nov. 20, 2005
                                                                                                           
    This macro gives a form to post a new comment to the page and shows a list of the posted comments.
                                                                                                           
    @copyright: 2005 by Seungik Lee <seungiklee<at>gmail.com>  http://cds.icu.ac.kr/~silee/
    @license: GPL

    Usage: [[PageComment2]]

    Features:
        
        - Simple usage, just put [[PageComment2]] on any page.
        - Lets anonymous users post a new comment with an input form.
        - Shows a list of the posted comments.
        - Support for comment deletion by given password.
        - Support for administrative action, e.g., 
            - to delete a comment without entering a given password

    Parameters:

        - pagename: the page name which the comments are retrieved for. by default the page itself.
            If the user has no 'read' ACL for that page, it does not allow to insert/view comments.
            e.g., pagename=AnotherPage
        
        - section: the section name of the page. The comments in different sections are managed in separated sub pages.
            Section name should be alphanumeric format ([a-zA-Z0-9] in regular expression). 
            If not, all the non-alphanumric characters are removed.
            e.g., section=1, section=News, section=Opinion
            
        - inputonly: shows input form only. list of the comments are shown to admin users only.
            - inputonly=0; default, all list is shown to all users including anonymous users
            - inputonly=1; shown to admin users only (who has the page delete privilege)
            
        - commentonly: shows the list of comments only.
            - commentonly=0; default, both of the list and input form will be shown
            - commentonly=1; only the list of comments will be shown
                
        - countonly: returns the number of the comments posted to this page
            - countonly=0; default, normal form (input form; list of comments)
            - countonly=1; just return the number of comments. 
                e.g., 'There are [[PageComments(countonly=1)]] comments here'
    
        - rows: the # of rows of the textarea. default 2. e.g., rows=2
        
        - cols: the # of columns of the textarea. default 60. e.g., cols=60
        
        - maxlength: limitation on # of characters for comment text. default 0 (no limit). e.g., maxlength=500
        
        - newerfirst: order of the list of comments.
            - newerfirst=0: default, newer ones are listed at the end
            - newerfirst=1: newer ones are listed at the top
            
        - tablewidth: the width of the table format for PageComment2, default '' (none). 
            e.g., tablewidth=600, tablewidth=100%
    
    Change Log

        - Nov. 20, 2005 - Version 0.94
            - some parameters are added
            - some minor bugs are fixed
        
        - Nov. 19, 2005 - Version 0.92
            - some minor bugs are fixed
            - 'olderfirst' parameter replaced with 'newerfirst'
        
        - Nov. 19, 2005 - Version 0.91
            - some parameters are added
            - validates smiley markup
            - modified view
        
        - Nov. 18, 2005 - Version 0.90 (Release 2)
            - No text data file support any more: Comment is stored in the sub wiki page.
            - (does not compatible with Release 1: PageComment.py)
            - Custom icon (smiley) can be inserted
            - Pre-fill the name input field with his/her login name
            - Logs at add/remove comments
            - Added some parameters    
        
        - Oct. 08, 2005 - Version 0.82
            - Changed the directory the data file stored to be secured
        
        - Oct. 07, 2005 - Version 0.81 
            - Unicode encoding related bugs in deletecomment function are patched. 
            - Instruction bugs are patched. 
        
        - Oct. 06, 2005 - Version 0.80 
            - The initial version is released.


    Notes
        
        - 'Gallery.py' developed by Simon Ryan has inspired this macro.
        - Thanks to many of the MoinMoin users for valuable comments.
        - Visit http://moinmoin.wikiwikiweb.de/MacroMarket/PageComment2 for more detail

"""

from MoinMoin import config, wikiutil
import StringIO, time, re
from MoinMoin.Page import Page
from MoinMoin.PageEditor import PageEditor
from MoinMoin.parser import wiki


class Globs:
    # A quick place to plonk those shared variables
    
    adminmsg = ''
    datapagename = ''
    pagename = ''
    curpagename = ''
    cursubname = ''
    admin = ''
    macro = ''
    defaultacl = ''
    defaulticon = ''        
    formid = 0

class Params:

    rows = 0
    cols = 0
    maxlength = 0
    newerfirst = 0
    tablewidth = ''
    commentfirst = 0
    pagename = ''
    commentonly = 0
    inputonly = 0
    countonly = 0
    section = ''


def execute(macro, args):

    # INITIALIZATION ----------------------------------------
    getparams(args)
    setglobalvalues(macro)
    
    # internal variables
    request = macro.request
    _ = request.getText
    
    if not Globs.pagename == Globs.curpagename:
        if not macro.request.user.may.read(Globs.pagename):
            return macro.formatter.rawHTML(u'PageComment: %s' % _('You are not allowed to view this page.'))
        elif not Page(request, Globs.pagename).exists():
            return macro.formatter.rawHTML(u'PageComment: %s' % _('This page is already deleted or was never created!'))

    
    if Params.countonly:
        html = len(fetchcomments())
        return macro.formatter.rawHTML('%s' % html)
    
    datapagename = Globs.datapagename
    
    # form vals
    comicon = Globs.defaulticon
    comauthor = ''
    comtext = ''
    compasswd = ''
    comrev = 0
    
    addcommand = u'addcomment%d' % Globs.formid
    delcommand = u'delcomment%d' % Globs.formid
    
    action = macro.form.get('commentaction', [''])[0]
    
    if action == addcommand:
    
        # process form input for comment add
        form_fields = {'comicon': Globs.defaulticon, 'comauthor': '', 'comtext': '', 'compasswd': '', 'comrev': 0}
        required_fields = {'comauthor': _('Name'), 'comtext': _('Text'), 'compasswd': _('Password'), 'comrev': 'Rev. #'}
        
        formvals, missingfields = getforminput(macro.form, form_fields, required_fields)
        
        comicon = formvals['comicon']
        comauthor = formvals['comauthor']
        comtext = formvals['comtext']
        compasswd = formvals['compasswd']
        comrev = formvals['comrev']
    
        if not len(missingfields) == len(required_fields):
            if not missingfields:
                flag = addcomment(macro, comicon, comauthor, comtext, compasswd, comrev)
                
                if flag:
                    comicon = Globs.defaulticon
                    comauthor = ''
                    comtext = ''
                    compasswd = ''
                    comrev = 0
            else:
                message( _('Required attribute "%(attrname)s" missing') % { 'attrname': u', '.join(missingfields) } )
    
    elif action == delcommand:
    
        # process form input for comment delete
        form_fields = {'delkey': '', 'delpasswd': ''}
        required_fields = {'delkey': 'Comment Key', 'delpasswd': 'Password'}
        
        formvals, missingfields = getforminput(macro.form, form_fields, required_fields)
        
        delkey = formvals['delkey']
        delpasswd = formvals['delpasswd']
        
        if not len(missingfields) == len(required_fields):
            if not missingfields:
                deletecomment(macro, delkey, delpasswd)
            else:
                message( _('Required attribute "%(attrname)s" missing') % { 'attrname': u', '.join(missingfields) } )
    
    # format output
    html = []
    
    html.append(u'<a name="pagecomment%d">' % Globs.formid)
    html.append(u'<table class="pagecomment" %s>' % Params.tablewidth)
    html.append(u'<tr><td style="border-width: 0px;">')
    html.append(u'<font style="color: #aa0000;">%s</font>' % Globs.adminmsg)
    html.append(u'</td></tr>')

    if Params.commentfirst:
        html.append(showcommentsection())
        html.append(commentformsection(comauthor, comtext, compasswd, comicon, comrev))
    else:
        html.append(commentformsection(comauthor, comtext, compasswd, comicon, comrev))
        html.append(showcommentsection())

    html.append(u'</table>')
    
    return macro.formatter.rawHTML(u'\n'.join(html))


def commentformsection(comauthor, comtext, compasswd, comicon, comrev):
    html = []
    
    if not Params.commentonly:
        html.append(u'<tr><td style="border-width: 0px;">')
        html.append(u'<table class="commentform"><tr><td style="border-width: 1px;">')
        html.append(commentform(comauthor, comtext, compasswd, comicon, comrev))
        html.append(u'</td></tr></table>')
        html.append(u'</td></tr>')
    
    return u'\n'.join(html)


def showcommentsection():
    html = []
    if (not Params.inputonly) or Globs.admin:
        html.append(deleteform())
        html.append(u'<tr><td style="border: 0px;">')
        html.append(showcomment())
        html.append(u'</td></tr>')
    else:
        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>')

    return u'\n'.join(html)

def getforminput(form, inputfields, requiredfields):
    
    formvals = {}
    missingfields = []
    
    for item in inputfields.keys():
        if (not form.has_key(item)) and (item in requiredfields):
            missingfields.append(requiredfields[item])
        formvals[item] = form.get(item, [inputfields[item]])[0]
        
    return formvals, missingfields

def getparams(args):
    # process arguments
    
    params = {}
    if args:
        # Arguments are comma delimited key=value pairs
        sargs = args.split(',')
    
        for item in sargs:
            sitem = item.split('=')
        
            if len(sitem) == 2:
                key, value = sitem[0], sitem[1]
                params[key.strip()] = value.strip()

    Params.pagename = params.get('pagename', '')
    
    Params.section = params.get('section', '')
    if Params.section:
        Params.section = getescapedsectionname(Params.section)

    try:
        Params.inputonly = int(params.get('inputonly', 0))
    except ValueError:
        Params.inputonly = 0

    try:
        Params.commentonly = int(params.get('commentonly', 0))
    except ValueError:
        Params.commentonly = 0

    try:
        Params.countonly = int(params.get('countonly', 0))
    except ValueError:
        Params.countonly = 0

    try:
        Params.newerfirst = int(params.get('newerfirst', 0))
    except ValueError:
        Params.newerfirst = 0
        
    try:
        Params.commentfirst = int(params.get('commentfirst', 0))
    except ValueError:
        Params.commentfirst = 0
        
    try:
        Params.rows = int(params.get('rows', 2))
    except ValueError:
        Params.rows = 2

    try:
        Params.cols = int(params.get('cols', 60))
    except ValueError:
        Params.cols = 60

    try:
        Params.maxlength = int(params.get('maxlength', 0))
    except ValueError:
        Params.maxlength = 0
        
    Params.tablewidth = params.get('tablewidth', '')
    if Params.tablewidth:
        Params.tablewidth = ' width="%s" ' % Params.tablewidth

def setglobalvalues(macro):
    
    # Global variables
    Globs.macro = macro
    Globs.defaultacl = u'#acl All:'
    Globs.adminmsg = ''
    Globs.defaulticon = ''
    
    Globs.curpagename = macro.formatter.page.page_name
    
    if Params.pagename:
        Globs.pagename = Params.pagename
    else:
        Globs.pagename = Globs.curpagename
        
    Globs.cursubname = Globs.curpagename.split('/')[-1]
    Globs.datapagename = u'%s/%s%s' % (Globs.pagename, 'PageCommentData', Params.section)

    # Figure out if we have delete privs
    try:
        if macro.request.user.may.delete(Globs.datapagename):
            Globs.admin = 'true'
        else:
            Globs.admin = ''
    except AttributeError:
        Globs.admin = ''
        pass

    # set form id
    
    formid = int(macro.form.get('formid', ['0'])[0])
    formid += 1
    
    Globs.formid = formid
    macro.form['formid'] = ['%d' % formid]  

def message(astring):
    Globs.adminmsg = u'%s\n' % astring


def commentform(tmpauthor, tmptext, tmppasswd, tmpicon, comrev):
    # A form for posting a new comment
    request = Globs.macro.request
    datapagename = Globs.datapagename
    _ = request.getText
    
    cellstyle = u'border-width: 0px; vertical-align: middle; font-size: 0.9em; line-height: 1em;'
    
    pg = Page( request, datapagename )
    
    if pg.exists():
        comrev = pg.current_rev()
    else:
        comrev = 0
    
    if request.user.valid:
        html1 = [
            u'<input type="hidden" value="%s" name="comauthor">' % request.user.name,
		    u'<input type="hidden" value="*" name="compasswd">',
            u'<tr><td style="%s">%s: <i>%s</i></td>' % (cellstyle, _('Name'), request.user.name),
    		u'<td style="%s">%s: ****</td>' % (cellstyle, _('Password')),
    		]
    else:
        html1 = [
            u'<tr><td style="%s">%s: <input type="text" size="10" maxlength="20" name="comauthor" value="%s"></td>' % (cellstyle, _('Name'), tmpauthor),
    		u'<td style="%s">%s: <input type="password" size="6" maxlength="10" name="compasswd" value="%s"></td>' % (cellstyle, _('Password'), tmppasswd),
    		]
    
    html1 = u'\n'.join(html1)
    html2 = [
        u'<div id="commentform">',
        u'<form action="%s#pagecomment%d" name="comment" METHOD="POST">' % (Globs.cursubname, Globs.formid),
        u'<table class="addcommentform">',
        u'%s' % html1,
		# u'<td style="%s">Smiley: <input type="text" size="4" maxlength="4" name="comicon" value="%s"></td></tr>' % (cellstyle, tmpicon),
		u'<td style="%s">Smiley: %s</td></tr>' % (cellstyle, getsmileymarkuplist(tmpicon)),
		u'<tr><td colspan="3" style="%s"><textarea name="comtext" rows="%d" cols="%d">%s</textarea></td></tr>' % (cellstyle, Params.rows, Params.cols, tmptext),
        u'<tr><td colspan="3" align="center" style="%s"><input type="submit" value="%s"></td></tr>' % (cellstyle, _('Save')),
		u'</table>',
		u'<input type="hidden" name="action" value="show" >',
		u'<input type="hidden" name="comrev" value="%s">' % comrev,
		u'<input type="hidden" name="commentaction" value="addcomment%d">' % Globs.formid,
		u'</form>',
		u'</div>',
        ]
    
    
    return u'\n'.join(html2)
      
def addcomment(macro, comicon, comauthor, comtext, compasswd, comrev):
    # Add a comment with inputs
    
    request = Globs.macro.request
    cfg = request.cfg
    _ = request.getText
    
    # check input
    if comicon and (not comicon in config.smileys.keys()):
        message('Please use smiley markup only')
        return 0

    if Params.maxlength and (len(comtext) > Params.maxlength):
        message('Comment text is limited to %d characters. (%d characters now)' % (Params.maxlength, len(comtext)) )
        return 0
    
    if not comtext.strip():
        message('Please fill the comment text')
        return 0
    
    datapagename = Globs.datapagename
    
    pg = PageEditor( request, datapagename )
    pagetext = pg.get_raw_body()
    
    comtext = convertdelimiter(comtext)
    
    if request.user.valid:
        comloginuser = 'TRUE'
        comauthor = request.user.name
    else:
        comloginuser = ''
        comauthor = convertdelimiter(comauthor)
    
    newcomment = [
        u'{{{',
        u'%s' % comicon,
        u'%s' % comauthor,
        u'%s' % time.strftime(cfg.datetime_fmt, time.localtime(time.time())),
        u'',
        u'%s' % comtext,
        u'}}}',
        u'##PASSWORD %s' % compasswd,
        u'##LOGINUSER %s' % comloginuser,
        ]
        
    newpagetext = u'%s\n\n%s' % (pagetext, u'\n'.join(newcomment))

    if not pg.exists():
        action = 'SAVENEW'
        defaultacl = Globs.defaultacl
        warnmessages = '\'\'\'\'\'DO NOT EDIT THIS PAGE!!\'\'\' This page is automatically generated by Page``Comment macro.\'\'\n----'
        newpagetext = u'%s\n%s\n%s' % (defaultacl, warnmessages, newpagetext)
    else:
        action = 'SAVE'

    newpagetext = pg.normalizeText( newpagetext )
    
    comment = 'New comment by "%s"' % comauthor
    pg._write_file(newpagetext, action, u'Modified by PageComment macro')
    addLogEntry(request, 'COMNEW', Globs.pagename, comment)
    
    # message(_('The comment is added'))
    message(_('Thank you for your changes. Your attention to detail is appreciated.'))
    return 1

def showcomment():
    
    request = Globs.macro.request
    _ = request.getText
    
    commentlist = fetchcomments()
    
    if Params.newerfirst:
        commentlist.reverse()
    
    html = []
    cur_index = 0
    cellstyle = u'border-width: 0px; border-top-width: 1px; vertical-align: top; font-size: 0.9em; line-height: 1em;'
    
    html.append(u'<div id="commentlist"><table width="100%" class="commentlist">')
    
    for item in commentlist:
        if Globs.admin or (item['loginuser'] and request.user.valid and request.user.name == item['name']):
            htmlcommentdel = [
                u' <font style="font-size: 0.9em;">',
                u'<a style="color: #aa0000;" href="javascript: requesttodeleteadmin%d(document.delform%d, \'%s\');" title="%s">X</a>' % (Globs.formid, Globs.formid, item['key'], _('Delete')),
                u'</font>',
                ]
        elif item['loginuser']:
            htmlcommentdel = []

        else:
            htmlcommentdel = [
                u' <font style="font-size: 0.9em;">',
                u'<a style="color: #aa0000;" href="javascript: requesttodelete%d(document.delform%d, \'%s\');" title="%s">X</a>' % (Globs.formid, Globs.formid, item['key'], _('Delete')),
                u'</font>',
                ]
        
        htmlcomment = [
            u'<tr><td class="commenticon" style="%s width: 20px;">%s</td>' % (cellstyle, getsmiley(item['icon'])),
            u'<td class="commentauthor" style="%s"' % cellstyle,
            u'>%s</td>' % converttext(item['name']),
            u'<td style="%s width: 10px;">&nbsp;</td>' % cellstyle,
            u'<td class="commenttext" style="%s width: 100%%;">%s</td>' % (cellstyle, converttext(item['text'])),
            u'<td class="commentdate" style="%s text-align: right; font-size: 0.8em; " nowrap>%s%s</td></tr>' % (cellstyle, item['date'].replace(' ', '<br>'), u''.join(htmlcommentdel)),
            ]
        
        html.append(u'\n'.join(htmlcomment))
    
    html.append(u'</table></div>')
    
    return u'\n'.join(html)

def getescapedsectionname(targettext):
    regex = r'\W'
    pattern = re.compile(regex, re.UNICODE)
    sectionname = pattern.sub('', targettext)
    
    return sectionname


def getsmiley(markup):
    
    if markup in config.smileys.keys():
        formatter = Globs.macro.formatter
        return formatter.smiley(markup)
    else:
        return ''


def converttext(targettext):
    # Converts some special characters of html to plain-text style
    # What else to handle?

    # targettext = targettext.strip()
    targettext = targettext.replace(u'&', '&amp')
    targettext = targettext.replace(u'>', '&gt;')
    targettext = targettext.replace(u'<', '&lt;')
    targettext = targettext.replace(u'\n', '<br>')
    targettext = targettext.replace(u'"', '&quot;')
    targettext = targettext.replace(u'\t', '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;')
    targettext = targettext.replace(u'  ', '&nbsp;&nbsp;')

    return targettext
    
def convertdelimiter(targettext):
    # Converts delimeter to other string to avoid a crash
    
    targettext = targettext.replace('{{{', '{ { {')
    targettext = targettext.replace('}}}', '} } }')
    
    return targettext

    
def deleteform():
    # Javascript codes for deleting or restoring a comment
    
    request = Globs.macro.request
    _ = request.getText
    
    htmlresult = []
    
    html = [
        '<script language="javascript">',
        '<!--',
        ]
    htmlresult.append(u'\n'.join(html))
           
    html = [    
        '  function requesttodeleteadmin%d(delform, comkey) {' % Globs.formid,
        '      if (confirm("%s")) {;' % _('Really delete this page?'),
        '          delform.delkey.value = comkey;',
        '          delform.delpasswd.value = "****";',
        '          delform.submit();',
        '      }',
        '  }',
        '  function requesttodelete%d(delform, comkey) {' % Globs.formid,
        '      var passwd = prompt("%s:", "");' % _('Please specify a password!'),
        '      if(!(passwd == "" || passwd == null)) {',
        '          delform.delkey.value = comkey;',
        '          delform.delpasswd.value = passwd;',
        '          delform.submit();',
        '      }',
        '  }',
        ]
    
    htmlresult.append(u'\n'.join(html))
                
    html = [
        '//-->',
        '</script>',
        '<form name="delform%d" action="%s#pagecomment%d" METHOD="post">' % (Globs.formid, Globs.cursubname, Globs.formid),
        '<input type="hidden" value="show" name="action">',
        '<input name="delpasswd" type="hidden" value="****">',
        '<input name="delkey" type="hidden" value="">',
        '<input type="hidden" name="commentaction" value="delcomment%s">' % Globs.formid,
        '</form>',
        ]
    htmlresult.append(u'\n'.join(html))

    return u'\n'.join(htmlresult)


def filtercomment(index='', name='', passwd=''):
    
    # filter by index
    if index:
        filteredlist1 = fetchcomments(index, index)
    else:
        filteredlist1 = fetchcomments()
    
    # filter by name
    filteredlist2 = []
    if name:
        for item in filteredlist1:
            if name == item['name']:
                filteredlist2.append(item)
    else:
        filteredlist2 = filteredlist1
    
    # filter by password
    filteredlist3 = []
    if passwd:
        for item in filteredlist2:
            if passwd == item['passwd']:
                filteredlist3.append(item)
    else:
        filteredlist3 = filteredlist2

    return filteredlist3
        

def fetchcomments(startindex=1, endindex=9999):
    
    commentlist = []
    
    request = Globs.macro.request
    formatter = Globs.macro.formatter
    datapagename = Globs.datapagename

    pg = Page( request, datapagename )
    pagetext = pg.get_raw_body()
    
    regex = r'^(#acl\s*.*)$'
    pattern = re.compile(regex, re.UNICODE + re.MULTILINE + re.IGNORECASE)
    pagetext = pattern.sub('', pagetext)
    
    regex = ur"""
^[\{]{3}\n
^(?P<icon>[^\n]*)\n
^(?P<name>[^\n]*)\n
^(?P<date>[^\n]*)\n\n
^(?P<text>\s*.*?[^}]*)[\}]{3}[\n]*
^[#]{2}PASSWORD[ ](?P<passwd>[^\n]*)[\n]*
^[#]{2}LOGINUSER[ ](?P<loginuser>[^\n]*)[\n]*"""

    pattern = re.compile(regex, re.UNICODE + re.MULTILINE + re.VERBOSE)
    commentitems = pattern.findall(pagetext)
    
    cur_index = 0
    
    for item in commentitems:
        comment = {}
        cur_index += 1
        
        if cur_index < startindex:
            continue
        
        comment['index'] = cur_index
        comment['icon'] = item[0]
        comment['name'] = item[1]
        comment['date'] = item[2]
        comment['text'] = item[3]
        comment['passwd'] = item[4]
        comment['loginuser'] = item[5]
        
        # experimental
        comment['key'] = comment['date'].strip()
        
        commentlist.append(comment)
        
        if cur_index >= endindex:
            break

    return commentlist

def deletecomment(macro, delkey, delpasswd):
    # Deletes a comment with given index and password
    
    request = Globs.macro.request
    formatter = Globs.macro.formatter
    datapagename = Globs.datapagename
    _ = request.getText

    pg = PageEditor( request, datapagename )
    pagetext = pg.get_raw_body()
    
    regex = ur"""
(?P<comblock>^[\{]{3}\n
^(?P<icon>[^\n]*)\n
^(?P<name>[^\n]*)\n
^(?P<date>[^\n]*)[\n]+
^(?P<text>\s*.*?[^}]*)[\}]{3}[\n]*
^[#]{2}PASSWORD[ ](?P<passwd>[^\n]*)[\n]*
^[#]{2}LOGINUSER[ ](?P<loginuser>[^\n]*)[\n$]*)"""

    pattern = re.compile(regex, re.UNICODE + re.MULTILINE + re.VERBOSE)
    commentitems = pattern.findall(pagetext)
    
    for item in commentitems:
        
        if delkey == item[3].strip():
            comauthor = item[2]
            if Globs.admin or (request.user.valid and request.user.name == comauthor) or delpasswd == item[5]:
                newpagetext = pagetext.replace(item[0], '', 1)
                
                action = 'SAVE'
                comment = 'comment deleted by "%s"' % comauthor
                pg._write_file(newpagetext, action, u'Modified by PageComment macro')
                addLogEntry(request, 'COMDEL', Globs.pagename, comment)
                
                message(_('The comment is deleted'))
                
                return
            else:
                message(_('Sorry, wrong password.'))
                return
                
    message(_('No such comment'))

    
def addLogEntry(request, action, pagename, msg):
    # Add an entry to the edit log on adding comments.
    from MoinMoin.logfile import editlog
    t = wikiutil.timestamp2version(time.time())
    msg = unicode(msg)

    # TODO: for now we simply write 2 logs, maybe better use some multilog stuff
    # Write to global log
    log = editlog.EditLog(request)
    log.add(request, t, 99999999, action, pagename, request.remote_addr, msg)

    # Write to local log
    log = editlog.EditLog(request, rootpagename=pagename)
    log.add(request, t, 99999999, action, pagename, request.remote_addr, msg)
    
def getsmileymarkuplist(defaulticon):
    
    html = [
        u'<select name="comicon">',
        u'  <option value=""></option>',
        ]
    
    for smiley in config.smileys.keys():
        if defaulticon.strip() == smiley:
            html.append(u'  <option selected>%s</option>' % wikiutil.escape(smiley))
        else:
            html.append(u'  <option>%s</option>' % wikiutil.escape(smiley))

    html.append(u'</select>')
    
    return u'\n'.join(html)