Exemplo n.º 1
0
 def getActions(self, req, id):
     """Returns the actions that can be performed on the ticket as a list of
     `[action, label, hints, [input_fields]]` elements, where `input_fields` is
     a list of `[name, value, [options]]` for any required action inputs."""
     ts = TicketSystem(self.env)
     t = model.Ticket(self.env, id)
     actions = []
     for action in ts.get_available_actions(req, t):
         fragment = genshi.builder.Fragment()
         hints = []
         first_label = None
         for controller in ts.action_controllers:
             if action in [c_action for c_weight, c_action \
                             in controller.get_ticket_actions(req, t)]:
                 label, widget, hint = \
                     controller.render_ticket_action_control(req, t, action)
                 fragment += widget
                 hints.append(to_unicode(hint).rstrip('.') + '.')
                 first_label = first_label == None and label or first_label
         controls = []
         for elem in fragment.children:
             if not isinstance(elem, genshi.builder.Element):
                 continue
             if elem.tag == 'input':
                 controls.append((elem.attrib.get('name'),
                                  elem.attrib.get('value'), []))
             elif elem.tag == 'select':
                 value = ''
                 options = []
                 for opt in elem.children:
                     if not (opt.tag == 'option' and opt.children):
                         continue
                     option = opt.children[0]
                     options.append(option)
                     if opt.attrib.get('selected'):
                         value = option
                 controls.append((elem.attrib.get('name'), value, options))
         actions.append((action, first_label, " ".join(hints), controls))
     return actions
Exemplo n.º 2
0
    def save_ticket(self, ticket_data, author):
        """If ticket_data contains an ID, modifies defined fields in that ticket.
           If not, creates new ticket. Returns the ID of new/modified ticket."""

        id = None
        comment = ''

        if 'id' in ticket_data:
            id = ticket_data['id']
        ticket = model.Ticket(self.env, id)

        for key, value in ticket_data.items():
            if key == 'comment':
                comment = value
            elif key != 'id':
                ticket[key] = value
        if id:
            ticket.save_changes(author, comment)
        else:
            ticket.insert()
            id = ticket.id

        return id
Exemplo n.º 3
0
 def putAttachment(self,
                   req,
                   ticket,
                   filename,
                   description,
                   data,
                   replace=True):
     """ Add an attachment, optionally (and defaulting to) overwriting an
     existing one. Returns filename."""
     if not model.Ticket(self.env, ticket).exists:
         raise ResourceNotFound('Ticket "%s" does not exist' % ticket)
     if replace:
         try:
             attachment = Attachment(self.env, 'ticket', ticket, filename)
             req.perm(attachment.resource).require('ATTACHMENT_DELETE')
             attachment.delete()
         except TracError:
             pass
     attachment = Attachment(self.env, 'ticket', ticket)
     req.perm(attachment.resource).require('ATTACHMENT_CREATE')
     attachment.author = req.authname
     attachment.description = description
     attachment.insert(filename, StringIO(data.data), len(data.data))
     return attachment.filename
Exemplo n.º 4
0
 def replace_tags(self, req, name, tags):
     ticket = model.Ticket(self.env, name)
     ticket['keywords'] = ' '.join(sorted(tags))
     ticket.save_changes(req.authname, None)
Exemplo n.º 5
0
 def add_tags(self, req, name, tags):
     ticket = model.Ticket(self.env, name)
     ticket_tags = self._ticket_tags(ticket)
     ticket_tags.update(tags)
     ticket['keywords'] = ' '.join(sorted(ticket_tags))
     ticket.save_changes(req.authname, None)
Exemplo n.º 6
0
 def do_remove(db):
     ticket = model.Ticket(self.env, number, db=db)
     ticket.delete()
Exemplo n.º 7
0
 def changeLog(self, req, id, when=0):
     t = model.Ticket(self.env, id)
     return t.get_changelog(when)
Exemplo n.º 8
0
 def assertCommentAdded(self, ticket_id, comment):
     ticket = model.Ticket(self.env, int(ticket_id))
     changes = ticket.get_changelog()
     comment_change = [c for c in changes if c[2] == 'comment'][-1]
     self.assertEqual(comment_change[4], comment)
Exemplo n.º 9
0
 def get(self, req, id):
     """ Fetch a ticket. Returns [id, time_created, time_changed, attributes]. """
     t = model.Ticket(self.env, id)
     req.perm(t.resource).require('TICKET_VIEW')
     t['_ts'] = str(to_utimestamp(t.time_changed))
     return (t.id, t.time_created, t.time_changed, t.values)
Exemplo n.º 10
0
    def validate_ticket(self, req, ticket):
        """Validate a ticket after it's been populated from user input.
       
        Must return a list of `(field, message)` tuples, one for each problem
        detected. `field` can be `None` to indicate an overall problem with the
        ticket. Therefore, a return value of `[]` means everything is OK."""

        res = []

        # the ticket we receive is a temporary not-yet-commited ticket
        # and contains fields set that weren't changed as well,
        # retrieve the original one so we can compare
        ot = model.Ticket(self.env, ticket.id)

        self.env.log.debug('validate_ticket: %s' % ticket.id)

        # refuse changes to dup_count and dups fields
        new = ticket.values.get('dups', None)
        if new is not None and new != ot.values.get('dups', None):
            res.append(('dups', 'Cannot manually change the dups field.'))
            return res

        new = ticket.values.get('dup_count', None)
        if new is not None and new != ot.values.get('dup_count', None):
            res.append(
                ('dup_count', 'Cannot manually change the dup_count field.'))
            return res

        new_id = ticket.values.get('dup_of', None)

        # allow unsetting
        if not new_id:
            self.env.log.debug("validate_ticket: dup_of is None, so fine")
            return res

        # care only about tickets that have dup_of changes
        old = ot.values.get('dup_of', None)
        if old == new_id:
            self.env.log.debug("validate_ticket: no dup_of changes")
            return res

        # refuse to change closed tickets
        if ticket.values['status'] == u'closed':
            if ticket.values['resolution'] == u'duplicate':
                self.env.log.debug(
                    "validate_ticket: allowing unduplicate to get validated")
                # but still subject to other rules
            else:
                self.env.log.debug(
                    "validate_ticket: refusing to dup closed ticket #%s" %
                    ticket.id)
                res.append(
                    ('dup_of',
                     'Ticket is already closed, and not as a duplicate.'))
                return res

        # refuse to dup_of and reopen ticket in one go
        if ot.values['status'] == u'closed' \
            and ticket.values['status'] == u'reopened':
            self.env.log.debug("validate_ticket: "
                               "refusing to dup_of and reopen ticket #%s" %
                               ticket.id)
            res.append(('status',
                        'If you want to duplicate an already closed ticket, '
                        'only change dup_of without reopening the ticket.'))
            return res

        # warn when it starts with #
        if len(new_id) > 0 and new_id[0] == '#':
            res.append(('dup_of',
                        'Please enter the ticket number without a leading #.'))
            return res

        # refuse to dup to non-existing tickets; this raises a TracError
        # if it doesn't exist
        # coderanger says a Ticket can have anything with a __str__ method
        # as id
        # except in the 0.10.5dev branch, a non-existing ticket id raises
        # a TracError with %d in the format string (fixed on TRUNK),
        # so make it an int here
        master = model.Ticket(self.env, int(new_id))

        # refuse to dup to self
        if str(new_id) == str(ticket.id):
            self.env.log.debug("validate_ticket: "
                               "cowardly refusing to dup to self #%s" %
                               ticket.id)
            res.append(('dup_of', 'Cannot duplicate a ticket to itself.'))
            return res

        self.env.log.debug('validate_ticket: Validated ticket %s' % ticket.id)
        return res
Exemplo n.º 11
0
 def name_details(self, name):
     ticket = model.Ticket(self.env, name)
     href = self.env.href.ticket(name)
     summary = ticket['summary'] or u''
     return (href, '<a href="%s">#%s</a>' % (href, name), ticket.exists
             and summary)
Exemplo n.º 12
0
 def name_details(self, name):
     ticket = model.Ticket(self.env, name)
     href = self.env.href.ticket(name)
     from trac.wiki.formatter import wiki_to_oneliner
     return (href, wiki_to_oneliner('#%s' % name, self.env),
             ticket.exists and ticket['summary'] or '')
Exemplo n.º 13
0
 def get(self, req, id):
     """ Fetch a ticket. Returns [id, time_created, time_changed, attributes]. """
     t = model.Ticket(self.env, id)
     return (t.id, t.time_created, t.time_changed, t.values)
Exemplo n.º 14
0
 def getAvailableActions(self, req, id):
     """Returns the actions that can be performed on the ticket."""
     ticketSystem = TicketSystem(self.env)
     t = model.Ticket(self.env, id)
     return ticketSystem.get_available_actions(t, req.perm)
Exemplo n.º 15
0
 def remove_tags(self, req, name, tags):
     ticket = model.Ticket(self.env, name)
     ticket_tags = self._ticket_tags(ticket)
     ticket_tags.symmetric_difference_update(tags)
     ticket['keywords'] = ' '.join(sorted(ticket_tags))
     ticket.save_changes(req.authname, None)
Exemplo n.º 16
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))
Exemplo n.º 17
0
 def remove_all_tags(self, req, name):
     ticket = model.Ticket(self.env, name)
     ticket['keywords'] = ''
     ticket.save_changes(req.authname, None)
Exemplo n.º 18
0
 def delete(self, req, id):
     """ Delete ticket with the given id. """
     t = model.Ticket(self.env, id)
     req.perm(t.resource).require('TICKET_ADMIN')
     t.delete()
Exemplo n.º 19
0
                  dest='priority',
                  type='string',
                  help='ticket priority',
                  default='normal')
options, args = parser.parse_args()

if None in (options.summary, options.description, options.reporter,
            options.owner, options.component, options.project):
    sys.stderr.write("Please make sure that summary, description, reporter,"
                     " owner, componnet and project are defined")
    sys.stderr.flush()
    sys.exit(1)

env = open_environment(options.project)

t = model.Ticket(env)
t['status'] = options.status
t['summary'] = options.summary
t['description'] = options.description.replace("\\n", "\n")
t['reporter'] = options.reporter
t['owner'] = options.owner
t['type'] = options.type
t['component'] = options.component
t['component'] = options.component
if options.cc:
    t['cc'] = options.cc
t['priority'] = options.priority
t.insert()

try:
    tn = TicketNotifyEmail(env)
Exemplo n.º 20
0
 def changeLog(self, req, id, when=0):
     t = model.Ticket(self.env, id)
     req.perm(t.resource).require('TICKET_VIEW')
     for date, author, field, old, new, permanent in t.get_changelog(when):
         yield (date, author, field, old, new, permanent)
Exemplo n.º 21
0
 def assertFieldValue(self, ticket_id, field, new_value):
     ticket = model.Ticket(self.env, int(ticket_id))
     self.assertEqual(ticket[field], new_value)
Exemplo n.º 22
0
 def delete(self, req, id):
     """ Delete ticket with the given id. """
     t = model.Ticket(self.env, id)
     t.delete()