Beispiel #1
0
 def find_blockers(self, ticket, field, blockers):
     remote_tktsys = RemoteTicketSystem(self.env)
     links = remote_tktsys.parse_links(ticket[field])
     for remote_name, link in links:
         linked_ticket = RemoteTicket(self.env, remote_name, link)
         if linked_ticket['status'] != 'closed':
             blockers.append((remote_name, link))
         else:
             self.find_blockers(linked_ticket, field, blockers)
     return blockers
Beispiel #2
0
 def find_blockers(self, ticket, field, blockers):
     remote_tktsys = RemoteTicketSystem(self.env)
     links = remote_tktsys.parse_links(ticket[field])
     for remote_name, link in links:
         linked_ticket = RemoteTicket(self.env, remote_name, link)
         if linked_ticket['status'] != 'closed':
             blockers.append((remote_name, link))
         else:
             self.find_blockers(linked_ticket, field, blockers)
     return blockers
Beispiel #3
0
 def validate_links_exist(self, ticket, end):
     remote_tktsys = RemoteTicketSystem(self.env)
     links = remote_tktsys.parse_links(ticket[end])
     bad_links = []
     for remote_name, link in links:
         try:
             tkt = RemoteTicket(self.env, remote_name, link)
         except ResourceNotFound:
             bad_links.append((remote_name, link))
     if bad_links:
         return ("Remote tickets linked in '%s' could not be found: [%s]"
                 % (end, ', '.join('%s:#%s' % t for t in bad_links)))
Beispiel #4
0
 def validate_links_exist(self, ticket, end):
     remote_tktsys = RemoteTicketSystem(self.env)
     links = remote_tktsys.parse_links(ticket[end])
     bad_links = []
     for remote_name, link in links:
         try:
             tkt = RemoteTicket(self.env, remote_name, link)
         except ResourceNotFound:
             bad_links.append((remote_name, link))
     if bad_links:
         return ("Remote tickets linked in '%s' could not be found: [%s]" %
                 (end, ', '.join('%s:#%s' % t for t in bad_links)))
Beispiel #5
0
 def _refresh_ticket(self):
     rts = RemoteTicketSystem(self.env)
     remote_trac = rts.get_remote_trac(self.remote_name)['url']
     xmlrpc_addr = Href(remote_trac).rpc()
     server = xmlrpclib.ServerProxy(xmlrpc_addr)
     
     try:
         tkt_vals = server.ticket.get(self.id)
     except xmlrpclib.ProtocolError, e:
         msg = ("Could not contact remote Trac '%s' at %s. "
                "Received error %s, %s")
         log = ("XML-RPC ProtocolError contacting Trac %s at %s, "
                "errcode=%s, errmsg='%s'")
         args = (self.remote_name, xmlrpc_addr, e.errcode, e.errmsg)
         self.env.log.warn(log, *args)
         raise ResourceNotFound(msg % args, "Uncontactable server")
Beispiel #6
0
    def _refresh_ticket(self):
        rts = RemoteTicketSystem(self.env)
        remote_trac = rts.get_remote_trac(self.remote_name)['url']
        xmlrpc_addr = Href(remote_trac).rpc()
        server = xmlrpclib.ServerProxy(xmlrpc_addr)

        try:
            tkt_vals = server.ticket.get(self.id)
        except xmlrpclib.ProtocolError, e:
            msg = ("Could not contact remote Trac '%s' at %s. "
                   "Received error %s, %s")
            log = ("XML-RPC ProtocolError contacting Trac %s at %s, "
                   "errcode=%s, errmsg='%s'")
            args = (self.remote_name, xmlrpc_addr, e.errcode, e.errmsg)
            self.env.log.warn(log, *args)
            raise ResourceNotFound(msg % args, "Uncontactable server")
Beispiel #7
0
    def _fetch_ticket(self):
        rts = RemoteTicketSystem(self.env)
        db = self.env.get_read_db()
        cursor = db.cursor()

        # Try to retrieve remote ticket from cache
        cursor.execute(
            '''SELECT %s FROM remote_tickets 
                       WHERE remote_name=%%s and id=%%s
                       ''' % (', '.join(self.table_fields)),
            (self.remote_name, self.id))
        row = cursor.fetchone()

        # Remote ticket not in cache
        if not row:
            self._refresh_ticket()

        self._cachetime = from_utimestamp(row[self.cachetime_pos])
        ttl = timedelta(seconds=int(rts.cache_ttl) // 1000,
                        microseconds=int(rts.cache_ttl) % 1000 * 1000)

        # Cached remote ticket is too old
        if self._cachetime < datetime.now(utc) - ttl:
            self._refresh_ticket()

        # Cached ticket is valid, populate instance
        for name, value in zip(self.remote_fields, row):
            if name in self.time_fields:
                self.values[name] = from_utimestamp(value)
            elif value is None:
                self.values[name] = empty
            else:
                self.values[name] = value
Beispiel #8
0
 def _do_ticket(self, req, template, data, content_type):
     if 'ticket' in data and 'linked_tickets' in data:
         ticket = data['ticket']
         context = Context.from_request(req, ticket.resource)
         
         # Add name:#n links to link fields of Ticket instance when
         # flowing from storage to browser
         if req.method == 'GET':
             RemoteLinksProvider(self.env).augment_ticket(ticket)
         
         # Rerender link fields
         for field in data['fields']:
             if field['type'] == 'link':
                 name = field['name']
                 field['rendered'] = format_to_oneliner(self.env, context,
                                                        ticket[name])
         
         # Add RemoteTicket objects for linked issues table, and pass list
         # of rejects that could not be retrieved
         linked_tickets, linked_rejects = self._remote_tickets(ticket,
                                                               context)
         data['linked_tickets'].extend(linked_tickets)
         data['linked_rejects'].extend(linked_rejects)
     
     # Provide list of remote sites if newlinked form options are present
     if 'newlinked_options' in data:
         remote_sites = RemoteTicketSystem(self.env).get_remote_tracs()
         data['remote_sites'] = remote_sites
     
     return (template, data, content_type)
Beispiel #9
0
 def find_cycle(self, ticket, field, path):
     tkt_ref = '%s:#%s' % (getattr(ticket, 'remote_name', ''), ticket.id)
     if tkt_ref in path:
         path.append(tkt_ref)
         return path
     
     path.append(tkt_ref)
     
     remote_tktsys = RemoteTicketSystem(self.env)
     links = remote_tktsys.parse_links(ticket[field])
     for remote_name, link in links:
         linked_ticket = RemoteTicket(self.env, remote_name, link)
         cycle = self.find_cycle(linked_ticket, field, copy(path))
         if cycle != None:
             return cycle
     return None
Beispiel #10
0
    def find_cycle(self, ticket, field, path):
        tkt_ref = '%s:#%s' % (getattr(ticket, 'remote_name', ''), ticket.id)
        if tkt_ref in path:
            path.append(tkt_ref)
            return path

        path.append(tkt_ref)

        remote_tktsys = RemoteTicketSystem(self.env)
        links = remote_tktsys.parse_links(ticket[field])
        for remote_name, link in links:
            linked_ticket = RemoteTicket(self.env, remote_name, link)
            cycle = self.find_cycle(linked_ticket, field, copy(path))
            if cycle != None:
                return cycle
        return None
Beispiel #11
0
    def validate_ticket(self, req, ticket):
        action = req.args.get('action')
        ticket_system = TicketSystem(self.env)
        links_provider = LinksProvider(self.env)
        remote_tktsys = RemoteTicketSystem(self.env)

        for end in ticket_system.link_ends_map:
            check = self.validate_links_exist(ticket, end)
            if check:
                yield None, check
                continue

            validator_name = links_provider.get_validator(end)
            if validator_name == 'no_cycle':
                validator = self.validate_no_cycle
            elif (validator_name == 'parent_child'
                  and end == links_provider.PARENT_END):
                validator = self.validate_parent
            else:
                validator = self.validate_any

            check = validator(ticket, end)
            if check:
                yield None, check

            if action == 'resolve':
                blockers = self.find_blockers(ticket, end, [])
                if blockers:
                    blockers_str = ', '.join('%s:#%s' % rlink
                                             for rlink in unique(blockers))
                    msg = ("Cannot resolve this ticket because it is "
                           "blocked by '%s' tickets [%s]" %
                           (end, blockers_str))
                    yield None, msg
Beispiel #12
0
 def _remote_tickets(self, ticket, context):
     link_fields = [f for f in ticket.fields if f['type'] == 'link']
     rts = RemoteTicketSystem(self.env)
     
     linked_tickets = []
     linked_rejects = []
     for field in link_fields:
         for link_name, link in rts.parse_links(ticket[field['name']]):
             tkt_fmt = format_to_oneliner(self.env, context,
                                          '%s:#%s' % (link_name, link))
             try:
                 tkt = RemoteTicket(self.env, link_name, link)
                 linked_tickets.append((field['label'], tkt_fmt, tkt))
             except ResourceNotFound:
                 linked_rejects.append((field['label'], tkt_fmt))
     
     return linked_tickets, linked_rejects
Beispiel #13
0
    def validate_parent(self, ticket, end):
        cycle_validation = self.validate_no_cycle(ticket, end)
        if cycle_validation:
            return cycle_validation

        links = RemoteTicketSystem(self.env).parse_links(ticket[end])
        if len(links) > 1 and end == LinksProvider.PARENT_END:
            parents_str = ', '.join('%s:#%s' % (remote_name, tkt_id)
                                    for (remote_name, tkt_id) in links)
            return ("Multiple links in '%s': %s:#%s -> [%s]" %
                    (LinksProvider(self.env).render_end(end),
                     ticket.remote_name, ticket.id, parents_str))
Beispiel #14
0
 def _do_newticket(self, req, template, data, content_type):
     link_remote_val = req.args.get('linked_remote_val', '')
     pattern = RemoteTicketSystem(self.env).REMOTES_RE
     lrv_match = pattern.match(link_remote_val)
     link_end = req.args.get('linked_end', '')
     ends_map = TicketSystem(self.env).link_ends_map
     
     if ('ticket' in data and lrv_match and link_end in ends_map):
         ticket = data['ticket']
         remote_name = lrv_match.group(1)
         remote_id = lrv_match.group(2)
         remote_ticket = RemoteTicket(self.env, remote_name, remote_id,
                                      refresh=True)
         link_fields = [f for f in ticket.fields if f['name'] == link_end]
         copy_field_names = link_fields[0]['copy_fields']
         
         ticket[link_end] = link_remote_val
         for fname in copy_field_names:
             ticket[fname] = remote_ticket[fname]
         
         data['remote_ticket'] = remote_ticket
         
     return (template, data, content_type)
Beispiel #15
0
 def pre_process_request(self, req, handler):
     # If linked_val request argument matches the URL of a known
     # remote site then:
     #  - Parse it, storing the result in linked_remote_val
     #  - Remove the linked_val argument, so trac doesn't also process it
     if 'linked_val' in req.args:
         linked_val = req.args['linked_val']
         patt = re.compile(r'(.+)/ticket/(\d+)')
         for name, site in RemoteTicketSystem(self.env)._intertracs.items():
             m = patt.match(linked_val)
             if m:
                 remote_base, remote_tkt_id = m.groups()
                 if remote_base == site['url'].rstrip('/'):
                     req.args['linked_remote_val'] = '%s:#%s' \
                                                     % (name, remote_tkt_id)
                     del req.args['linked_val']
                     break
     return handler
Beispiel #16
0
    def ticket_changed(self, ticket, comment, author, old_values):
        link_fields = [f['name'] for f in ticket.fields if f.get('link')]
        ticket_system = TicketSystem(self.env)
        links_provider = LinksProvider(self.env)
        remote_tktsys = RemoteTicketSystem(self.env)

        # We go behind trac's back to augment the ticket with remote links
        # As a result trac doesn't provide a correct old_values so fetch
        # our own
        orig_old_vals = old_values
        if old_values is None:
            old_values = {}
        else:
            self._augment_values(ticket.id, old_values)

        @self.env.with_transaction()
        def do_changed(db):
            cursor = db.cursor()
            for end in link_fields:
                # Determine links added or removed in this change by taking the
                # set difference of new and old values
                new_rtkts = set(remote_tktsys.parse_links(ticket[end]))
                old_rtkts = set(remote_tktsys.parse_links(old_values.get(end)))

                links_added = new_rtkts - old_rtkts
                links_removed = old_rtkts - new_rtkts
                links_changed = old_rtkts ^ new_rtkts  # Additons and removals

                other_end = ticket_system.link_ends_map[end]

                # Add link records for remote links created in this change
                records = [('', ticket.id, end, rname, rid)
                           for rname, rid in links_added]
                if other_end:
                    records += [(rname, rid, other_end, '', ticket.id)
                                for rname, rid in links_added]
                cursor.executemany(
                    '''
                    INSERT INTO remote_ticket_links
                    (source_name, source, type, destination_name, destination)
                    VALUES (%s, %s, %s, %s, %s)''', records)

                # Remove link records for remote links removed in this change
                records = [('', ticket.id, end, rname, rid)
                           for rname, rid in links_removed]
                if other_end:
                    records += [(rname, rid, other_end, '', ticket.id)
                                for rname, rid in links_added]
                cursor.executemany(
                    '''
                    DELETE FROM remote_ticket_links 
                    WHERE source_name=%s AND source=%s AND type=%s
                    AND destination_name=%s AND destination=%s''', records)

                # Record change history in ticket_change
                # Again we're going behind trac's back, so take care not to
                # obliterate existing records:
                #  - If the field (end) has changed local links, as well as
                #    changed remote links then update the record
                #  - If the only change was to remote links then there is no
                #    ticket_change record to update, so insert one
                if links_changed and orig_old_vals is not None:
                    when_ts = to_utimestamp(ticket['changetime'])

                    cursor.execute(
                        '''
                        UPDATE ticket_change
                        SET oldvalue=%s, newvalue=%s
                        WHERE ticket=%s AND time=%s AND author=%s AND field=%s
                        ''', (old_values[end], ticket[end], ticket.id, when_ts,
                              author, end))

                    # Check that a row was updated, if so
                    if cursor.rowcount >= 1:
                        continue

                    cursor.execute(
                        '''
                        INSERT INTO ticket_change
                        (ticket, time, author, field, oldvalue, newvalue)
                        VALUES (%s, %s, %s, %s, %s, %s)
                        ''', (ticket.id, when_ts, author, end, old_values[end],
                              ticket[end]))