[[PageOutline(2-3)]] = 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. == Goals 1. Users with `TICKET_SIGN` permission can sign tickets. 1. Signed tickets are considered closed with resolution `signed`. 1. Signed tickets are read only, except to users with `TICKET_ADMIN` permission. 1. Signed tickets are visually distinguished. == Steps === TICKET_SIGN permission Enable the optional component [TracPermissions#CreatingNewPrivileges ExtraPermissionsProvider] and configure a new permission: {{{#!ini [extra-permissions] _perms = TICKET_SIGN }}} === Signed Resolution Add `signed` as a //Resolution// through the web admin page //Admin// -> //Ticket System// -> //Resolutions// (`/admin/ticket/resolution`), or using TracAdmin: {{{#!sh trac-admin $env resolution add signed }}} === Sign workflow step Configure your [TracWorkflow workflow] adding a step to resolve as signed: {{{#!ini [ticket-workflow] 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. {{{#!ini resolve.set_resolution = fixed, invalid, wontfix, duplicate, worksforme }}} === Readonly logic Create a [TracDev/PluginDevelopment#Singlefileplugins single file plugin] that implements [wiki:TracDev/PluginDevelopment/ExtensionPoints/trac.perm.IPermissionPolicy IPermissionPolicy]: {{{#!python # -*- coding: utf-8 -*- from trac.core import * from trac.perm import IPermissionPolicy from trac.resource import ResourceNotFound from trac.ticket.model import Ticket class ReadonlySignedTickets(Component): implements(IPermissionPolicy) allowed_actions = ('TICKET_VIEW',) 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 try: t = Ticket(self.env, resource.id) except ResourceNotFound: pass else: if t['status'] == 'closed' and t['resolution'] == 'signed': return False }}} Edit the `permission_policies` option in the [TracIni#trac-section "[trac]"] section of trac.ini, adding the component ''before'' the default [TracPermissions permission] policy: {{{#!ini [trac] permission_policies = ReadonlySignedTickets, ... }}} === Visual indication 1. Install th:ContextChromePlugin#TicketcolorbyType and enable it: {{{#!ini [components] contextchrome.style.typeclasstoticket = enabled }}} 2. Configure it to decorate based on the ticket's resolution: {{{#!ini [ticket] decorate_fields = resolution }}} 3. Add [TracInterfaceCustomization#SiteAppearance 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: {{{#!css body.resolution_is_signed { background: #f5deb3 url(signed.png); } }}} 5. Add [attachment:signed.png] to the htdocs directory. == Variations === 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`: {{{#!python 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: {{{#!python # ... if t['status'] == 'signed': return False }}} And change the workflow step to: {{{#!ini [ticket-workflow] sign = closed -> signed sign.permissions = TICKET_SIGN }}} And configure `signed` as an [CookBook/Configuration/Workflow#Multipleendstatesandmilestoneprogress end state for milestone progress]: {{{#!ini [milestone-groups] closed = closed,signed }}} Change ''resolution'' to ''status'' in the visual steps. ---- See [Trac-ML:36200 original mailing list discussion]