Edgewall Software

Signed Tickets

Some projects require a special workflow where tickets can be signed.

This recipe shows steps to implement various aspects of such a feature using a combination of configuration, existing plugins and a small custom permission policy containing the authorization rules.


  1. Users with TICKET_SIGN permission can sign tickets.
  2. Signed tickets are considered closed with resolution signed.
  3. Signed tickets are read only, except to users with TICKET_ADMIN permission.
  4. Signed tickets are visually distinguished.


Signed Resolution

Add signed as a Resolution through the web admin page AdminTicket SystemResolutions (/admin/ticket/resolution), or using TracAdmin:

trac-admin $env resolution add signed

Sign workflow step

Configure your workflow adding a step to resolve as signed:

sign = new,assigned,accepted,reopened -> closed
sign.name = resolve
sign.permissions = TICKET_SIGN
sign.operations = set_resolution
sign.set_resolution = signed

You probably also want to configure the list of Resolutions for other actions with a set_resolution operation, so that the Signed resolution does not appear in the list.

resolve.set_resolution = fixed, invalid, wontfix, duplicate, worksforme

Readonly logic

Create a single file plugin that implements IPermissionPolicy:

# -*- coding: utf-8 -*-

from trac.core import *
from trac.perm import IPermissionPolicy, IPermissionRequestor
from trac.resource import ResourceNotFound
from trac.ticket.model import Ticket

class ReadonlySignedTickets(Component):

    implements(IPermissionPolicy, IPermissionRequestor)

    allowed_actions = ('TICKET_VIEW',)

    # IPermissionRequestor methods

    def get_permission_actions(self):
        return ['TICKET_SIGN']

    # IPermissionPolicy methods

    def check_permission(self, action, username, resource, perm):
        if resource is None or resource.realm != 'ticket' or \
                resource.id is None or \
                action in self.allowed_actions or \
                action in ('TICKET_ADMIN', 'TRAC_ADMIN') or \
                'TICKET_ADMIN' in perm:
            return None

            t = Ticket(self.env, resource.id)
        except ResourceNotFound:
            if t['status'] == 'closed' and t['resolution'] == 'signed':
                return False

Edit the permission_policies option in the [trac] section of trac.ini, adding the component before the default permission policy:

permission_policies = ReadonlySignedTickets, ...

Visual indication

  1. Install th:ContextChromePlugin#TicketcolorbyType and enable it:
    contextchrome.style.typeclasstoticket = enabled
  2. Configure it to decorate based on the ticket's resolution:
    decorate_fields = resolution
  3. Add site.html (the first code snippet in that section) to your Environment templates directory.
  4. Add style.css to your Environment htdocs directory. Add CSS using the selector body.resolution_is_signed. For example:
    body.resolution_is_signed {
        background: #f5deb3 url(signed.png);
  5. Add signed.png to the htdocs directory.


Allow ticket comments

Additional permissions can be specified in allowed_actions. If you wish to allow commenting on signed tickets, add TICKET_APPEND to the allowed_actions:

    allowed_actions = ('TICKET_VIEW', 'TICKET_APPEND')

Status instead of resolution

If you want to use a signed status instead of a resolution, change the last two lines of code to:

         # ...
        if t['status'] == 'signed':
            return False

And change the workflow step to:

sign = closed -> signed
sign.permissions = TICKET_SIGN

And configure signed as an end state for milestone progress:

closed = closed,signed

Change resolution to status in the visual steps.

See original mailing list discussion

Last modified 7 years ago Last modified on May 8, 2017, 12:30:51 AM

Attachments (1)

Download all attachments as: .zip

Note: See TracWiki for help on using the wiki.