Attachment 'RecentChanges.py'
Download 1 # -*- coding: iso-8859-1 -*-
2 """
3 MoinMoin - RecentChanges Macro
4
5 Parameter "ddiffs" by Ralf Zosel <ralf@zosel.com>, 04.12.2003.
6
7 @copyright: 2000-2004 Juergen Hermann <jh@web.de>
8 @copyright: 2010 Roger D. Haase, added zero-width spaces and soft hyphen word breaks
9 @license: GNU GPL, see COPYING for details.
10 """
11 import time, re
12
13 from MoinMoin import util, wikiutil
14 from MoinMoin.Page import Page
15 from MoinMoin.logfile import editlog
16 from MoinMoin.util.chartypes import chars_lower, chars_upper
17
18 _DAYS_SELECTION = [1, 2, 3, 7, 14, 30, 60, 90]
19 _MAX_DAYS = 7
20 _MAX_PAGENAME_LENGTH = 15 # 35
21 _MAX_COMMENT_LENGTH = 20
22
23 #############################################################################
24 ### RecentChanges Macro
25 #############################################################################
26
27 Dependencies = ["time"] # ["user", "pages", "pageparams", "bookmark"]
28
29 def split_page_name(page_name):
30 """ Return a string with the page name split by zero-width spaces.
31
32 @param page_name: the name of the page to be split
33 @rtype: unicode
34 @return: pagename of this page, split into space separated words
35 """
36 if len(page_name) <= _MAX_PAGENAME_LENGTH:
37 return page_name
38
39 zero_width_space = "​"
40 # treat "/" as a lower case character
41 split_regex = re.compile('([%s])([%s])' % ("/" + chars_lower, chars_upper), re.UNICODE)
42 split_by_spaces = page_name.split(" ")
43 space_parts = []
44 for split_by_space in split_by_spaces:
45 split_by_camelcases = split_regex.sub(r'\1 \2', split_by_space).split(" ")
46 camelcase_parts = []
47 for split_by_camelcase in split_by_camelcases:
48 if len(split_by_camelcase) > _MAX_PAGENAME_LENGTH:
49 lowercase_parts = []
50 while split_by_camelcase:
51 lowercase_parts.append(wikiutil.escape(split_by_camelcase[:_MAX_PAGENAME_LENGTH]))
52 split_by_camelcase = split_by_camelcase[_MAX_PAGENAME_LENGTH:]
53 camelcase_parts.append(zero_width_space.join(lowercase_parts))
54 else:
55 camelcase_parts.append(wikiutil.escape(split_by_camelcase))
56 space_parts.append(zero_width_space.join(camelcase_parts))
57 return " ".join(space_parts)
58
59 def format_comment(request, line):
60 comment = line.comment
61 action = line.action
62 _ = request.getText
63 if action.startswith('ATT'):
64 filename = wikiutil.url_unquote(line.extra)
65 if action == 'ATTNEW':
66 comment = _("Upload of attachment '%(filename)s'.") % {
67 'filename': filename}
68 elif action == 'ATTDEL':
69 comment = _("Attachment '%(filename)s' deleted.") % {
70 'filename': filename}
71 elif action == 'ATTDRW':
72 comment = _("Drawing '%(filename)s' saved.") % {
73 'filename': filename}
74 elif '/REVERT' in action:
75 rev = int(line.extra)
76 comment = (_("Revert to revision %(rev)d.") % {'rev': rev}) + " " + comment
77 elif '/RENAME' in action:
78 comment = (_("Renamed from '%(oldpagename)s'.") % {'oldpagename': line.extra}) + " " + comment
79
80 return wikiutil.make_breakable(comment, _MAX_COMMENT_LENGTH)
81
82 def format_page_edits(macro, lines, bookmark_usecs):
83 request = macro.request
84 _ = request.getText
85 d = {} # dict for passing stuff to theme
86 line = lines[0]
87 pagename = line.pagename
88 rev = int(line.rev)
89 tnow = time.time()
90 is_new = lines[-1].action == 'SAVENEW'
91 is_renamed = lines[-1].action == 'SAVE/RENAME'
92 # check whether this page is newer than the user's bookmark
93 hilite = line.ed_time_usecs > (bookmark_usecs or line.ed_time_usecs)
94 page = Page(request, pagename)
95
96 html_link = ''
97 if not page.exists():
98 img = request.theme.make_icon('deleted')
99 revbefore = rev - 1
100 if revbefore and page.exists(rev=revbefore, domain='standard'):
101 # indicate page was deleted and show diff to last existing revision of it
102 html_link = page.link_to_raw(request, img, querystr={'action': 'diff'}, rel='nofollow')
103 else:
104 # just indicate page was deleted
105 html_link = img
106 elif page.isConflict():
107 img = request.theme.make_icon('conflict')
108 html_link = page.link_to_raw(request, img, querystr={'action': 'edit'}, rel='nofollow')
109 elif hilite:
110 # show special icons if change was after the user's bookmark
111 if is_new:
112 img = 'new'
113 elif is_renamed:
114 img = 'renamed'
115 else:
116 img = 'updated'
117 img = request.theme.make_icon(img)
118 html_link = page.link_to_raw(request, img, querystr={'action': 'diff', 'date': '%d' % bookmark_usecs}, rel='nofollow')
119 else:
120 # show "DIFF" icon else
121 img = request.theme.make_icon('diffrc')
122 html_link = page.link_to_raw(request, img, querystr={'action': 'diff'}, rel='nofollow')
123
124 d['icon_html'] = html_link
125 d['pagelink_html'] = page.link_to(request, text=split_page_name(page.page_name), no_escape=1)
126
127 # print time of change
128 d['time_html'] = None
129 if request.cfg.changed_time_fmt:
130 tdiff = long(tnow - wikiutil.version2timestamp(long(line.ed_time_usecs))) / 60 # has to be long for py 2.2.x
131 if tdiff < 100:
132 d['time_html'] = _("%(mins)dm ago") % {
133 'mins': tdiff}
134 else:
135 d['time_html'] = time.strftime(request.cfg.changed_time_fmt, line.time_tuple)
136
137 # print editor name or IP
138 d['editors'] = None
139 if request.cfg.show_names:
140 if len(lines) > 1:
141 counters = {}
142 for idx in range(len(lines)):
143 name = lines[idx].getEditor(request)
144 if not name in counters:
145 counters[name] = []
146 counters[name].append(idx+1)
147 poslist = [(v, k) for k, v in counters.items()]
148 poslist.sort()
149 d['editors'] = []
150 for positions, name in poslist:
151 d['editors'].append("%s [%s]" % (
152 name, util.rangelist(positions)))
153 else:
154 d['editors'] = [line.getEditor(request)]
155
156 comments = []
157 for idx in range(len(lines)):
158 comment = format_comment(request, lines[idx])
159 if comment:
160 comments.append((idx+1, comment))
161
162 d['changecount'] = len(lines)
163 d['comments'] = comments
164
165 img = request.theme.make_icon('info')
166 d['info_html'] = page.link_to_raw(request, img, querystr={'action': 'info'}, rel='nofollow')
167
168 return request.theme.recentchanges_entry(d)
169
170 def cmp_lines(first, second):
171 return cmp(first[0], second[0])
172
173 def print_abandoned(macro):
174 request = macro.request
175 _ = request.getText
176 output = []
177 d = {}
178 page = macro.formatter.page
179 pagename = page.page_name
180 d['page'] = page
181 d['q_page_name'] = wikiutil.quoteWikinameURL(pagename)
182 msg = None
183
184 pages = request.rootpage.getPageList()
185 last_edits = []
186 for name in pages:
187 log = Page(request, name).editlog_entry()
188 if log:
189 last_edits.append(log)
190 # we don't want all Systempages at the beginning of the abandoned list
191 # line = editlog.EditLogLine({})
192 # line.pagename = page
193 # line.ed_time = 0
194 # line.comment = 'not edited'
195 # line.action = ''
196 # line.userid = ''
197 # line.hostname = ''
198 # line.addr = ''
199 # last_edits.append(line)
200 del pages
201 last_edits.sort()
202
203 # set max size in days
204 max_days = min(int(request.values.get('max_days', 0)), _DAYS_SELECTION[-1])
205 # default to _MAX_DAYS for users without bookmark
206 if not max_days:
207 max_days = _MAX_DAYS
208 d['rc_max_days'] = max_days
209
210 # give known user the option to extend the normal display
211 if request.user.valid:
212 d['rc_days'] = _DAYS_SELECTION
213 else:
214 d['rc_days'] = None
215
216 d['rc_update_bookmark'] = None
217 output.append(request.theme.recentchanges_header(d))
218
219 length = len(last_edits)
220
221 index = 0
222 last_index = 0
223 day_count = 0
224
225 if length > 0:
226 line = last_edits[index]
227 line.time_tuple = request.user.getTime(wikiutil.version2timestamp(line.ed_time_usecs))
228 this_day = line.time_tuple[0:3]
229 day = this_day
230
231 while 1:
232
233 index += 1
234
235 if index > length:
236 break
237
238 if index < length:
239 line = last_edits[index]
240 line.time_tuple = request.user.getTime(wikiutil.version2timestamp(line.ed_time_usecs))
241 day = line.time_tuple[0:3]
242
243 if (day != this_day) or (index == length):
244 d['bookmark_link_html'] = None
245 d['date'] = request.user.getFormattedDate(wikiutil.version2timestamp(last_edits[last_index].ed_time_usecs))
246 output.append(request.theme.recentchanges_daybreak(d))
247 this_day = day
248
249 for page in last_edits[last_index:index]:
250 output.append(format_page_edits(macro, [page], None))
251 last_index = index
252 day_count += 1
253 if (day_count >= max_days):
254 break
255
256 d['rc_msg'] = msg
257 output.append(request.theme.recentchanges_footer(d))
258 return ''.join(output)
259
260
261 def macro_RecentChanges(macro, abandoned=False):
262 # handle abandoned keyword
263 if abandoned:
264 return print_abandoned(macro)
265
266 request = macro.request
267 _ = request.getText
268 output = []
269 user = request.user
270 page = macro.formatter.page
271 pagename = page.page_name
272
273 d = {}
274 d['page'] = page
275 d['q_page_name'] = wikiutil.quoteWikinameURL(pagename)
276
277 log = editlog.EditLog(request)
278
279 tnow = time.time()
280 msg = ""
281
282 # get bookmark from valid user
283 bookmark_usecs = request.user.getBookmark() or 0
284
285 # add bookmark link if valid user
286 d['rc_curr_bookmark'] = None
287 d['rc_update_bookmark'] = None
288 if request.user.valid:
289 d['rc_curr_bookmark'] = _('(no bookmark set)')
290 if bookmark_usecs:
291 currentBookmark = wikiutil.version2timestamp(bookmark_usecs)
292 currentBookmark = user.getFormattedDateTime(currentBookmark)
293 currentBookmark = _('(currently set to %s)') % currentBookmark
294 deleteBookmark = page.link_to(request, _("Delete bookmark"), querystr={'action': 'bookmark', 'time': 'del'}, rel='nofollow')
295 d['rc_curr_bookmark'] = currentBookmark + ' ' + deleteBookmark
296
297 version = wikiutil.timestamp2version(tnow)
298 d['rc_update_bookmark'] = page.link_to(request, _("Set bookmark"), querystr={'action': 'bookmark', 'time': '%d' % version}, rel='nofollow')
299
300 # set max size in days
301 max_days = min(int(request.values.get('max_days', 0)), _DAYS_SELECTION[-1])
302 # default to _MAX_DAYS for useres without bookmark
303 if not max_days and not bookmark_usecs:
304 max_days = _MAX_DAYS
305 d['rc_max_days'] = max_days
306
307 # give known user the option to extend the normal display
308 if request.user.valid:
309 d['rc_days'] = _DAYS_SELECTION
310 else:
311 d['rc_days'] = []
312
313 output.append(request.theme.recentchanges_header(d))
314
315 pages = {}
316 ignore_pages = {}
317
318 today = request.user.getTime(tnow)[0:3]
319 this_day = today
320 day_count = 0
321
322 for line in log.reverse():
323
324 if not request.user.may.read(line.pagename):
325 continue
326
327 line.time_tuple = request.user.getTime(wikiutil.version2timestamp(line.ed_time_usecs))
328 day = line.time_tuple[0:3]
329 hilite = line.ed_time_usecs > (bookmark_usecs or line.ed_time_usecs)
330
331 if ((this_day != day or (not hilite and not max_days))) and len(pages) > 0:
332 # new day or bookmark reached: print out stuff
333 this_day = day
334 for p in pages:
335 ignore_pages[p] = None
336 pages = pages.values()
337 pages.sort(cmp_lines)
338 pages.reverse()
339
340 if request.user.valid:
341 bmtime = pages[0][0].ed_time_usecs
342 d['bookmark_link_html'] = page.link_to(request, _("Set bookmark"), querystr={'action': 'bookmark', 'time': '%d' % bmtime}, rel='nofollow')
343 else:
344 d['bookmark_link_html'] = None
345 d['date'] = request.user.getFormattedDate(wikiutil.version2timestamp(pages[0][0].ed_time_usecs))
346 output.append(request.theme.recentchanges_daybreak(d))
347
348 for p in pages:
349 output.append(format_page_edits(macro, p, bookmark_usecs))
350 pages = {}
351 day_count += 1
352 if max_days and (day_count >= max_days):
353 break
354
355 elif this_day != day:
356 # new day but no changes
357 this_day = day
358
359 if line.pagename in ignore_pages:
360 continue
361
362 # end listing by default if user has a bookmark and we reached it
363 if not max_days and not hilite:
364 msg = _('[Bookmark reached]')
365 break
366
367 if line.pagename in pages:
368 pages[line.pagename].append(line)
369 else:
370 pages[line.pagename] = [line]
371 else:
372 if len(pages) > 0:
373 # end of loop reached: print out stuff
374 # XXX duplicated code from above
375 # but above does not trigger if we have the first day in wiki history
376 for p in pages:
377 ignore_pages[p] = None
378 pages = pages.values()
379 pages.sort(cmp_lines)
380 pages.reverse()
381
382 if request.user.valid:
383 bmtime = pages[0][0].ed_time_usecs
384 d['bookmark_link_html'] = page.link_to(request, _("Set bookmark"), querystr={'action': 'bookmark', 'time': '%d' % bmtime}, rel='nofollow')
385 else:
386 d['bookmark_link_html'] = None
387 d['date'] = request.user.getFormattedDate(wikiutil.version2timestamp(pages[0][0].ed_time_usecs))
388 output.append(request.theme.recentchanges_daybreak(d))
389
390 for p in pages:
391 output.append(format_page_edits(macro, p, bookmark_usecs))
392
393
394 d['rc_msg'] = msg
395 output.append(request.theme.recentchanges_footer(d))
396
397 return ''.join(output)
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.