Back to index

moin  1.9.0~rc2
editlog.py
Go to the documentation of this file.
00001 """
00002     MoinMoin edit log class
00003 
00004     This is used for accessing the global edit-log (e.g. by RecentChanges) as
00005     well as for the local edit-log (e.g. PageEditor, info action).
00006 
00007     TODO:
00008     * when we have items with separate data and metadata storage, we do not
00009       need the local edit-log file any more (because everything in it will be
00010       stored into the revision's metadata).
00011     * maybe we can even get rid of the global edit-log as we know it now (and just
00012       maintaining a cache of recent changes metadata)
00013 
00014     @copyright: 2006 MoinMoin:ThomasWaldmann
00015     @license: GNU GPL, see COPYING for details.
00016 """
00017 
00018 from MoinMoin import log
00019 logging = log.getLogger(__name__)
00020 
00021 from MoinMoin.logfile import LogFile
00022 from MoinMoin import wikiutil, user, config
00023 from MoinMoin.Page import Page
00024 
00025 class EditLogLine:
00026     """
00027     Has the following attributes
00028 
00029     ed_time_usecs
00030     rev
00031     action
00032     pagename
00033     addr
00034     hostname
00035     userid
00036     extra
00037     comment
00038     """
00039     def __init__(self, usercache):
00040         self._usercache = usercache
00041 
00042     def __cmp__(self, other):
00043         try:
00044             return cmp(self.ed_time_usecs, other.ed_time_usecs)
00045         except AttributeError:
00046             return cmp(self.ed_time_usecs, other)
00047 
00048     def is_from_current_user(self, request):
00049         user = request.user
00050         if user.id:
00051             return user.id == self.userid
00052         return request.remote_addr == self.addr
00053 
00054     def getEditorData(self, request):
00055         """ Return a tuple of type id and string or Page object
00056             representing the user that did the edit.
00057 
00058             DEPRECATED - try to use getInterwikiEditorData
00059             NOT USED ANY MORE BY MOIN CODE!
00060 
00061             The type id is one of 'ip' (DNS or numeric IP), 'user' (user name)
00062             or 'homepage' (Page instance of user's homepage).
00063         """
00064         result = 'ip', request.cfg.show_hosts and self.hostname or ''
00065         if self.userid:
00066             if self.userid not in self._usercache:
00067                 self._usercache[self.userid] = user.User(request, self.userid, auth_method="editlog:53")
00068             userdata = self._usercache[self.userid]
00069             if userdata.name:
00070                 pg = wikiutil.getHomePage(request, username=userdata.name)
00071                 if pg:
00072                     result = ('homepage', pg)
00073                 else:
00074                     result = ('user', userdata.name)
00075 
00076         return result
00077 
00078 
00079     def getInterwikiEditorData(self, request):
00080         """ Return a tuple of type id and string or Page object
00081             representing the user that did the edit.
00082 
00083             The type id is one of 'ip' (DNS or numeric IP), 'user' (user name)
00084             or 'homepage' (Page instance of user's homepage) or 'anon' ('').
00085         """
00086         result = 'anon', ''
00087         if request.cfg.show_hosts and self.hostname:
00088             result = 'ip', self.hostname
00089         if self.userid:
00090             if self.userid not in self._usercache:
00091                 self._usercache[self.userid] = user.User(request, self.userid, auth_method="editlog:75")
00092             userdata = self._usercache[self.userid]
00093             if userdata.mailto_author and userdata.email:
00094                 return ('email', userdata.email)
00095             elif userdata.name:
00096                 interwiki = wikiutil.getInterwikiHomePage(request, username=userdata.name)
00097                 if interwiki:
00098                     result = ('interwiki', interwiki)
00099         return result
00100 
00101 
00102     def getEditor(self, request):
00103         """ Return a HTML-safe string representing the user that did the edit.
00104         """
00105         _ = request.getText
00106         if request.cfg.show_hosts and self.hostname and self.addr:
00107             title = " @ %s[%s]" % (self.hostname, self.addr)
00108         else:
00109             title = ""
00110         kind, info = self.getInterwikiEditorData(request)
00111         if kind == 'interwiki':
00112             name = self._usercache[self.userid].name
00113             aliasname = self._usercache[self.userid].aliasname
00114             if not aliasname:
00115                 aliasname = name
00116             title = aliasname + title
00117             text = (request.formatter.interwikilink(1, title=title, generated=True, *info) +
00118                     request.formatter.text(name) +
00119                     request.formatter.interwikilink(0, title=title, *info))
00120         elif kind == 'email':
00121             name = self._usercache[self.userid].name
00122             aliasname = self._usercache[self.userid].aliasname
00123             if not aliasname:
00124                 aliasname = name
00125             title = aliasname + title
00126             url = 'mailto:%s' % info
00127             text = (request.formatter.url(1, url, css='mailto', title=title) +
00128                     request.formatter.text(name) +
00129                     request.formatter.url(0))
00130         elif kind == 'ip':
00131             try:
00132                 idx = info.index('.')
00133             except ValueError:
00134                 idx = len(info)
00135             title = '???' + title
00136             text = request.formatter.text(info[:idx])
00137         elif kind == 'anon':
00138             title = ''
00139             text = _('anonymous')
00140         else:
00141             raise Exception("unknown EditorData type")
00142         return (request.formatter.span(1, title=title) +
00143                 text +
00144                 request.formatter.span(0))
00145 
00146 
00147 class EditLog(LogFile):
00148     """ Used for accessing the global edit-log (e.g. by RecentChanges) as
00149         well as for the local edit-log (e.g. PageEditor, info action).
00150     """
00151     def __init__(self, request, filename=None, buffer_size=4096, **kw):
00152         if filename is None:
00153             rootpagename = kw.get('rootpagename', None)
00154             if rootpagename:
00155                 filename = Page(request, rootpagename).getPagePath('edit-log', isfile=1)
00156             else:
00157                 filename = request.rootpage.getPagePath('edit-log', isfile=1)
00158         LogFile.__init__(self, filename, buffer_size)
00159         self._NUM_FIELDS = 9
00160         self._usercache = {}
00161 
00162         # Used by antispam in order to show an internal name instead
00163         # of a confusing userid
00164         self.uid_override = kw.get('uid_override', None)
00165 
00166     def add(self, request, mtime, rev, action, pagename, host=None, extra=u'', comment=u''):
00167         """ Generate (and add) a line to the edit-log.
00168 
00169         If `host` is None, it's read from request vars.
00170         """
00171         if request.cfg.log_remote_addr:
00172             if host is None:
00173                 host = request.remote_addr or ''
00174 
00175             if request.cfg.log_reverse_dns_lookups:
00176                 import socket
00177                 try:
00178                     hostname = socket.gethostbyaddr(host)[0]
00179                     hostname = unicode(hostname, config.charset)
00180                 except (socket.error, UnicodeError):
00181                     hostname = host
00182             else:
00183                 hostname = host
00184         else:
00185             host = ''
00186             hostname = ''
00187 
00188         comment = wikiutil.clean_input(comment)
00189         user_id = request.user.valid and request.user.id or ''
00190 
00191         if self.uid_override is not None:
00192             user_id = ''
00193             hostname = self.uid_override
00194             host = ''
00195 
00196         line = u"\t".join((str(long(mtime)), # has to be long for py 2.2.x
00197                            "%08d" % rev,
00198                            action,
00199                            wikiutil.quoteWikinameFS(pagename),
00200                            host,
00201                            hostname,
00202                            user_id,
00203                            extra,
00204                            comment,
00205                            )) + "\n"
00206         self._add(line)
00207 
00208     def parser(self, line):
00209         """ Parse edit-log line into fields """
00210         fields = line.strip().split('\t')
00211         # Pad empty fields
00212         missing = self._NUM_FIELDS - len(fields)
00213         if missing:
00214             fields.extend([''] * missing)
00215         result = EditLogLine(self._usercache)
00216         (result.ed_time_usecs, result.rev, result.action,
00217          result.pagename, result.addr, result.hostname, result.userid,
00218          result.extra, result.comment, ) = fields[:self._NUM_FIELDS]
00219         if not result.hostname:
00220             result.hostname = result.addr
00221         result.pagename = wikiutil.unquoteWikiname(result.pagename.encode('ascii'))
00222         result.ed_time_usecs = long(result.ed_time_usecs or '0') # has to be long for py 2.2.x
00223         return result
00224 
00225     def set_filter(self, **kw):
00226         """ optionally filter for specific pagenames, addrs, hostnames, userids """
00227         expr = "1"
00228         for field in ['pagename', 'addr', 'hostname', 'userid']:
00229             if field in kw:
00230                 expr = "%s and x.%s == %s" % (expr, field, repr(kw[field]))
00231 
00232         if 'ed_time_usecs' in kw:
00233             expr = "%s and long(x.ed_time_usecs) == %s" % (expr, long(kw['ed_time_usecs'])) # must be long for py 2.2.x
00234 
00235         self.filter = eval("lambda x: " + expr)
00236 
00237 
00238     def news(self, oldposition):
00239         """ What has changed in the edit-log since <oldposition>?
00240             Returns edit-log final position() and list of changed item names.
00241         """
00242         if oldposition is None:
00243             self.to_end()
00244         else:
00245             self.seek(oldposition)
00246         items = []
00247         for line in self:
00248             items.append(line.pagename)
00249             if line.action == 'SAVE/RENAME':
00250                 items.append(line.extra) # == old page name
00251 
00252         newposition = self.position()
00253         logging.log(logging.NOTSET, "editlog.news: new pos: %r new items: %r", newposition, items)
00254         return newposition, items
00255