source: addcommentmacro/0.11/addcomment/macro.py

Last change on this file was 16765, checked in by Ryan J Ollos, 6 years ago

TracAddCommentMacro 0.3: Add license info

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