Edgewall Software
Modify

Opened 10 years ago

Last modified 10 years ago

#11477 new enhancement

Extend ITicketManipulator (or similar) - Filter Comments

Reported by: anonymous Owned by:
Priority: normal Milestone: unscheduled
Component: ticket system Version: 1.0.1
Severity: normal Keywords: comment
Cc: olemis+trac@… Branch:
Release Notes:
API Changes:
Internal Changes:

Description (last modified by Peter Suter)

Example:

Version 1.0.1 code in ticket/web_ui.py

def _get_comment_history(self, req, ticket, cnum):
    history = []
    for version, date, author, comment in ticket.get_comment_history(cnum):
        history.append({
            'version': version, 'date': date, 'author': author,
            'comment': _("''Initial version''") if version == 0 else '',
            'value': comment,
            'url': self._make_comment_url(req, ticket, cnum, version)
        })
    return history

If ITicketManipulator in ticket/api.py contained a method like:

def comment_filter(self, req, version, date, author, comment):
   """Validate if a comment can be displayed
    Must return a 4-tuple corresponding to version, date, author, comment
    The 4 may be empty or None to "nullify display of the comment
    """

The code from above in web-ui.py could be rewritten as:

def _get_comment_history(self, req, ticket, cnum):
    history = []
    for version, date, author, comment in ticket.get_comment_history(cnum):
        for manipulator in self.ticket_manipulators:
            version, date, author, comment = manipulator.comment_filter(req, version, date, author, comment)
        if version or date or author or comment:
            # Only if the comment has not been fully "nullified" add it to the list to display
            history.append({
                'version': version, 'date': date, 'author': author,
                'comment': _("''Initial version''") if version == 0 else '',
                'value': comment,
                'url': self._make_comment_url(req, ticket, cnum, version)
                })
    return history

This allows filtering of comments by Plugins which can create set of rules (user, group, permission based, …)

The existing th:PrivateCommentPlugin is broken beyond 0.12 because it filters the Template Stream and needs to adapt to changes in the stream.

The proposed modification allows filtering "objects" rather than content and therefore eases up the creation of a Comment Filtering Plugin.

Attachments (0)

Change History (5)

comment:1 by Ryan J Ollos, 10 years ago

Milestone: 1.0.2

comment:2 by Olemis Lang <olemis+trac@…>, 10 years ago

Cc: olemis+trac@… added

comment:3 by anonymous, 10 years ago

Being a "trac" newbie I thought my solution above would be good but failed to understand I was mixing "comment history" and "changelog".

This time I went down to the arena to implement a "changelog entries" filter and apply the modifications needed to trac.

In ticket/api.py

class ITicketChangelogFilter(Interface):
    """Extension point interface for components that filter
    changelog entries when they are being retrieved for display."""

    def changelog_filter(req, changelog):
        """
        Called when a list of changelog entries is being retrieved
        for display.
        MUST return a list of changelog entries

        Each changelog entry has the following fields:
          date, author, field, old, new, permanent
        """

In ticket/web_ui.py

1st the import

from trac.ticket.api import TicketSystem, ITicketManipulator, ITicketChangelogFilter

The definition of changelog_filters in TicketModule

class TicketModule(Component):

    implements(IContentConverter, INavigationContributor, IRequestHandler,
               ISearchSource, ITemplateProvider, ITimelineEventProvider)

    # ADD THE EXTENSION POINT FOR IticketChangelogFilter
    changelog_filters = ExtensionPoint(ITicketChangelogFilter)

Modify grouped_changelog_entries, because this is the only method calling Ticket.get_changelog

Modifications are marked with comments

    # NOTICE THE ADDED 'req' parameter
    # IT IS NEEDED TO PASS IT DOWN TO ITicketChangelogFilter entities

    def grouped_changelog_entries(self, req, ticket, db=None, when=None):
        """Iterate on changelog entries, consolidating related changes
        in a `dict` object.

        :since 1.0: the `db` parameter is no longer needed and will be removed
        in version 1.1.1
        """
        field_labels = TicketSystem(self.env).get_ticket_field_labels()
        changelog = ticket.get_changelog(when=when)
        # BEGIN FILTERING OF CHANGELOG ENTRIES
        for clogfilter in self.changelog_filters:
            changelog = clogfilter.changelog_filter(req, changelog)
        # END FILTERING OF CHANGELOG ENTRIES

And then modify the call to "grouped_changelog_entries" made from "rendered_changelog_entries" to pass the needed 'req' parameter

    def rendered_changelog_entries(self, req, ticket, when=None):
        """Iterate on changelog entries, consolidating related changes
        in a `dict` object.
        """
        attachment_realm = ticket.resource.child('attachment')
        # NOTE -- ADDED req as parameter in the call
        for group in self.grouped_changelog_entries(req, ticket, when=when):

Once all this is done we can quickly implement a plugin to filter changelog entries where comments made by "adam" will not be seen by "eve"

from trac.core import Component, implements
from trac.ticket.api import ITicketChangelogFilter

class ChangelogFilter(Component):
    implements(ITicketChangelogFilter)

    # ITemplateStreamFilter methods
    def changelog_filter(self, req, changelog):
        # step backwards to delete without affecting indexes
        self.log.error('comment_filter called: changelog len %d' % len(changelog))
        for i in xrange(len(changelog) - 1, -1, -1):
            date, author, field, old, new, permanent = changelog[i]
            if author == 'adam' and field == 'comment' and req.username == 'eve':
                del changelog[i]
        return changelog

The solution isn't perfect because the changelog entries can still be retrieved over the XMLRPC plugin. But modifying Ticket.get_changelog in ticket/model.py seems a modification too deep in the system (given that model.py has not single ExtensionPoint defined so far)

comment:4 by anonymous, 10 years ago

Diff and sample plugin (installable) uploaded to github

Best regards

comment:5 by Peter Suter, 10 years ago

Description: modified (diff)
Keywords: comment added
Milestone: unscheduled

Please, if you propose more than a couple lines of code changes just attach a patch. For enhancements start from trunk.

Note that grouped_changelog_entries can probably not be changed to receive a req for compatibility reasons. Also it is called from TicketNotifyEmail where no web request is available.

Modify Ticket

Change Properties
Set your email in Preferences
Action
as new The ticket will remain with no owner.
The ticket will be disowned.
as The resolution will be set. Next status will be 'closed'.
The owner will be changed from (none) to anonymous. Next status will be 'assigned'.

Add Comment


E-mail address and name can be saved in the Preferences .
 
Note: See TracTickets for help on using tickets.