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 127 128 129 130 131 132 133 134
|
# -*- coding: utf-8 -*-
#
# Copyright (C) 2012-2015 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 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]['label']
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 = None
if res_ms:
try:
Milestone(self.env, milestone)
except ResourceNotFound:
pass
else:
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(self.env, new_milestone)
except ResourceNotFound:
add_warning(req, _("Milestone %(name)s does "
"not exist.", name=new_milestone))
else:
self.log.info('changed milestone from %s to %s',
old_milestone, new_milestone)
return {'milestone': 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('->')]
res_milestone = {}
if len(transition) == 2:
resolutions = [y.strip() for y in transition[0].split(',')]
for res in resolutions:
res_milestone[res] = transition[1]
return res_milestone
|