Edgewall Software

TracDev/ContextRefactoring: work_in_progress_snapshot.diff

File work_in_progress_snapshot.diff, 40.8 KB (added by cboos, 14 months ago)

Work in progress snapshot - the diff (on r6020) corresponding to the above files, except for the context.py file which has so much changed it's not funny.

  • trac/attachment.py

    diff -r e0c9d8655e31 -r 263dd7d3d361 trac/attachment.py
    a b  
    2727 
    2828from trac import perm, util 
    2929from trac.config import BoolOption, IntOption 
    30 from trac.context import IContextProvider, Context, ResourceSystem, \ 
    31                          ResourceNotFound 
     30from trac.context import IResourceManager, ResourceSystem, ResourceNotFound 
    3231from trac.core import * 
    3332from trac.env import IEnvironmentSetupParticipant 
    3433from trac.perm import PermissionError, PermissionSystem, IPermissionPolicy 
     
    8079        OK.""" 
    8180 
    8281 
    83 class AttachmentContext(Context): 
    84     """Context for attachment resources.""" 
    85  
    86     realm = 'attachment' 
    87  
    88     # methods reimplemented from Context 
    89  
    90     def get_resource(self): 
    91         return Attachment(self.env, self.parent.realm, self.parent.id, 
    92                           filename=self.id, db=self._db) 
    93  
    94     def get_href(self, href, path=None, **kwargs): 
    95         """Return an URL to the attachment itself. 
    96  
    97         A `format` keyword argument equal to `'raw'` will be converted 
    98         to the raw-attachment prefix. 
    99         """ 
    100         format = kwargs.get('format') 
    101         prefix = 'attachment' 
    102         if format == 'raw': 
    103             kwargs.pop('format') 
    104             prefix = 'raw-attachment' 
    105         path = [unicode(p) for p in [prefix, self.parent.realm, self.parent.id, 
    106                                      self.id, path] if p] 
    107         return Context.get_href(self, href, '/' + '/'.join(path), **kwargs) 
    108  
    109     def permid(self): 
    110         return self.parent.permid() + (self.realm, self.id) 
    111  
    112     def name(self): 
    113         if self.id: 
    114             return _("Attachment '%(id)s' in %(parent)s", id=self.id, 
    115                      parent=self.parent.name()) 
    116         else: 
    117             return _("Attachments of %(parent)s", parent=self.parent.name()) 
    118  
    119     def shortname(self): 
    120         return '%s:%s' % (self.parent.shortname(), self.filename) 
    121  
    122     def summary(self): 
    123         return self.resource.description 
    124  
    12582 
    12683class AttachmentList(object): 
    12784    """Helper class for template data, representing a list of attachments.""" 
    12885     
    12986    def __init__(self, parent, attachments): 
    130         self.req = parent.req 
    131         self.parent = parent 
    132         self.attachments = [parent('attachment', a.filename, resource=a) 
    133                             for a in attachments] 
    134  
    135     def attach_href(self): 
    136         return self.parent('attachment').get_href(self.req.href, action='new') 
     87        self.attachments = filter(None, [parent.child('attachment', a.filename, 
     88                                                      model=a) 
     89                                         for a in attachments]) 
     90        self.new_attachment = parent.child('attachment') 
     91 
     92    def attach_href(self, href): 
     93        return self.new_attachment.url(href, action='new') 
    13794 
    13895    def can_create(self): 
    139         return 'ATTACHMENT_CREATE' in self.req.perm(self.parent('attachment')) 
     96        return 'ATTACHMENT_CREATE' in self.new_attachment.perm 
    14097 
    14198 
    14299class Attachment(object): 
     
    322279class AttachmentModule(Component): 
    323280 
    324281    implements(IEnvironmentSetupParticipant, IRequestHandler, 
    325                INavigationContributor, IWikiSyntaxProvider, 
    326                IContextProvider) 
     282               INavigationContributor, IWikiSyntaxProvider, IResourceManager) 
    327283 
    328284    change_listeners = ExtensionPoint(IAttachmentChangeListener) 
    329285    manipulators = ExtensionPoint(IAttachmentManipulator) 
     
    381337            return True 
    382338 
    383339    def process_request(self, req): 
     340        parent_id = None 
    384341        parent_realm = req.args.get('realm') 
    385342        path = req.args.get('path') 
     343        filename = None 
    386344         
    387345        if not parent_realm or not path: 
    388346            raise HTTPBadRequest(_('Bad request')) 
    389347 
    390         context = Context(self.env, req) 
    391  
     348        parent_realm = req.perm(parent_realm) 
    392349        action = req.args.get('action', 'view') 
    393350        if action == 'new': 
    394             attachment = Attachment(self.env, parent_realm, path) 
     351            parent_id = path 
    395352        else: 
    396353            segments = path.split('/') 
    397354            parent_id = '/'.join(segments[:-1]) 
    398355            filename = len(segments) > 1 and segments[-1] 
    399356            if not filename: # if there's a trailing '/', show the list 
    400                 return self._render_list(req, context(parent_realm, parent_id)) 
    401             attachment = Attachment(self.env, parent_realm, parent_id, 
    402                                     filename) 
    403  
    404         ctx = context(attachment.parent_realm, attachment.parent_id) \ 
    405               ('attachment', attachment.filename, resource=attachment) 
    406         add_link(req, 'up', ctx.parent.resource_href(), ctx.parent.name()) 
     357                return self._render_list( 
     358                    req, parent_realm.assert_copy(id=parent_id)) 
     359 
     360        parent = parent_realm.assert_copy(id=parent_id) 
     361        attachment = parent.child('attachment', filename) 
     362         
     363        add_link(req, 'up', parent.url(req.href), parent.name) 
    407364         
    408365        if req.method == 'POST': 
    409366            if action == 'new': 
    410                 self._do_save(ctx) 
     367                self._do_save(req, attachment) 
    411368            elif action == 'delete': 
    412                 self._do_delete(ctx) 
     369                self._do_delete(req, attachment) 
    413370        elif action == 'delete': 
    414             data = self._render_confirm_delete(ctx) 
     371            data = self._render_confirm_delete(req, attachment) 
    415372        elif action == 'new': 
    416             data = self._render_form(ctx) 
    417         else: 
    418             data = self._render_view(ctx) 
    419  
    420         data['context'] = ctx 
     373            data = self._render_form(req, attachment) 
     374        else: 
     375            data = self._render_view(req, attachment) 
     376 
     377        data['resource'] = attachment 
    421378 
    422379        add_stylesheet(req, 'common/css/code.css') 
    423380        return 'attachment.html', data, None 
     
    437394        """Return list of attachments allowed in the request.""" 
    438395        return AttachmentList(parent, 
    439396            [a for a in Attachment.select(self.env, parent.realm, parent.id) 
    440              if 'ATTACHMENT_VIEW' in req.perm(parent('attachment', a.filename))]) 
     397             if parent.child('attachment', a.filename)]) 
    441398 
    442399    def get_history(self, start, stop, realm): 
    443400        """Return an iterable of tuples describing changes to attachments on 
     
    458415            time = datetime.fromtimestamp(ts, utc) 
    459416            yield ('created', realm, id, filename, time, description, author) 
    460417 
    461     def get_timeline_events(self, context, start, stop): 
    462         """Return an iterable of events suitable for ITimelineEventProvider. 
    463  
    464         `context` specifies the realm. 
     418    def get_timeline_events(self, req, resource_realm, start, stop): 
     419        """Return an event generator suitable for ITimelineEventProvider. 
     420 
     421        Events are changes to attachments on resources of the given 
     422        `resource_realm.realm`. 
    465423        """ 
    466         req = context.req 
    467424        for change, realm, id, filename, time, descr, author in \ 
    468                 self.get_history(start, stop, context.realm): 
    469             ctx = context(realm=realm, id=id)('attachment', filename) 
    470             if 'ATTACHMENT_VIEW' not in req.perm(ctx): 
    471                 continue 
    472             title = tag(tag.em(os.path.basename(filename)), ' attached to ', 
    473                         tag.em(ctx.parent.name(), title=ctx.parent.summary())) 
    474             event = TimelineEvent(self, 'attachment') 
    475             event.set_changeinfo(time, author) 
    476             event.add_markup(title=title) 
    477             event.add_wiki(ctx, body=descr) 
    478             yield event 
     425                self.get_history(start, stop, resource_realm.realm): 
     426            parent = resource_realm(id=id) 
     427            if parent: 
     428                attachment = parent.child('attachment', filename) 
     429                if attachment: 
     430                    title = tag(tag.em(os.path.basename(filename)), 
     431                                _(" attached to "), 
     432                                tag.em(parent.name, title=parent.summary)) 
     433                    event = TimelineEvent(self, 'attachment') 
     434                    event.set_changeinfo(time, author) 
     435                    event.add_markup(title=title) 
     436                    event.add_wiki(parent, body=descr) 
     437                    yield event 
    479438 
    480439    def event_formatter(self, event, key): 
    481440        return None 
    482441     
    483     # IContextProvider methods 
    484  
    485     def get_context_classes(self): 
    486         yield AttachmentContext 
     442    # IResourceManager methods 
     443     
     444    def get_resource_realms(self): 
     445        yield 'attachment' 
     446 
     447    def get_model(self, resource): 
     448        return Attachment(resource.env, resource.parent.realm, 
     449                          resource.parent.id, filename=resource.id) 
     450 
     451    def get_resource_url(self, resource, href, path=None, **kwargs): 
     452        """Return an URL to the attachment itself. 
     453 
     454        A `format` keyword argument equal to `'raw'` will be converted 
     455        to the raw-attachment prefix. 
     456        """ 
     457        format = kwargs.get('format') 
     458        prefix = 'attachment' 
     459        if format == 'raw': 
     460            kwargs.pop('format') 
     461            prefix = 'raw-attachment' 
     462        path = [unicode(p) for p in 
     463                [prefix, resource.parent.realm, resource.parent.id, 
     464                 resource.id, path] if p] 
     465        return ResourceSystem(self.env).get_resource_url( 
     466            resource, href, '/' + '/'.join(path), **kwargs) 
     467 
     468    def get_resource_description(self, resource, format=None): 
     469        if format == 'compact': 
     470            return '%s:%s' % (resource.parent.shortname, resource.filename) 
     471        elif format == 'summary': 
     472            return resource.model.description 
     473        if resource.id: 
     474            return _("Attachment '%(id)s' in %(parent)s", id=resource.id, 
     475                     parent=resource.parent.name) 
     476        else: 
     477            return _("Attachments of %(parent)s", parent=resource.parent.name) 
    487478 
    488479    # Internal methods 
    489480 
    490     def _do_save(self, context): 
    491         req, attachment = context.req, context.resource 
    492         req.perm.require('ATTACHMENT_CREATE', context) 
     481    def _do_save(self, req, attachment): 
     482        attachment.perm.require('ATTACHMENT_CREATE') 
    493483 
    494484        if 'cancel' in req.args: 
    495             req.redirect(context.parent.resource_href()) 
     485            req.redirect(attachment.parent.url(req.href)) 
    496486 
    497487        upload = req.args['attachment'] 
    498488        if not hasattr(upload, 'filename') or not upload.filename: 
     
    520510        filename = os.path.basename(filename) 
    521511        if not filename: 
    522512            raise TracError(_('No file uploaded')) 
    523         # Now the filename is known, update the attachment context 
    524         context.id = filename 
    525  
    526         attachment.description = req.args.get('description', '') 
    527         attachment.author = get_reporter_id(req, 'author') 
    528         attachment.ipnr = req.remote_addr 
     513        # Now the filename is known, update the attachment resource 
     514        attachment.id = filename 
     515 
     516        model = Attachment(self.env, parent_realm, path)  
     517        model.description = req.args.get('description', '') 
     518        model.author = get_reporter_id(req, 'author') 
     519        model.ipnr = req.remote_addr 
    529520 
    530521        # Validate attachment 
    531522        for manipulator in self.manipulators: 
    532             for field, message in manipulator.validate_attachment(req, 
    533                                                                   attachment): 
     523            for field, message in manipulator.validate_attachment(req, model): 
    534524                if field: 
    535525                    raise InvalidAttachment(_('Attachment field %(field)s is ' 
    536526                                              'invalid: %(message)s', 
     
    541531 
    542532        if req.args.get('replace'): 
    543533            try: 
    544                 old_attachment = Attachment(self.env, attachment.parent_realm, 
    545                                             attachment.parent_id, filename) 
     534                old_attachment = Attachment(self.env, attachment.parent.realm, 
     535                                            attachment.parent.id, filename) 
    546536                if not (old_attachment.author and req.authname \ 
    547537                        and old_attachment.author == req.authname): 
    548                     req.perm.require('ATTACHMENT_DELETE', context) 
     538                    attachment.perm.require('ATTACHMENT_DELETE') 
    549539                old_attachment.delete() 
    550540            except TracError: 
    551541                pass # don't worry if there's nothing to replace 
    552             attachment.filename = None 
    553         attachment.insert(filename, upload.file, size) 
     542            model.filename = None 
     543        model.insert(filename, upload.file, size) 
    554544 
    555545        # Redirect the user to list of attachments (must add a trailing '/') 
    556         req.redirect(context.resource_href('..') + '/') 
    557  
    558     def _do_delete(self, context): 
    559         req, attachment = context.req, context.resource 
    560         req.perm.require('ATTACHMENT_DELETE', context) 
    561  
    562         parent_href = context.parent.resource_href() 
     546        req.redirect(attachment.url(req.href, '..') + '/') 
     547 
     548    def _do_delete(self, req, attachment): 
     549        resource.perm.require('ATTACHMENT_DELETE') 
     550 
     551        parent_href = attachment.parent.url(req.href) 
    563552        if 'cancel' in req.args: 
    564553            req.redirect(parent_href) 
    565554 
    566         context.resource.delete() 
    567  
    568         # Redirect the user to the attachment parent page 
     555        attachment.model.delete() 
    569556        req.redirect(parent_href) 
    570557 
    571     def _render_confirm_delete(self, context): 
    572         req, attachment = context.req, context.resource 
    573         req.perm.require('ATTACHMENT_DELETE', context) 
    574          
    575         attachment = context.resource 
    576         return {'mode': 'delete', 
    577                 'title': _('%(context)s (delete)', context=context.name()), 
     558    def _render_confirm_delete(self, req, attachment): 
     559        attachment.perm.require('ATTACHMENT_DELETE') 
     560        return {'mode': 'delete', 'title': _('%(attachment)s (delete)', 
     561                                             attachment=attachment.name), 
     562                'attachment': attachment.model} # FIXME 
     563 
     564    def _render_form(self, req, attachment): 
     565        attachment.perm.require('ATTACHMENT_CREATE') 
     566        return {'mode': 'new', 'author': get_reporter_id(req), 
    578567                'attachment': attachment} 
    579568 
    580     def _render_form(self, context): 
    581         req, attachment = context.req, context.resource 
    582         req.perm.require('ATTACHMENT_CREATE', context) 
    583  
    584         return {'mode': 'new', 'author': get_reporter_id(context.req)} 
    585  
    586     def _render_list(self, req, context): 
    587         context.req.perm.require('ATTACHMENT_VIEW', context('attachment')) 
    588  
     569    def _render_list(self, req, parent): 
     570        attachment = parent.assert_child('attachment') 
    589571        data = { 
    590             'mode': 'list', 'context': None, # no specific attachment 
    591             'attachments': self.permitted_attachment_list(req, context)} 
    592  
    593         add_link(context.req, 'up', context.resource_href(), context.name()) 
     572            'mode': 'list', 'attachment': None, # no specific attachment 
     573            'attachments': self.permitted_attachment_list(req, parent)} 
     574 
     575        add_link(req, 'up', parent.url(req.href), parent.name) 
    594576 
    595577        return 'attachment.html', data, None 
    596578 
    597     def _render_view(self, context): 
    598         req, attachment = context.req, context.resource 
    599         req.perm.require('ATTACHMENT_VIEW', context) 
    600  
    601         req.check_modified(attachment.date) 
    602  
    603         data = {'mode': 'view', 'title': context.name(), 
    604                 'attachment': attachment} 
    605  
    606         fd = attachment.open() 
     579    def _render_view(self, req, attachment): 
     580        req.check_modified(attachment.model.date) 
     581 
     582        data = {'mode': 'view', 'title': attachment.name, 
     583                'attachment': attachment}  
     584 
     585        fd = attachment.model.open() 
    607586        try: 
    608587            mimeview = Mimeview(self.env) 
    609588 
     
    611590            str_data = fd.read(1000) 
    612591            fd.seek(0) 
    613592             
    614             mime_type = mimeview.get_mimetype(attachment.filename, str_data) 
     593            mime_type = mimeview.get_mimetype(attachment.model.filename, 
     594                                              str_data) 
    615595 
    616596            # Eventually send the file directly 
    617597            format = req.args.get('format') 
     
    628608                if 'charset=' not in mime_type: 
    629609                    charset = mimeview.get_charset(str_data, mime_type) 
    630610                    mime_type = mime_type + '; charset=' + charset 
    631                 req.send_file(attachment.path, mime_type) 
     611                req.send_file(attachment.model.path, mime_type) 
    632612 
    633613            # add ''Plain Text'' alternate link if needed 
    634614            if (self.render_unsafe_content and  
    635615                mime_type and not mime_type.startswith('text/plain')): 
    636                 plaintext_href = context.resource_href(format='txt') 
     616                plaintext_href = attachment.url(req.href, format='txt') 
    637617                add_link(req, 'alternate', plaintext_href, _('Plain Text'), 
    638618                         mime_type) 
    639619 
    640620            # add ''Original Format'' alternate link (always) 
    641             raw_href = context.resource_href(format='raw') 
     621            raw_href = attachment.url(req.href, format='raw') 
    642622            add_link(req, 'alternate', raw_href, _('Original Format'), 
    643623                     mime_type) 
    644624 
    645625            self.log.debug("Rendering preview of file %s with mime-type %s" 
    646                            % (attachment.filename, mime_type)) 
    647  
     626                           % (attachment.model.filename, mime_type)) 
     627 
     628            context = RenderingContext(req.href, attachment) 
    648629            data['preview'] = mimeview.preview_data( 
    649630                context, fd, os.fstat(fd.fileno()).st_size, mime_type, 
    650                 attachment.filename, raw_href, annotations=['lineno']) 
     631                attachment.model.filename, raw_href, annotations=['lineno']) 
    651632            return data 
    652633        finally: 
    653634            fd.close() 
     
    655636    def _format_link(self, formatter, ns, target, label): 
    656637        link, params, fragment = formatter.split_link(target) 
    657638        ids = link.split(':', 2) 
    658         context = None 
     639        attachment = None 
    659640        if len(ids) == 3: 
    660641            known_realms = ResourceSystem(self.env).get_known_realms() 
    661642            # new-style attachment: TracLinks (filename:realm:id) 
    662643            if ids[1] in known_realms: 
    663                 context = formatter.context(ids[1], ids[2]) \ 
    664                           ('attachment', ids[0]) 
     644                parent = formatter.perm(ids[1], ids[2]) 
     645                if parent: 
     646                    attachment = parent.child('attachment', ids[0]) 
    665647            else: # try old-style attachment: TracLinks (realm:id:filename) 
    666648                if ids[0] in known_realms: 
    667                     context = formatter.context(ids[0], ids[1]) \ 
    668                               ('attachment', ids[2]) 
     649                    parent = formatter.perm(ids[0], ids[1]) 
     650                    if parent: 
     651                        attachment = parent.child('attachment', ids[2]) 
    669652        else: # local attachment: TracLinks (filename) 
    670             context = formatter.context('attachment', link) 
    671         if context: 
     653            attachment = formatter.resource.child('attachment', link) 
     654        if attachment: 
    672655            try: 
    673                 attachment = context.resource 
     656                check_existing = attachment.model # TODO: verify 
    674657                format = None 
    675658                if ns.startswith('raw'): 
    676659                    format = 'raw' 
    677660                return tag.a(label, class_='attachment', 
    678                              href=context.resource_href(format=format) + params, 
    679                              title=context.name()) 
     661                             href=(attachment.url(formatter.href, 
     662                                                  format=format) + params), 
     663                             title=attachment.name) 
    680664            except TracError, e: 
    681665                pass 
    682666        return tag.a(label, class_='missing attachment', rel='nofollow') 
     
    697681                              'milestone': 'MILESTONE_DELETE'}, 
    698682    } 
    699683 
    700     def check_permission(self, username, action, context): 
     684    def check_permission(self, username, action, resource): 
    701685        perm_map = self._perm_maps.get(action) 
    702686        if perm_map: 
    703             legacy_action = perm_map.get(context.resource.parent_realm) 
     687            legacy_action = perm_map.get(resource.parent.realm) 
    704688            if legacy_action: 
    705689                decision = PermissionSystem(self.env) \ 
    706                     .check_permission(legacy_action, username, context) 
     690                    .check_permission(legacy_action, username, resource) 
    707691                if not decision: 
    708692                    self.env.log.debug('LegacyAttachmentPolicy denied %s ' 
    709693                                       'access to %s. User needs %s' % 
    710                                        (username, context, legacy_action)) 
     694                                       (username, resource, legacy_action)) 
    711695                return decision 
  • trac/mimeview/api.py

    diff -r e0c9d8655e31 -r 263dd7d3d361 trac/mimeview/api.py
    a b  
    7171 
    7272 
    7373__all__ = ['get_mimetype', 'is_binary', 'detect_unicode', 'Mimeview', 
    74            'content_to_unicode'] 
     74           'content_to_unicode', 'RenderingContext'] 
    7575 
    7676 
    7777# Some common MIME types and their associated keywords and/or file extensions 
     
    215215    return mimeview.to_unicode(content, mimetype) 
    216216 
    217217 
     218class RenderingContext(object): 
     219    """Rendering contexts. 
     220 
     221    This specifies ''how'' a rendering should be done, with various 
     222    options that might be relevant to some or all the renderers. 
     223 
     224    A context stores the `href` used as a base for building URLs. 
     225    If not given, this reverts to the value of `env.abs_href`. 
     226     
     227    A resource can also be associated to a rendering context, and that resource 
     228    will be used as the base content when rendering relative TracLinks. 
     229 
     230    Another aspect related to the access context consists of the scope or 
     231    context trail by which the information belonging to a context is 
     232    presented. It is quite usual that contexts are embedded in other 
     233    contexts. This can be known by querying the `parent` context, which 
     234    is automatically set when creating a subcontext from another context. XXX 
     235 
     236    For example, when rendering a ticket description from within a 
     237    Custom Query rendered by the TicketQuery macro inside a wiki page, 
     238    the context ''path'' will be: 
     239 
     240    context.resource.(realm, id) = ('ticket', '12') 
     241    context.parent.resource.(realm, id) = ('wiki', 'CurrentStatus') 
     242 
     243    Finally, the context could also know about the expected output MIME type 
     244    which should be used to present the information to the user (TODO) 
     245    """ 
     246 
     247    def __init__(self, req, resource, parent=None, href=None, **kwargs): 
     248        self.req = req 
     249        self.resource = resource or req.perm.toplevel() 
     250        self.env = self.resource.env 
     251        self.perm = self.resource.perm 
     252        self.parent = parent 
     253        self.href = href or self.env.abs_href 
     254        self.properties = kwargs 
     255 
     256    def __repr__(self): 
     257        path = [] 
     258        context = self 
     259        while context: 
     260            if context.resource.realm: # skip toplevel resource 
     261                path.append(unicode(context.resource)) 
     262            context = context.parent 
     263        return '<RenderingContext %s>' % (' - '.join(reversed(path))) 
     264 
     265    def __call__(self, resource, href=None, **kwargs): 
     266        """Return a sub-`RenderingContext`. 
     267 
     268        'href' can be modified on the fly, which is usefull to e.g. turn URL 
     269        generation to absolute mode. 
     270 
     271        'resource' can be modified this way as well. 
     272        Any remaining keyword argument will be treated as a new property. 
     273        """ 
     274        return RenderingContext(self.req, resource, self, href or self.href, 
     275                                **kwargs) 
     276     
     277    def __contains__(self, resource): 
     278        """Check whether a given resource is in the rendering path. 
     279 
     280        This is useful for avoiding to render resources recursively. 
     281        """ 
     282        context = self 
     283        while context: 
     284            if context.resource == resource: 
     285                return True 
     286            context = context.parent 
     287 
     288 
    218289class IHTMLPreviewRenderer(Interface): 
    219290    """Extension point interface for components that add HTML renderers of 
    220291    specific content types to the `Mimeview` component. 
     
    248319        """ 
    249320 
    250321    def render(context, mimetype, content, filename=None, url=None): 
    251         """Render an XHTML preview of the raw `content` within a Context. 
     322        """Render an XHTML preview of the raw `content` in context. 
     323 
     324        `context` is a `RenderingContext`. 
    252325 
    253326        The `content` might be: 
    254327         * a `str` object 
     
    292365        """Return the XHTML markup for the table cell that contains the 
    293366        annotation data. 
    294367 
    295         `context` is the context corresponding to the content being annotated, 
     368        `context` is the `RenderingContext` corresponding to the content being 
     369        annotated, 
    296370        `row` is the tr Element being built, `number` is the line number being 
    297371        processed and `line` is the line's actual content. 
    298372        `annotations` is whatever additional data the `get_annotation_data` 
  • trac/perm.py

    diff -r e0c9d8655e31 -r 263dd7d3d361 trac/perm.py
    a b  
    1919"""Management of permissions.""" 
    2020 
    2121from trac.config import ExtensionOption, OrderedExtensionsOption 
     22from trac.context import Resource 
    2223from trac.core import * 
    2324from trac.util.compat import set 
    2425from trac.util.translation import _ 
     
    3031class PermissionError(StandardError): 
    3132    """Insufficient permissions to complete the operation""" 
    3233 
    33     def __init__ (self, action=None): 
     34    def __init__ (self, action=None, resource=None): 
    3435        StandardError.__init__(self) 
    3536        self.action = action 
     37        self.resource = resource 
    3638 
    3739    def __str__ (self): 
    3840        if self.action: 
    39             return _('%(perm)s privileges are required to perform this operation.', 
    40                      perm=self.action)