root/addcommentmacro/0.11/addcomment/macro.py

Revision 3887, 9.4 kB (checked in by osimons, 7 months ago)

AddCommentMacro:

  • Wiki page preview got us a double set of editing toolbars. Fixed.
  • Also adding some extra logging and deleting the redirect marker in search of possible issue with redirect (that I can't replicate).
Line 
1 # vim: expandtab
2 import re, time
3 from StringIO import StringIO
4
5 from genshi.builder import tag
6
7 from trac.core import *
8 from trac.wiki.formatter import format_to_html
9 from trac.util import TracError
10 from trac.util.text import to_unicode
11 from trac.web.api import IRequestFilter, RequestDone
12 from trac.web.chrome import add_script
13 from trac.wiki.api import parse_args, IWikiMacroProvider
14 from trac.wiki.macros import WikiMacroBase
15 from trac.wiki.model import WikiPage
16 from trac.wiki.web_ui import WikiModule
17
18 from macropost.api import IMacroPoster
19
20 class AddCommentMacro(WikiMacroBase):
21     """A macro to add comments to a page. Usage:
22     {{{
23     [[AddComment]]
24     }}}
25     The macro accepts one optional argument that allows appending
26     to the wiki page even though user may not have modify permission:
27     {{{
28     [[AddComment(appendonly)]]
29     }}}
30     """
31     implements(IWikiMacroProvider, IRequestFilter, IMacroPoster)
32
33     def expand_macro(self, formatter, name, content):
34        
35         args, kw = parse_args(content)
36         req = formatter.req
37         context = formatter.context
38        
39         # Prevent multiple inclusions - store a temp in req
40         if hasattr(req, 'addcommentmacro'):
41             raise TracError('\'AddComment\' macro cannot be included twice.')
42         req.addcommentmacro = True
43        
44         # Prevent it being used outside of wiki page context
45         resource = context.resource
46         if not resource.realm == 'wiki':
47             raise TracError('\'AddComment\' macro can only be used in Wiki pages.')
48        
49         # Setup info and defaults
50         authname = req.authname
51         page = WikiPage(self.env, resource)
52         page_url = req.href.wiki(resource.id)
53         wikipreview = req.args.get("preview", "")
54        
55         # Can this user add a comment to this page?
56         appendonly = ('appendonly' in args)
57         cancomment = False
58         if page.readonly:
59             if 'WIKI_ADMIN' in req.perm(resource):
60                 cancomment = True
61         elif 'WIKI_MODIFY' in req.perm(resource):
62             cancomment = True
63         elif appendonly and 'WIKI_VIEW' in req.perm(resource):
64             cancomment = True
65         else:
66             raise TracError('Error: Insufficient privileges to AddComment')
67        
68         # Get the data from the POST
69         comment = req.args.get("addcomment", "")
70         preview = req.args.get("previewaddcomment", "")
71         cancel = req.args.get("canceladdcomment", "")
72         submit = req.args.get("submitaddcomment", "")
73         if not cancel and req.authname == 'anonymous':
74             authname = req.args.get("authoraddcomment", authname)
75        
76         # Ensure [[AddComment]] is not present in comment, so that infinite
77         # recursion does not occur.
78         comment = to_unicode(re.sub('(^|[^!])(\[\[AddComment)', '\\1!\\2', comment))
79        
80         the_preview = the_message = the_form = tag()
81
82         # If we are submitting or previewing, inject comment as it should look
83         if cancomment and comment and (preview or submit):
84             heading = tag.h4("Comment by ", authname, " on ",
85                         to_unicode(time.strftime('%c', time.localtime())),
86                         id="commentpreview")
87             if preview:
88                 the_preview = tag.div(heading,
89                                 format_to_html(self.env, context, comment),
90                                 class_="wikipage", id="preview")
91        
92         # Check the form_token
93         form_ok = True
94         if submit and req.args.get('__FORM_TOKEN','') != req.form_token:
95             form_ok = False
96             the_message = tag.div(tag.strong("ERROR: "),
97                 "AddComment received incorrect form token. "
98                 "Do you have cookies enabled?",
99                 class_="system-message")
100        
101         # When submitting, inject comment before macro
102         if comment and submit and cancomment and form_ok:
103             submitted = False
104             newtext = ""
105             for line in page.text.splitlines():
106                 if line.find('[[AddComment') == 0:
107                     newtext += "==== Comment by %s on %s ====\n%s\n\n" % (
108                             authname,
109                             to_unicode(time.strftime('%c', time.localtime())),
110                             comment)
111                     submitted = True
112                 newtext += line+"\n"
113             if submitted:
114                 page.text = newtext
115                
116                 # Let the wiki page manipulators have a look at the
117                 # submission.
118                 valid = True
119                 req.args.setdefault('comment', 'Comment added.')
120                 try:
121                     for manipulator in WikiModule(self.env).page_manipulators:
122                         for field, message in manipulator.validate_wiki_page(req, page):
123                             valid = False
124                             if field:
125                                 the_message += tag.div(tag.strong("invalid field '%s': " % field),
126                                                        message,
127                                                        class_="system-message")
128                             else:
129                                 the_message += tag.div(tag.strong("invalid: "),
130                                                        message,
131                                                        class_="system-message")
132
133                 # The TracSpamfilterPlugin does not generate messages,
134                 # but throws RejectContent.
135                 except TracError, s:
136                     valid = False
137                     the_message += tag.div(tag.strong("ERROR: "), s, class_="system-message")
138
139                 if valid:       
140                     page.save(authname, req.args['comment'], req.environ['REMOTE_ADDR'])
141                     # We can't redirect from macro as it will raise RequestDone
142                     # which like other macro errors gets swallowed in the Formatter.
143                     # We need to re-raise it in a post_process_request instead.
144                     try:
145                         self.env.log.debug(
146                             "AddComment saved - redirecting to: %s" % page_url)
147                         req._outheaders = []
148                         req.redirect(page_url)
149                     except RequestDone:
150                         req.addcomment_raise = True
151             else:
152                 the_message = tag.div(tag.strong("ERROR: "), "[[AddComment]] "
153                           "macro call must be the only content on its line. "
154                           "Could not add comment.",
155                           class_="system-message")
156
157         the_form = tag.form(
158                     tag.fieldset(
159                         tag.legend("Add comment"),
160                         tag.div(
161                             (wikipreview and "Page preview..." or None),
162                             tag.textarea((not cancel and comment or ""),
163                                         class_="wikitext",
164                                         id="addcomment",
165                                         name="addcomment",
166                                         cols=80, rows=5,
167                                     disabled=(not cancomment and "disabled" or None)),
168                             class_="field"
169                         ),
170                         (req.authname == 'anonymous' and tag.div(
171                             tag.label("Your email or username:",
172                                     for_="authoraddcomment"),
173                             tag.input(id="authoraddcomment", type="text",
174                                     size=30, value=authname)
175                         ) or None),
176                         tag.input(type="hidden", name="__FORM_TOKEN",
177                                         value=req.form_token),
178                         tag.div(
179                             tag.input(value="Add comment", type="submit",
180                                     name="submitaddcomment", size=30,
181                                     disabled=(not cancomment and "disabled" or None)),
182                             tag.input(value="Preview comment", type="submit",
183                                     name="previewaddcomment",
184                                     disabled=(not cancomment and "disabled" or None)),
185                             tag.input(value="Cancel", type="submit",
186                                     name="canceladdcomment",
187                                     disabled=(not cancomment and "disabled" or None)),
188                             class_="buttons"
189                         ),
190                     ),
191                     method="post",
192                     action=page_url+"#commenting",
193                 )
194
195         if not wikipreview:
196             # Wiki edit preview already adds this javascript file
197             add_script(req, 'common/js/wikitoolbar.js')
198        
199         return tag.div(the_preview, the_message, the_form, id="commenting")
200    
201     # IMacroPoster method
202    
203     def process_macro_post(self, req):
204         self.log.debug('AddCommentMacro: Got a POST')
205
206     # IRequestFilter methods
207
208     def pre_process_request(self, req, handler):
209         return handler
210
211     def post_process_request(self, req, template, data, content_type):
212         if hasattr(req, 'addcomment_raise'):
213             self.env.log.debug("AddCommentMacro: Re-raising RequestDone from redirect")
214             del(req.addcomment_raise)
215             raise RequestDone
216         return template, data, content_type
Note: See TracBrowser for help on using the browser.