[[PageOutline]] = Workflow Discussion = The original proposal is at NewWorkflow. The [source:/sandbox/workflow workflow sandbox] contains a refactored ticket API which aims to allow plugins control over most aspects of the ticketing system. Change log is [log:sandbox/workflow here]. == API Requirements == === Control of action transitions === * This interface should be stackable (useful eg. with a `DeleteTicket` plugin which adds a single 'delete' action, which can be used in conjunction with a full workflow replacement plugin). * Plugins should register which actions they handle, and then handle them when requested to do so. * Ensure that the user has sufficient permission to perform this action. === Control of ticket fields === * Manipulate custom/built-in fields via plugin architecture. eg. hiding existing fields, changing their attributes, etc. An example of this is the `DuplicateField` plugin which removes the "duplicate" status from the "resolve" action and adds a new action "resolve as xxx". * Pluggable field types, eg. percentage bar, checkbox set, numeric input field, dollar field, etc. === Validation === * Validation of ticket date before being committed to the database. == [source:trunk/trac/ticket/web_ui.py Trunk Ticket Action Flowchart] == These two flowcharts represent the current trunk ticket creation and modification logic. === [source:trunk/trac/ticket/web_ui.py@latest#L77 New Ticket Flowchart] === attachment:workflow-new.png === [source:trunk/trac/ticket/web_ui.py@latest#L181 Modify Ticket Flowchart] === attachment:workflow-edit.png === Proposed Unified Flowchart + Extension Points === This proposes a unification of the logic for both creating and modifying tickets. In addition it adds extension points where plugins can modify the behaviour of the ticketing system. [[Image(workflow.png)]] Taking the above [wiki:WorkFlow#APIRequirements requirements] into consideration, I think the following extension points should cater for all (most?) aspects. {{{ #!python class ITicketFieldProvider(Interface): """ Provide custom ticket fields programmatically. Allows plugins to supply fields without manipulation of the [ticket-custom] section of the trac.ini file. """ def get_ticket_fields(): """ Return an iterable of custom trac.ticket.field.Field objects. """ def validate_ticket_field(name, value): """ Validate custom ticket field value after user submission. """ class ITicketFieldTypeProvider(Interface): """ Provide custom ticket field types (subclasses of trac.ticket.field.Field). """ def get_ticket_field_types(): """ Return list of (name, type) tuples, where type is a subclass of trac.ticket.field.Field. """ def validate_ticket_field_type(type, value): """ Validate a field of type provided by this extension. """ class ITicketManipulator(Interface): """ Miscellaneous manipulation of ticket workflow features. """ def prepare_ticket_for_render(self, req, ticket, fields, actions): """ Prepare the ticket fields and actions for rendering. Fields and actions can both be modified. """ def validate_ticket(self, req, ticket): """ Validate ticket state after population from user provided values. """ class ITicketActionController(Interface): """ Allows plugins to add and control ticket actions. """ def get_ticket_actions(req, ticket): """ Return an iterable of actions that are available given the current state of ticket and the request object provided. """ def get_ticket_action_controls(req, ticket, action): """ Return an iterable of tuples in the form (label, (control, ...)). Where control is an trac.ticket.field.Field object. """ def apply_ticket_action(req, ticket, action): """ Perform action on ticket. """ }}} == Tasks == === Done === * Add a `disabled` attribute to fields so that workflow hooks can disable fields but still leave them visible. * Add a `hidden` attribute for the same reason. I've actually already done this, simply by renaming the `skip` attribute to `hidden`. Did this to be more consistent with HTML. * Add a `fullrow` attribute which signifies that the form element will span both columns of the ticket property fieldset. eg. summary, type, description and reporter would all be `fullrow=1`. [2833] * Remove code specific to individual fields from ticket.cs/newticket.cs. The summary, type, description and reporter would be converted to use the the same generic code as the rest of the fields. [2833] * Remove large `if/elif` statement from ticket.cs/newticket.cs. Currently there is a large if/then/else style block which is used to display all fields other than the four described above. This could be removed and replaced with a call to form_control(). [2833] * In order to specify the order the generic field display code would use, the above changes would probably require the ticket.fields.* HDF branch to be changed to an array (currently it is a dict). This change would be in api.py (return fields in the correct display order) and the .cs files, possibly elsewhere. [2832]. * If possible I would also like to factor out the ticket field display/edit code from both ticket.cs and newticket.cs into ticket_fields.cs, as the template code is basically functionally identical. [2833] * Need a clean way to differentiate between fields that should not be displayed in the summary and those that should not be displayed at all. eg. `summary` should be hidden in the ticket summary (as it is used for the title), and should only be editable by users with `TICKET_ADMIN` privileges. Perhaps the logic should be that if a field is `hidden` it is not displayed anywhere, unless it is also `editable`, in which case it is only displayed in the ticket properties. [2837] * Add a `.plaintext` option which negates the default behaviour of WikiFormatting field labels and values (see #925, #1395 and #1791 for related information). [2849] * Add a `.title` option, or maybe `.tooltip`, though I think for the sake of consisteny it should be `.title`. [2851] === TODO === * Add an `.onchange` option for javascript field validation. * Add a `checkboxes` field type. This differs from the `checkbox` type in that it is a set of related checkboxes. It would store the data in the field value as `check1|check2|check4`. I'm not sure about this one, or how it would be presented in the query module, but I figured that people might find it useful. Similarly, a `multi_select` type could be useful. * Remove `ticket_custom_props()` macro? It does not appear to be referenced. * There are a number of locations in the ticket code where permissions are hard coded. As an example, the `TICKET_CREATE` permission is required to create a new ticket. Should this be overridable by `ITicketWorkflow` implementors? * It might be an idea to simply have a `apply_ticket_changes()` method instead of having `apply_ticket_action()` and `update_ticket_fields()`. * Add a `percentage` field type? Represented as a bar like in the milestone:0.10 view, except if you click on a position in the bar it sets the field value. This could probably just be done by setting `html_value` appropriately. == Basic Configurable Workflow == Basic workflow configuration is possible in TracIni through two new sections, `ticket-status` and `ticket-actions`. This configurability is limited to defining ticket actions and status states and their transitions with basic permission enforcement. === Defining available actions for a status === Each key under the `ticket-status` section is a ticket status and the value associated with each key is the actions available. {{{ [ticket-status] assigned = leave resolve reassign closed = leave reopen retest new = leave resolve reassign accept reopened = leave resolve reassign resolved = leave reassign reopen verify verified = leave reassign reopen retest close }}} === Mapping actions to resulting statuses === The `ticket-actions` section defines what the ticket status will be for each action, in addition to the permission required for that action. {{{ [ticket-actions] accept = assigned close = closed close.permission = TRAC_ADMIN reassign = new reopen = reopened reopen.permission = TICKET_ADMIN resolve = resolved retest = resolved retest.permission = TICKET_ADMIN verify = verified }}} == Plugabble Workflow == See the [source:sandbox/workflow/trac/ticket/api.py source] for extension points. == Available Field Types and Options == Common options: * '''label''': Descriptive label. * '''value''': Default value. * '''html_value''': This option is most useful to plugins. If it exists it is the content displayed instead of the normal '''value'''. This can be useful for fields with ticket ID's, to display the correct TracLinks for the tickets. * '''order''': Sort order placement. (Determines relative placement in forms.) * '''hidden''': Field is not displayed. Useful for storing extra ticket attributes programmatically (false by default). * '''fullrow''': Field spans a full row when displayed in the ticket properties. * '''editable''': Field is editable (true by default if field is not hidden). If a field is hidden but editable, it will not display in the ticket summary but will be displayed and editable in the ticket properties. * '''plaintext''': Field labels and values use WikiFormatting by default. To display plain text set this option to true. * '''title''': HTML title (tooltip) for this field. Types and type-specific options: * '''text''': A simple (one line) text field. * size: Size of text field. * '''checkbox''': A boolean value check box. * value: Default value (0 or 1). * '''select''': Drop-down select box. Uses a list of values. * options: List of values, separated by '''|''' (vertical pipe). * value: Default value (Item #, starting at 0). * '''radio''': Radio buttons. Essentially the same as '''select'''. * options: List of values, separated by '''|''' (vertical pipe). * value: Default value (Item #, starting at 0). * '''textarea''': Multi-line text area. * cols: Width in columns. * rows: Height in lines.