| | 1 | # Author: Steven N. Severinghaus <sns@severinghaus.org> |
|---|
| | 2 | # Last Modified: 2006-09-29 |
|---|
| | 3 | # Home: http://trac-hacks.org/wiki/LastModifiedMacro |
|---|
| | 4 | # |
|---|
| | 5 | # The LastModified macro is free software; you can redistribute it and/or |
|---|
| | 6 | # modify it under the terms of the GNU General Public License as published |
|---|
| | 7 | # by the Free Software Foundation; either version 2 of the License, or (at |
|---|
| | 8 | # your option) any later version. |
|---|
| | 9 | # |
|---|
| | 10 | # Created for the BFGFF project, http://chimp.acm.uiuc.edu/ |
|---|
| | 11 | |
|---|
| | 12 | from trac.core import * |
|---|
| | 13 | from trac.wiki.macros import WikiMacroBase |
|---|
| | 14 | from trac.util import escape, html, Markup |
|---|
| | 15 | from trac.web import * |
|---|
| | 16 | import time, datetime, re |
|---|
| | 17 | |
|---|
| | 18 | __all__ = ['LastModified'] |
|---|
| | 19 | |
|---|
| | 20 | |
|---|
| | 21 | class LastModified(WikiMacroBase): |
|---|
| | 22 | """ |
|---|
| | 23 | Shows the last modification date of the specified page, or the page the |
|---|
| | 24 | macro appears in if not specified. An optional argument, delta, can be |
|---|
| | 25 | given to show the time elapsed since the last modification. The output is |
|---|
| | 26 | placed in span with a title that gives the exact modification date and |
|---|
| | 27 | the author of the change. This span can be optionally CSS styled to have |
|---|
| | 28 | a thin grey line under the text and a help cursor that tooltips the |
|---|
| | 29 | span title. |
|---|
| | 30 | |
|---|
| | 31 | For example, ![[LastModified(WikiStart)]] produces: |
|---|
| | 32 | [[LastModified(WikiStart)]] |
|---|
| | 33 | |
|---|
| | 34 | Alternatively, ![[LastModified(WikiStart,delta)]] produces: |
|---|
| | 35 | [[LastModified(WikiStart,delta)]] |
|---|
| | 36 | |
|---|
| | 37 | Finally, ![[LastModified(WikiStart,normal,style)]] produces: |
|---|
| | 38 | [[LastModified(WikiStart,normal,style)]] |
|---|
| | 39 | |
|---|
| | 40 | """ |
|---|
| | 41 | |
|---|
| | 42 | def render_macro(self, req, name, content): |
|---|
| | 43 | page_name = mode = None |
|---|
| | 44 | use_css = False |
|---|
| | 45 | if not content: |
|---|
| | 46 | current_href = req.path_info[6:] or 'WikiStart' |
|---|
| | 47 | page_name=re.sub(' \(.*$ ', '', current_href) |
|---|
| | 48 | mode='normal' |
|---|
| | 49 | else: |
|---|
| | 50 | args = content.split(',') |
|---|
| | 51 | if len(args) > 3: |
|---|
| | 52 | raise Exception("Accepts no more than three arguments") |
|---|
| | 53 | if len(args) == 0: |
|---|
| | 54 | raise Exception("The argument parser does not accept empty arguments") |
|---|
| | 55 | for karg in args: |
|---|
| | 56 | if not mode and (karg == 'delta' or karg == 'normal'): |
|---|
| | 57 | mode = karg |
|---|
| | 58 | elif not use_css and karg == 'style': |
|---|
| | 59 | use_css = True |
|---|
| | 60 | elif not page_name: |
|---|
| | 61 | page_name = karg |
|---|
| | 62 | else: |
|---|
| | 63 | raise Exception("Unknown or duplicate parameter passed") |
|---|
| | 64 | if not mode: |
|---|
| | 65 | mode = 'normal' |
|---|
| | 66 | if not page_name: |
|---|
| | 67 | current_href = req.path_info[6:] or 'WikiStart' |
|---|
| | 68 | page_name=re.sub(' \(.*$ ', '', current_href) |
|---|
| | 69 | |
|---|
| | 70 | db=self.env.get_db_cnx() |
|---|
| | 71 | cursor=db.cursor() |
|---|
| | 72 | cursor.execute("SELECT author, time FROM wiki WHERE name = '%s' " |
|---|
| | 73 | "ORDER BY version DESC LIMIT 1" % page_name) |
|---|
| | 74 | row=cursor.fetchone() |
|---|
| | 75 | author=row[0] |
|---|
| | 76 | time_int=row[1] |
|---|
| | 77 | timestamp=time.localtime(time_int) |
|---|
| | 78 | |
|---|
| | 79 | # XXX: Not wanting to screw around with modding parameters (yet), |
|---|
| | 80 | # configuration happens here. Simply toggle 0 or 1 for false/true |
|---|
| | 81 | if use_css: |
|---|
| | 82 | cssstyle='style="border-bottom: 1px dotted gray; cursor: help;" ' |
|---|
| | 83 | else: |
|---|
| | 84 | cssstyle='' |
|---|
| | 85 | |
|---|
| | 86 | if mode=='delta': |
|---|
| | 87 | now=datetime.datetime.utcfromtimestamp(time.time()) |
|---|
| | 88 | last_modified=datetime.datetime.utcfromtimestamp(time_int) |
|---|
| | 89 | elapsed=now-last_modified |
|---|
| | 90 | if elapsed.days==0: |
|---|
| | 91 | if elapsed.seconds/3600>1.5: |
|---|
| | 92 | count=elapsed.seconds/3600 |
|---|
| | 93 | unit='hour' |
|---|
| | 94 | elif elapsed.seconds/60>1.5: |
|---|
| | 95 | count=elapsed.seconds/60 |
|---|
| | 96 | unit='minute' |
|---|
| | 97 | else: |
|---|
| | 98 | count=elapsed.seconds |
|---|
| | 99 | unit='second' |
|---|
| | 100 | elif elapsed.days/3650>1.5: |
|---|
| | 101 | count=elapsed.days/3650 |
|---|
| | 102 | unit='decade' |
|---|
| | 103 | elif elapsed.days/365>1.5: |
|---|
| | 104 | count=elapsed.days/365 |
|---|
| | 105 | unit='year' |
|---|
| | 106 | elif elapsed.days/30>1.5: |
|---|
| | 107 | count=elapsed.days/30 |
|---|
| | 108 | unit='month' |
|---|
| | 109 | elif elapsed.days/7>1.5: |
|---|
| | 110 | count=elapsed.days/7 |
|---|
| | 111 | unit='week' |
|---|
| | 112 | else: |
|---|
| | 113 | count=elapsed.days |
|---|
| | 114 | unit='day' |
|---|
| | 115 | output=""+repr(count)+" "+unit |
|---|
| | 116 | if (count != 1 and count != -1): output+="s" |
|---|
| | 117 | else: |
|---|
| | 118 | output=time.strftime("%Y-%m-%d", timestamp) |
|---|
| | 119 | |
|---|
| | 120 | wrapped_output='<span class="last-modified" ' +cssstyle+'title="'+\ |
|---|
| | 121 | time.asctime(timestamp)+' by '+author+'">'+\ |
|---|
| | 122 | output+'</span>' |
|---|
| | 123 | |
|---|
| | 124 | #return Markup('<span class="last-modified" %s title="%s by %s"> %s</span>' |
|---|
| | 125 | # , cssstyle, time.asctime(timestamp), author, output) |
|---|
| | 126 | return wrapped_output |