text: # -*- coding: utf-8 -*-
  #
  # Copyright (C) 2007-2013 Edgewall Software
  # Copyright (C) 2007 Eli Carter <retracile@gmail.com>
  # 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 collections import OrderedDict
  
  from trac.core import Component, implements
  from trac.perm import IPermissionRequestor
  from trac.ticket.api import ITicketActionController
  from trac.ticket.default_workflow import ConfigurableTicketWorkflow
  from trac.util import to_list
  from trac.util.html import tag
  
  revision = "$Rev$"
  url = "$URL$"
  
  
  class CodeReviewActionController(Component):
      """Support for simple code reviews.
  
      The action that supports the `code_review` operation will present
      an extra choice for the review decision. Depending on that decision,
      a specific state will be selected.
  
      Example (from the enterprise-review-workflow.ini):
      {{{#!ini
      request_review = in_work -> in_review
      review = in_review -> *
      review.label = review as
      review.operations = code_review
      review.code_review =
        approve -> in_QA,
        approve as noted -> post_review,
        request changes -> in_work
      }}}
  
      Don't forget to add the `CodeReviewActionController` to the workflow
      option in the `[ticket]` section in TracIni. When added to the default
      value of `workflow`, the line will look like this:
      {{{#!ini
      workflow = ConfigurableTicketWorkflow,CodeReviewActionController
      }}}
      """
  
      implements(IPermissionRequestor, ITicketActionController)
  
      # IPermissionRequestor methods
  
      def get_permission_actions(self):
          return ['TICKET_REVIEW']
  
      # ITicketActionController methods
  
      def get_ticket_actions(self, req, ticket):
          # The review action is available in those status where it has been
          # configured, for those users who have the TICKET_REVIEW permission, as
          # long as they are not the owner of the ticket (you can't review your
          # own work).
          actions_we_handle = []
          if req.authname != ticket['owner'] and \
                      'TICKET_REVIEW' in req.perm(ticket.resource):
              controller = ConfigurableTicketWorkflow(self.env)
              actions_we_handle = controller.get_actions_by_operation_for_req(
                  req, ticket, 'code_review')
          self.log.debug('code review handles actions: %r', actions_we_handle)
          return actions_we_handle
  
      def get_all_status(self):
          all_status = set()
          controller = ConfigurableTicketWorkflow(self.env)
          for weight, action \
                  in controller.get_actions_by_operation('code_review'):
              review_options = self._get_review_options(action)
              all_status.update(review_options.itervalues())
          return all_status
  
      def render_ticket_action_control(self, req, ticket, action):
          id, selected = self._get_selected(req, action)
  
          review_options = self._get_review_options(action)
          actions = ConfigurableTicketWorkflow(self.env).actions
  
          label = actions[action]['label']
          control = tag(["as: ",
                         tag.select([
                             tag.option(option,
                                        selected=(option == selected or None))
                             for option in review_options],
                             name=id, id=id)])
          if selected:
              new_status = self._get_new_status(req, action, review_options)
              hint = "Next status will be '%s'" % new_status
          else:
              hint = "Next status will be one of " + \
                     ', '.join("'%s'" % st for st in review_options.itervalues())
          return label, control, hint
  
      def get_ticket_changes(self, req, ticket, action):
          new_status = self._get_new_status(req, action)
          return {'status': new_status or 'new'}
  
      def apply_action_side_effects(self, req, ticket, action):
          pass
  
      # Internal methods
  
      def _get_selected(self, req, action):
          id = 'action_%s_code_review' % action
          return id, req.args.get(id)
  
      def _get_review_options(self, action):
          values = self.config.getlist('ticket-workflow', action + '.code_review')
          return OrderedDict(to_list(v, sep='->') for v in values)
  
      def _get_new_status(self, req, action, review_options=None):
          selected = self._get_selected(req, action)[1]
          if not review_options:
              review_options = self._get_review_options(action)
          return review_options[selected]
  