Beispiel #1
0
    def apply_preset(self, req, tickets, preset=None):
        if preset is None:
            return tickets
            
        presets = preset and [kw.split('=', 1) for kw in preset.split('&')] or []
        fields = dict([(field, value) for field, value in presets])

        warn = []
        modified_tickets = []
        if tickets and presets:
            db = self.env.get_db_cnx()
            ticket_module = TicketModule(self.env)
            action = fields.get('action')

            for ticket_id in tickets:
                if 'TICKET_CHGPROP' in req.perm('ticket', ticket_id):
                    ticket  = Ticket(self.env, ticket_id, db)
                    ticket.populate(fields)
                    if action:
                        field_changes, problems = ticket_module.get_ticket_changes(req, ticket, action)
                        if problems:
                            for problem in problems:
                                warn.append(problem)
                        ticket_module._apply_ticket_changes(ticket, field_changes) # Apply changes made by the workflow

                    ticket.save_changes(req.authname, None, db=db)
                    modified_tickets.append(ticket_id)
                else:
                    warn.append(_("You have no permission to modify ticket '%(ticket)s'", ticket=ticket_id))
            db.commit()
        return { 'tickets' : modified_tickets, 'warnings': warn}
        def _implementation(db):
            tkt = Ticket(self.env, ticket_id)
            tm = TicketModule(self.env)
            req.args[field] = new_value
            tm._populate(req, tkt, plain_fields=True)

            changes, problems = tm.get_ticket_changes(req, tkt, "btn_save")

            if problems:
                raise ValueError(problems)

            tm._apply_ticket_changes(tkt, changes)
            valid = tm._validate_ticket(req, tkt, force_collision_check=True)
            if not valid:
                raise ValueError(req.chrome['warnings'])
            else:
                tkt.save_changes(req.authname, "", when=datetime.now(utc))
        def do_save(db):
            tm = TicketModule(self.env)
            req.args["milestone"] = milestone

            if ts:
                req.args["ts"] = ts
                
            tm._populate(req, ticket, plain_fields=True)

            changes, problems = tm.get_ticket_changes(req, ticket, "btn_save")

            if problems:
                raise ValueError(problems)

            tm._apply_ticket_changes(ticket, changes)
            valid = tm._validate_ticket(req, ticket, force_collision_check=True)
            if not valid:
                raise ValueError(req.chrome['warnings'])
            else:
                ticket.save_changes(req.authname, "", when=datetime.now(utc))
        def _implementation(db):
            tkt = Ticket(self.env, ticket_id)
            ts = TicketSystem(self.env)
            tm = TicketModule(self.env)
            if action not in ts.get_available_actions(req, tkt):
                raise ValueError(["This ticket cannot be moved to this status,\
                      perhaps the ticket has been updated by someone else."])

            field_changes, problems = \
                tm.get_ticket_changes(req, tkt, action)

            if problems:
                raise ValueError(problems)

            tm._apply_ticket_changes(tkt, field_changes)
            valid = tm._validate_ticket(req, tkt, force_collision_check=True)
            if not valid:
                raise ValueError(req.chrome['warnings'])
            else:
                tkt.save_changes(req.authname, "", when=datetime.now(utc))
Beispiel #5
0
 def update(self,
            req,
            id,
            comment,
            attributes={},
            notify=False,
            author='',
            when=None):
     """ Update a ticket, returning the new ticket in the same form as
     get(). 'New-style' call requires two additional items in attributes:
     (1) 'action' for workflow support (including any supporting fields
     as retrieved by getActions()),
     (2) '_ts' changetime token for detecting update collisions (as received
     from get() or update() calls).
     ''Calling update without 'action' and '_ts' changetime token is
     deprecated, and will raise errors in a future version.'' """
     t = model.Ticket(self.env, id)
     # custom author?
     if author and not (req.authname == 'anonymous' \
                         or 'TICKET_ADMIN' in req.perm(t.resource)):
         # only allow custom author if anonymous is permitted or user is admin
         self.log.warn(
             "RPC ticket.update: %r not allowed to change author "
             "to %r for comment on #%d", req.authname, author, id)
         author = ''
     author = author or req.authname
     # custom change timestamp?
     if when and not 'TICKET_ADMIN' in req.perm(t.resource):
         self.log.warn(
             "RPC ticket.update: %r not allowed to update #%d with "
             "non-current timestamp (%r)", author, id, when)
         when = None
     when = when or to_datetime(None, utc)
     # and action...
     if not 'action' in attributes:
         # FIXME: Old, non-restricted update - remove soon!
         self.log.warning("Rpc ticket.update for ticket %d by user %s " \
                 "has no workflow 'action'." % (id, req.authname))
         req.perm(t.resource).require('TICKET_MODIFY')
         time_changed = attributes.pop('_ts', None)
         if time_changed and \
                 str(time_changed) != str(to_utimestamp(t.time_changed)):
             raise TracError("Ticket has been updated since last get().")
         for k, v in attributes.iteritems():
             t[k] = v
         t.save_changes(author, comment, when=when)
     else:
         ts = TicketSystem(self.env)
         tm = TicketModule(self.env)
         # TODO: Deprecate update without time_changed timestamp
         time_changed = attributes.pop('_ts', to_utimestamp(t.time_changed))
         try:
             time_changed = int(time_changed)
         except ValueError:
             raise TracError("RPC ticket.update: Wrong '_ts' token " \
                             "in attributes (%r)." % time_changed)
         action = attributes.get('action')
         avail_actions = ts.get_available_actions(req, t)
         if not action in avail_actions:
             raise TracError("Rpc: Ticket %d by %s " \
                     "invalid action '%s'" % (id, req.authname, action))
         controllers = list(tm._get_action_controllers(req, t, action))
         all_fields = [field['name'] for field in ts.get_ticket_fields()]
         for k, v in attributes.iteritems():
             if k in all_fields and k != 'status':
                 t[k] = v
         # TicketModule reads req.args - need to move things there...
         req.args.update(attributes)
         req.args['comment'] = comment
         # Collision detection: 0.11+0.12 timestamp
         req.args['ts'] = str(from_utimestamp(time_changed))
         # Collision detection: 0.13/1.0+ timestamp
         req.args['view_time'] = str(time_changed)
         changes, problems = tm.get_ticket_changes(req, t, action)
         for warning in problems:
             add_warning(req, "Rpc ticket.update: %s" % warning)
         valid = problems and False or tm._validate_ticket(req, t)
         if not valid:
             raise TracError(" ".join(
                 [warning for warning in req.chrome['warnings']]))
         else:
             tm._apply_ticket_changes(t, changes)
             self.log.debug("Rpc ticket.update save: %s" % repr(t.values))
             t.save_changes(author, comment, when=when)
             # Apply workflow side-effects
             for controller in controllers:
                 controller.apply_action_side_effects(req, t, action)
     if notify:
         try:
             tn = TicketNotifyEmail(self.env)
             tn.notify(t, newticket=False, modtime=when)
         except Exception, e:
             self.log.exception("Failure sending notification on change of "
                                "ticket #%s: %s" % (t.id, e))
Beispiel #6
0
    def _do_save(self, req, db, milestone):
        if milestone.exists:
            req.perm(milestone.resource).require('MILESTONE_MODIFY')
        else:
            req.perm(milestone.resource).require('MILESTONE_CREATE')
        
        ticket_module = TicketModule(self.env)
        ticket_module._populate(req, milestone.ticket, False)
        if not milestone.exists:
            reporter_id = get_reporter_id(req, 'author')
            milestone.ticket.values['reporter'] = reporter_id

        action = req.args.get('action', 'leave')

        field_changes, problems = ticket_module.get_ticket_changes(req, milestone.ticket,
                                    action)
        if problems:
            for problem in problems:
                add_warning(req, problem)
                add_warning(req,
                            tag(tag.p('Please review your configuration, '
                                      'probably starting with'),
                                tag.pre('[trac]\nworkflow = ...\n'),
                                tag.p('in your ', tag.tt('trac.ini'), '.'))
                            )

        ticket_module._apply_ticket_changes(milestone.ticket, field_changes)

        old_name = milestone.name
        new_name = milestone.ticket['summary']
        
        milestone.name = new_name
        milestone.description = milestone.ticket['description']

        due = req.args.get('duedate', '')
        milestone.due = due and parse_date(due, tzinfo=req.tz) or None
        milestone.ticket['duedate']=milestone.due and str(to_timestamp(milestone.due)) or None

        completed = req.args.get('completedate', '')
        retarget_to = req.args.get('target')

        # Instead of raising one single error, check all the constraints and
        # let the user fix them by going back to edit mode showing the warnings
        warnings = []
        def warn(msg):
            add_warning(req, msg)
            warnings.append(msg)

        # -- check the name
        if new_name:
            if new_name != old_name:
                # check that the milestone doesn't already exists
                # FIXME: the whole .exists business needs to be clarified
                #        (#4130) and should behave like a WikiPage does in
                #        this respect.
                try:
                    other_milestone = StructuredMilestone(self.env, new_name, db)
                    warn(_('Milestone "%(name)s" already exists, please '
                           'choose another name', name=new_name))
                except ResourceNotFound:
                    pass
        else:
            warn(_('You must provide a name for the milestone.'))

        # -- check completed date
        if action in MilestoneSystem(self.env).starting_action:
            milestone.ticket['started'] = str(to_timestamp(datetime.now(utc)))
        if action in MilestoneSystem(self.env).completing_action:
            milestone.completed = datetime.now(utc)
            
        if warnings:
            return self._render_editor(req, db, milestone)
        
        # -- actually save changes
        if milestone.exists:
            cnum = req.args.get('cnum')
            replyto = req.args.get('replyto')
            internal_cnum = cnum
            if cnum and replyto: # record parent.child relationship
                internal_cnum = '%s.%s' % (replyto, cnum)

            now = datetime.now(utc)
            milestone.save_changes(get_reporter_id(req, 'author'),
                                         req.args.get('comment'), when=now,
                                         cnum=internal_cnum)
            # eventually retarget opened tickets associated with the milestone
            if 'retarget' in req.args and completed:
                cursor = db.cursor()
                cursor.execute("UPDATE ticket SET milestone=%s WHERE "
                               "milestone=%s and status != 'closed'",
                                (retarget_to, old_name))
                self.env.log.info('Tickets associated with milestone %s '
                                  'retargeted to %s' % (old_name, retarget_to))
        else:
            milestone.insert()
        db.commit()

        add_notice(req, _('Your changes have been saved.'))
        jump_to = req.args.get('jump_to', 'roadmap')
        if jump_to=='roadmap':
            req.redirect(req.href.roadmap())
        elif jump_to =='whiteboard':
            req.redirect(req.href.whiteboard('team_tasks')+'#'+milestone.name)
        else:
            req.redirect(req.href.milestone(milestone.name))
Beispiel #7
0
 def update(self, req, id, comment, attributes={}, notify=False, author='', when=None):
     """ Update a ticket, returning the new ticket in the same form as
     get(). 'New-style' call requires two additional items in attributes:
     (1) 'action' for workflow support (including any supporting fields
     as retrieved by getActions()),
     (2) '_ts' changetime token for detecting update collisions (as received
     from get() or update() calls).
     ''Calling update without 'action' and '_ts' changetime token is
     deprecated, and will raise errors in a future version.'' """
     t = model.Ticket(self.env, id)
     # custom author?
     if author and not (req.authname == 'anonymous' \
                         or 'TICKET_ADMIN' in req.perm(t.resource)):
         # only allow custom author if anonymous is permitted or user is admin
         self.log.warn("RPC ticket.update: %r not allowed to change author "
                 "to %r for comment on #%d", req.authname, author, id)
         author = ''
     author = author or req.authname
     # custom change timestamp?
     if when and not 'TICKET_ADMIN' in req.perm(t.resource):
         self.log.warn("RPC ticket.update: %r not allowed to update #%d with "
                 "non-current timestamp (%r)", author, id, when)
         when = None
     when = when or to_datetime(None, utc)
     # and action...
     if not 'action' in attributes:
         # FIXME: Old, non-restricted update - remove soon!
         self.log.warning("Rpc ticket.update for ticket %d by user %s " \
                 "has no workflow 'action'." % (id, req.authname))
         req.perm(t.resource).require('TICKET_MODIFY')
         time_changed = attributes.pop('_ts', None)
         if time_changed and \
                 str(time_changed) != str(to_utimestamp(t.time_changed)):
             raise TracError("Ticket has been updated since last get().")
         for k, v in attributes.iteritems():
             t[k] = v
         t.save_changes(author, comment, when=when)
     else:
         ts = TicketSystem(self.env)
         tm = TicketModule(self.env)
         # TODO: Deprecate update without time_changed timestamp
         time_changed = attributes.pop('_ts', to_utimestamp(t.time_changed))
         try:
             time_changed = int(time_changed)
         except ValueError:
             raise TracError("RPC ticket.update: Wrong '_ts' token " \
                             "in attributes (%r)." % time_changed)
         action = attributes.get('action')
         avail_actions = ts.get_available_actions(req, t)
         if not action in avail_actions:
             raise TracError("Rpc: Ticket %d by %s " \
                     "invalid action '%s'" % (id, req.authname, action))
         controllers = list(tm._get_action_controllers(req, t, action))
         all_fields = [field['name'] for field in ts.get_ticket_fields()]
         for k, v in attributes.iteritems():
             if k in all_fields and k != 'status':
                 t[k] = v
         # TicketModule reads req.args - need to move things there...
         req.args.update(attributes)
         req.args['comment'] = comment
         # Collision detection: 0.11+0.12 timestamp
         req.args['ts'] = str(from_utimestamp(time_changed))
         # Collision detection: 0.13/1.0+ timestamp
         req.args['view_time'] = str(time_changed)
         changes, problems = tm.get_ticket_changes(req, t, action)
         for warning in problems:
             add_warning(req, "Rpc ticket.update: %s" % warning)
         valid = problems and False or tm._validate_ticket(req, t)
         if not valid:
             raise TracError(
                 " ".join([warning for warning in req.chrome['warnings']]))
         else:
             tm._apply_ticket_changes(t, changes)
             self.log.debug("Rpc ticket.update save: %s" % repr(t.values))
             t.save_changes(author, comment, when=when)
             # Apply workflow side-effects
             for controller in controllers:
                 controller.apply_action_side_effects(req, t, action)
     if notify:
         try:
             tn = TicketNotifyEmail(self.env)
             tn.notify(t, newticket=False, modtime=when)
         except Exception, e:
             self.log.exception("Failure sending notification on change of "
                                "ticket #%s: %s" % (t.id, e))
Beispiel #8
0
    def _do_save(self, req, db, milestone):
        if milestone.exists:
            req.perm(milestone.resource).require('MILESTONE_MODIFY')
        else:
            req.perm(milestone.resource).require('MILESTONE_CREATE')

        ticket_module = TicketModule(self.env)
        ticket_module._populate(req, milestone.ticket, False)
        if not milestone.exists:
            reporter_id = get_reporter_id(req, 'author')
            milestone.ticket.values['reporter'] = reporter_id

        action = req.args.get('action', 'leave')

        field_changes, problems = ticket_module.get_ticket_changes(
            req, milestone.ticket, action)
        if problems:
            for problem in problems:
                add_warning(req, problem)
                add_warning(
                    req,
                    tag(
                        tag.p('Please review your configuration, '
                              'probably starting with'),
                        tag.pre('[trac]\nworkflow = ...\n'),
                        tag.p('in your ', tag.tt('trac.ini'), '.')))

        ticket_module._apply_ticket_changes(milestone.ticket, field_changes)

        old_name = milestone.name
        new_name = milestone.ticket['summary']

        milestone.name = new_name
        milestone.description = milestone.ticket['description']

        due = req.args.get('duedate', '')
        milestone.due = due and parse_date(due, tzinfo=req.tz) or None
        milestone.ticket['duedate'] = milestone.due and str(
            to_timestamp(milestone.due)) or None

        completed = req.args.get('completedate', '')
        retarget_to = req.args.get('target')

        # Instead of raising one single error, check all the constraints and
        # let the user fix them by going back to edit mode showing the warnings
        warnings = []

        def warn(msg):
            add_warning(req, msg)
            warnings.append(msg)

        # -- check the name
        if new_name:
            if new_name != old_name:
                # check that the milestone doesn't already exists
                # FIXME: the whole .exists business needs to be clarified
                #        (#4130) and should behave like a WikiPage does in
                #        this respect.
                try:
                    other_milestone = StructuredMilestone(
                        self.env, new_name, db)
                    warn(
                        _(
                            'Milestone "%(name)s" already exists, please '
                            'choose another name',
                            name=new_name))
                except ResourceNotFound:
                    pass
        else:
            warn(_('You must provide a name for the milestone.'))

        # -- check completed date
        if action in MilestoneSystem(self.env).starting_action:
            milestone.ticket['started'] = str(to_timestamp(datetime.now(utc)))
        if action in MilestoneSystem(self.env).completing_action:
            milestone.completed = datetime.now(utc)

        if warnings:
            return self._render_editor(req, db, milestone)

        # -- actually save changes
        if milestone.exists:
            cnum = req.args.get('cnum')
            replyto = req.args.get('replyto')
            internal_cnum = cnum
            if cnum and replyto:  # record parent.child relationship
                internal_cnum = '%s.%s' % (replyto, cnum)

            now = datetime.now(utc)
            milestone.save_changes(get_reporter_id(req, 'author'),
                                   req.args.get('comment'),
                                   when=now,
                                   cnum=internal_cnum)
            # eventually retarget opened tickets associated with the milestone
            if 'retarget' in req.args and completed:
                cursor = db.cursor()
                cursor.execute(
                    "UPDATE ticket SET milestone=%s WHERE "
                    "milestone=%s and status != 'closed'",
                    (retarget_to, old_name))
                self.env.log.info('Tickets associated with milestone %s '
                                  'retargeted to %s' % (old_name, retarget_to))
        else:
            milestone.insert()
        db.commit()

        add_notice(req, _('Your changes have been saved.'))
        jump_to = req.args.get('jump_to', 'roadmap')
        if jump_to == 'roadmap':
            req.redirect(req.href.roadmap())
        elif jump_to == 'whiteboard':
            req.redirect(
                req.href.whiteboard('team_tasks') + '#' + milestone.name)
        else:
            req.redirect(req.href.milestone(milestone.name))
Beispiel #9
0
    def invoke(self, message, warnings):
        """reply to a ticket"""
        ticket = self.ticket
        reporter = self._reporter(message)
        # get the mailBody and attachments
        mailBody, attachments = get_body_and_attachments(message)
        if not mailBody:
            warnings.append("Seems to be a reply to %s but I couldn't find a comment")
            return message

        #go throught work

        ts = TicketSystem(self.env)
        tm = TicketModule(self.env)
        perm = PermissionSystem(self.env)
        # TODO: Deprecate update without time_changed timestamp
        mockReq = self._MockReq(perm.get_user_permissions(reporter), reporter)
        avail_actions = ts.get_available_actions(mockReq, ticket)

        mailBody, inBodyFields, actions = self._get_in_body_fields(mailBody, avail_actions, reporter)
        if inBodyFields or actions :
            # check permissions
            perm = PermissionSystem(self.env)
            #we have properties movement, cheking user permission to do so
            if not perm.check_permission('MAIL2TICKET_PROPERTIES', reporter) : # None -> 'anoymous'
                raise ("%s does not have MAIL2TICKET_PROPERTIES permissions" % (user or 'anonymous'))

        action = None
        if actions :
            action = actions.keys()[0] 
        controllers = list(tm._get_action_controllers(mockReq, ticket, action))
        all_fields = [field['name'] for field in ts.get_ticket_fields()]


        #impact changes find in inBodyFields
        for field in inBodyFields :
            ticket._old[field] = ticket[field]
            ticket.values[field] = inBodyFields[field]
            mockReq.args[field] = inBodyFields[field]
        if action : 
            mockReq.args['action_%s_reassign_owner' % action] = ticket['owner']



        mockReq.args['comment'] = mailBody
        mockReq.args['ts'] = datetime.now()#to_datetime(None, utc)


        mockReq.args['ts'] = str(ticket.time_changed)
        
        changes, problems = tm.get_ticket_changes(mockReq, ticket, action)
        valid = problems and False or tm._validate_ticket(mockReq, ticket)

        tm._apply_ticket_changes(ticket, changes)


        # add attachments to the ticket
        add_attachments(self.env, ticket, attachments)

        ticket.save_changes(reporter, mailBody)
        
        for controller in controllers:
            controller.apply_action_side_effects(mockReq, ticket, action)
            # Call ticket change listeners
        for listener in ts.change_listeners:
            listener.ticket_changed(ticket, mailBody, reporter, ticket._old)

        tn = TicketNotifyEmail(self.env)
        tn.notify(ticket, newticket=0, modtime=ticket.time_changed)