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
|
# -*- coding: utf-8 -*-
#
# Copyright (C) 2012-2023 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 https://trac.edgewall.org/wiki/TracLicense.
#
# This software consists of voluntary contributions made by many
# individuals. For the exact contribution history, see the revision
# history and logs, available at https://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 import to_list
from trac.util.translation import _
from trac.web.chrome import add_warning
revision = "$Rev$"
url = "$URL$"
class MilestoneOperation(Component):
"""Sets milestone for specified resolutions.
Example:
{{{#!ini
[ticket-workflow]
resolve.operations = set_resolution,set_milestone
resolve.milestone = invalid,wontfix,duplicate,worksforme -> rejected
}}}
When setting resolution to `duplicate` the milestone will
automatically change to `rejected`. If user changes milestone
manually when resolving the ticket, this workflow operation has
''no effect''.
Don't forget to add `MilestoneOperation` to the `workflow` option
in the `[ticket]` section of TracIni. When added to the default
value of `workflow`, the line will look like this:
{{{#!ini
[ticket]
workflow = ConfigurableTicketWorkflow,MilestoneOperation
}}}
"""
implements(ITicketActionController)
def get_ticket_actions(self, req, ticket):
controller = ConfigurableTicketWorkflow(self.env)
return controller.get_actions_by_operation_for_req(req, ticket,
'set_milestone')
def get_all_status(self):
return []
def render_ticket_action_control(self, req, ticket, action):
actions = ConfigurableTicketWorkflow(self.env).actions
label = actions[action]['label']
hint = None
old_milestone = ticket._old.get('milestone')
if old_milestone is None:
resolutions, milestone = \
self._get_resolutions_and_milestone(action)
if resolutions:
try:
Milestone(self.env, milestone)
except ResourceNotFound:
pass
else:
res_hint = ', '.join("'%s'" % r for r in resolutions)
hint = _("For resolution %(resolutions)s the milestone "
"will be set to '%(milestone)s'.",
resolutions=res_hint, milestone=milestone)
return label, None, hint
def get_ticket_changes(self, req, ticket, action):
old_milestone = ticket._old.get('milestone')
if old_milestone is None:
new_milestone = self._get_resolutions_and_milestone(action)[1]
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_resolutions_and_milestone(self, action):
transitions = self.config.get('ticket-workflow', action + '.milestone')
milestone = None
resolutions = []
try:
transition = to_list(transitions, sep='->')
except ValueError:
pass
else:
if len(transition) == 2:
resolutions = to_list(transition[0])
milestone = transition[1]
return resolutions, milestone
|