1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
|
# -*- coding: utf-8 -*-
#
# Copyright (C) 2002-2013 Edgewall Software
# Copyright (C) 2012 Franz Mayer <franz.mayer@gefasoft.de>
# All rights reserved.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at http://trac.edgewall.com/license.html.
#
# This software consists of voluntary contributions made by many
# individuals. For the exact contribution history, see the revision
# history and logs, available at http://trac.edgewall.org/.
from genshi.builder import tag
from trac.core import Component, implements
from trac.resource import ResourceNotFound
from trac.ticket.api import ITicketActionController
from trac.ticket.default_workflow import ConfigurableTicketWorkflow
from trac.ticket.model import Milestone
from trac.util.translation import _
from trac.web.chrome import add_warning
revision = "$Rev$"
url = "$URL$"
class MilestoneOperation(Component):
"""Sets milestone for specific status.
=== Example ===
{{{
[ticket-workflow]
resolve.operations = set_resolution,set_milestone
resolve.milestone = invalid,wontfix,duplicate,worksforme->rejected
}}}
When setting status to `duplicate` the milestone will automatically change
to `rejected`.
'''Note:''' if user has changed milestone manually, this workflow operation
has ''no effect''!
=== Configuration ===
Don't forget to add `MilestoneOperation` to the workflow option
in `[ticket]` section. If there is no workflow option, the line will look
like this:
{{{
[ticket]
workflow = ConfigurableTicketWorkflow,MilestoneOperation
}}}
"""
implements(ITicketActionController)
def get_ticket_actions(self, req, ticket):
actions_we_handle = []
if req.authname != 'anonymous' and \
'TICKET_MODIFY' in req.perm(ticket.resource):
controller = ConfigurableTicketWorkflow(self.env)
actions_we_handle = controller.get_actions_by_operation_for_req(
req, ticket, 'set_milestone')
self.log.debug('set_milestone handles actions: %r' % actions_we_handle)
return actions_we_handle
def get_all_status(self):
return []
def render_ticket_action_control(self, req, ticket, action):
actions = ConfigurableTicketWorkflow(self.env).actions
label = actions[action]['name']
res_ms = self.__get_resolution_milestone_dict(ticket, action)
resolutions = ''
milestone = None
for i, resolution in enumerate(res_ms):
if i > 0:
resolutions = "%s, '%s'" % (resolutions, resolution)
else:
resolutions = "'%s'" % resolution
milestone = res_ms[resolution]
hint = _("For resolution %(resolutions)s the milestone will be "
"set to '%(milestone)s'.",
resolutions=resolutions, milestone=milestone)
return (label, None, hint)
def get_ticket_changes(self, req, ticket, action):
if action == 'resolve' and \
req.args and 'action_resolve_resolve_resolution' in req.args:
old_milestone = ticket._old.get('milestone') or None
user_milestone = ticket['milestone'] or None
# If there's no user defined milestone, we try to set it
# using the defined resolution -> milestone mapping.
if old_milestone is None:
new_status = req.args['action_resolve_resolve_resolution']
new_milestone = self.__get_new_milestone(ticket, action,
new_status)
# ... but we don't reset it to None unless it was None
if new_milestone is not None or user_milestone is None:
try:
milestone = Milestone(self.env, new_milestone)
self.log.info('changed milestone from %s to %s' %
(old_milestone, new_milestone) )
return {'milestone': new_milestone}
except ResourceNotFound:
add_warning(req, _("Milestone %(name)s does not exist.",
name=new_milestone))
return {}
def apply_action_side_effects(self, req, ticket, action):
pass
def __get_new_milestone(self, ticket, action, new_status):
"""Determines the new status"""
if new_status:
res_ms = self.__get_resolution_milestone_dict(ticket, action)
return res_ms.get(new_status)
def __get_resolution_milestone_dict(self, ticket, action):
transitions = self.config.get('ticket-workflow',
action + '.milestone').strip()
transition = [x.strip() for x in transitions.split('->')]
resolutions = [y.strip() for y in transition[0].split(',')]
res_milestone = {}
for res in resolutions:
res_milestone[res] = transition[1]
return res_milestone
|