def save_ticket(self, tckt, msg): # determine sequence number... cnum = 0 tm = TicketModule(self.env) for change in tm.grouped_changelog_entries(tckt, None): if change['permanent']: cnum += 1 nowdt = self.now nowdt = to_datetime(nowdt) tckt.save_changes(self.authname, msg, nowdt, cnum=str(cnum + 1)) ## Often the time overlaps and causes a db error, ## especially when the trac integration post-commit hook is used. ## NOTE TO SELF. I DON'T THINK THIS IS NECESSARY RIGHT NOW... #count = 0 #while count < 10: # try: # tckt.save_changes(self.authname, msg, self.now, cnum=cnum+1) # count = 42 # except Exception, e: # self.now += 1 # count += 1 tn = TicketNotifyEmail(self.env) tn.notify(tckt, newticket=0, modtime=nowdt) # We fudge time as it has to be unique self.now += 1
def attachment_added(self, attachment): # Check whether we're dealing with a ticket resource resource = attachment.resource while resource: if resource.realm == 'ticket': break resource = resource.parent if (resource and resource.realm == 'ticket' and resource.id is not None): with self.env.db_transaction as db: ticket = Ticket(attachment.env, resource.id, db) if (attachment.author == ticket['reporter'] and ticket['status'] == 'pending'): self.env.log.info('Removing Pending status for ticket %s due to attachment' % (ticket.id)) comment = 'Attachment (%s) added by ticket reporter.' % (attachment.filename) ticket['status'] = self.config.get('ticket', 'pending_removal_status') # determine sequence number... cnum = 0 tm = TicketModule(self.env) for change in tm.grouped_changelog_entries(ticket, db): c_cnum = change.get('cnum', None) if c_cnum and int(c_cnum) > cnum: cnum = int(c_cnum) #We can't just use attachment.date as it screws up event sequencing now = datetime.now(utc) ticket.save_changes(attachment.author, comment, now, db, str(cnum + 1)) #trigger notification since we've changed the ticket tn = TicketNotifyEmail(self.env) tn.notify(ticket, newticket=False, modtime=now)
def _create_html_body(self, chrome, req, ticket, cnum, link): tktmod = TicketModule(self.env) attmod = AttachmentModule(self.env) data = tktmod._prepare_data(req, ticket) tktmod._insert_ticket_data(req, ticket, data, req.authname, {}) data['ticket']['link'] = link changes = data.get('changes') if cnum is None: changes = [] else: changes = [ change for change in (changes or []) if change.get('cnum') == cnum ] data['changes'] = changes context = Context.from_request(req, ticket.resource, absurls=True) data.update({ 'can_append': False, 'show_editor': False, 'start_time': ticket['changetime'], 'context': context, 'alist': attmod.attachment_data(context), 'styles': self._get_styles(chrome), 'link': tag.a(link, href=link), 'tag_': tag_, }) rendered = chrome.render_template(req, 'htmlnotification_ticket.html', data, fragment=True) return unicode(rendered)
def save_ticket(self, tckt, db, msg): # determine sequence number... cnum = 0 tm = TicketModule(self.env) for change in tm.grouped_changelog_entries(tckt, db): if change['permanent']: cnum += 1 tckt.save_changes(self.authname, msg, self.now, db, cnum+1) ## Often the time overlaps and causes a db error, ## especially when the trac integration post-commit hook is used. ## NOTE TO SELF. I DON'T THINK THIS IS NECESSARY RIGHT NOW... #count = 0 #while count < 10: # try: # tckt.save_changes(self.authname, msg, self.now, db, cnum+1) # count = 42 # except Exception, e: # self.now += 1 # count += 1 db.commit() tn = TicketNotifyEmail(self.env) tn.notify(tckt, newticket=0, modtime=self.now) # We fudge time as it has to be unique self.now += 1
def setUp(self): self.env = EnvironmentStub() self.env.config.set( 'trac', 'permission_policies', 'DefaultTicketPolicy, DefaultPermissionPolicy, ' 'LegacyAttachmentPolicy') self.ticket_module = TicketModule(self.env)
def post_to_ticket(msg, author, tkt_id, env): """Post the message to the ticket and send a notify email.""" from trac.ticket.notification import TicketNotifyEmail from trac.ticket import Ticket from trac.ticket.web_ui import TicketModule from trac.util.datefmt import utc now = datetime.now(utc) try: db = env.get_db_cnx() # Get the related trac ticket object ticket = Ticket(env, tkt_id, db) # determine sequence number... cnum = 0 tm = TicketModule(env) for change in tm.grouped_changelog_entries(ticket, db): if change['permanent']: cnum += 1 ticket.save_changes(author, msg, now, db, cnum + 1) db.commit() tn = TicketNotifyEmail(env) tn.notify(ticket, newticket=0, modtime=now) except Exception, e: msg = 'Unexpected error processing ticket ID %s: %s' % (tkt_id, e) print >>sys.stderr, msg
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 process_request(self, req): """Creating a new pull request ticket needs some pre- and post- processing: * Check if the given repository is a fork of another known repository. * Initialize some ticket fields. Then forward the processing to `TicketModule` * Add the repository as is must be looked up via the used path and is not yet known to the ticket. """ req.perm.require('TICKET_CREATE') rm = RepositoryManager(self.env) reponame, repo, path = rm.get_repository_by_path(req.args.get('path')) convert_managed_repository(self.env, repo) if not repo.is_fork: raise TracError(_("Repository is not a fork.")) req.args['type'] = 'pull request' req.args['pr_srcrev'] = req.args.get('pr_srcrev', repo.get_youngest_rev()) tm = TicketModule(self.env) template, data, content_type = tm.process_request(req) data.update({'pr_srcrepo': repo}) return template, data, content_type
def process(self, commit, status, branch): self.closestatus = status milestones = [ m.name for m in Milestone.select(self.env) if m.name != 'unknown' ] if branch.startswith('fixes/'): branch = branch[6:] milestones = [m for m in milestones if m.startswith(branch)] self.milestone = sorted(milestones)[-1] msg = commit['message'] self.env.log.debug("Processing Commit: %s", msg) msg = "%s \n Branch: %s \n Changeset: %s" % (msg, branch, commit['id']) # author = commit['author']['name'] author = 'Github' timestamp = datetime.now(utc) cmd_groups = command_re.findall(msg) self.env.log.debug("Function Handlers: %s" % cmd_groups) tickets = {} for cmd, tkts in cmd_groups: funcname = self.__class__._supported_cmds.get(cmd.lower(), '') self.env.log.debug("Function Handler: %s" % funcname) if funcname: for tkt_id in ticket_re.findall(tkts): if (branch == "master") or branch.startswith("fixes/"): tickets.setdefault(tkt_id, []).append(getattr(self, funcname)) # disable this stuff for now, it causes duplicates on merges # proper implementation of this will require tracking commit hashes # else: # tickets.setdefault(tkt_id, []).append(self._cmdRefs) for tkt_id, cmds in tickets.iteritems(): try: db = self.env.get_db_cnx() ticket = Ticket(self.env, int(tkt_id), db) for cmd in cmds: cmd(ticket) # determine sequence number... cnum = 0 tm = TicketModule(self.env) for change in tm.grouped_changelog_entries(ticket, db): if change['permanent']: cnum += 1 ticket.save_changes(author, msg, timestamp, db, cnum + 1) db.commit() tn = TicketNotifyEmail(self.env) tn.notify(ticket, newticket=0, modtime=timestamp) except Exception, e: import traceback traceback.print_exc(file=sys.stderr)
def __init__(self, project=options.project, author=AUTHOR, maxage=options.maxage, url=options.url): self.env = open_environment(project) db = self.env.get_db_cnx() cursor = db.cursor() if url is None: url = self.env.config.get('trac', 'base_url') self.env.href = Href(url) self.env.abs_href = Href(url) self.msg = MESSAGE % (maxage) self.now = int(time.time()) maxtime = int(time.time()) - (60 * 60 * 24 * maxage) cursor.execute("SELECT id FROM ticket t, ticket_custom c " \ "WHERE t.status <> %s " \ "AND t.changetime < %s " \ "AND t.id = c.ticket " \ "AND c.name = %s " \ "AND c.value = %s ", ('closed', maxtime, 'pending', '1')) rows = cursor.fetchall() for row in rows: id = row[0] try: ticket = Ticket(self.env, id, db) ticket['status'] = 'closed' ticket['pending'] = '0' # determine sequence number... cnum = 0 tm = TicketModule(self.env) for change in tm.grouped_changelog_entries(ticket, db): if change['permanent']: cnum += 1 ticket.save_changes(author, self.msg, self.now, db, cnum + 1) db.commit() print 'Closing Ticket %s (%s)\n' % (id, ticket['summary']) tn = TicketNotifyEmail(self.env) tn.notify(ticket, newticket=0, modtime=self.now) except Exception, e: import traceback traceback.print_exc(file=sys.stderr) print>>sys.stderr, 'Unexpected error while processing ticket ' \ 'ID %s: %s' % (id, e)
def __init__(self, project=options.project, author=options.user, rev=options.rev, url=options.url): self.env = open_environment(project) repos = self.env.get_repository() repos.sync() # Instead of bothering with the encoding, we'll use unicode data # as provided by the Trac versioncontrol API (#1310). try: chgset = repos.get_changeset(rev) except NoSuchChangeset: return # out of scope changesets are not cached self.author = chgset.author self.rev = rev self.msg = "(In [%s]) %s" % (rev, chgset.message) self.now = datetime.now(utc) cmd_groups = command_re.findall(self.msg) tickets = {} for cmd, tkts in cmd_groups: funcname = CommitHook._supported_cmds.get(cmd.lower(), '') if funcname: for tkt_id in ticket_re.findall(tkts): func = getattr(self, funcname) tickets.setdefault(tkt_id, []).append(func) for tkt_id, cmds in tickets.iteritems(): try: db = self.env.get_db_cnx() ticket = Ticket(self.env, int(tkt_id), db) for cmd in cmds: cmd(ticket) # determine sequence number... cnum = 0 tm = TicketModule(self.env) for change in tm.grouped_changelog_entries(ticket, db): if change['permanent']: cnum += 1 ticket.save_changes(self.author, self.msg, self.now, db, cnum + 1) db.commit() tn = TicketNotifyEmail(self.env) tn.notify(ticket, newticket=0, modtime=self.now) except Exception, e: # import traceback # traceback.print_exc(file=sys.stderr) print>>sys.stderr, 'Unexpected error while processing ticket ' \ 'ID %s: %s' % (tkt_id, e)
def process_request(self, req): id = req.args.getint('id') req.perm('ticket', id).require('TICKET_ADMIN') ticket = Ticket(self.env, id) action = req.args['action'] cnum = req.args.get('cnum') if req.method == 'POST': if 'cancel' in req.args: href = req.href.ticket(id) if action == 'delete-comment': href += '#comment:%s' % cnum req.redirect(href) if action == 'delete': ticket.delete() add_notice( req, _("Ticket #%(num)s and all associated data " "removed.", num=ticket.id)) req.redirect(req.href()) elif action == 'delete-comment': cdate = from_utimestamp(long(req.args.get('cdate'))) ticket.delete_change(cdate=cdate) add_notice( req, _( "The ticket comment %(num)s on ticket " "#%(id)s has been deleted.", num=cnum, id=ticket.id)) req.redirect(req.href.ticket(id)) tm = TicketModule(self.env) data = tm._prepare_data(req, ticket) tm._insert_ticket_data(req, ticket, data, get_reporter_id(req, 'author'), {}) data.update(action=action, cdate=None) if action == 'delete-comment': data['cdate'] = req.args.get('cdate') cdate = from_utimestamp(long(data['cdate'])) for change in data['changes']: if change.get('date') == cdate: data['change'] = change data['cnum'] = change.get('cnum') break else: raise TracError(_("Comment %(num)s not found", num=cnum)) elif action == 'delete': attachments = Attachment.select(self.env, ticket.realm, ticket.id) data.update(attachments=list(attachments)) add_stylesheet(req, 'common/css/ticket.css') return 'ticket_delete.html', data
def process(self, commit, status, branch): self.closestatus = status milestones = [m.name for m in Milestone.select(self.env) if m.name != "unknown"] if branch.startswith("fixes/"): branch = branch[6:] milestones = [m for m in milestones if m.startswith(branch)] self.milestone = sorted(milestones)[-1] msg = commit["message"] self.env.log.debug("Processing Commit: %s", msg) msg = "%s \n Branch: %s \n Changeset: %s" % (msg, branch, commit["id"]) # author = commit['author']['name'] author = "Github" timestamp = datetime.now(utc) cmd_groups = command_re.findall(msg) self.env.log.debug("Function Handlers: %s" % cmd_groups) tickets = {} for cmd, tkts in cmd_groups: funcname = self.__class__._supported_cmds.get(cmd.lower(), "") self.env.log.debug("Function Handler: %s" % funcname) if funcname: for tkt_id in ticket_re.findall(tkts): if (branch == "master") or branch.startswith("fixes/"): tickets.setdefault(tkt_id, []).append(getattr(self, funcname)) # disable this stuff for now, it causes duplicates on merges # proper implementation of this will require tracking commit hashes # else: # tickets.setdefault(tkt_id, []).append(self._cmdRefs) for tkt_id, cmds in tickets.iteritems(): try: db = self.env.get_db_cnx() ticket = Ticket(self.env, int(tkt_id), db) for cmd in cmds: cmd(ticket) # determine sequence number... cnum = 0 tm = TicketModule(self.env) for change in tm.grouped_changelog_entries(ticket, db): if change["permanent"]: cnum += 1 ticket.save_changes(author, msg, timestamp, db, cnum + 1) db.commit() tn = TicketNotifyEmail(self.env) tn.notify(ticket, newticket=0, modtime=timestamp) except Exception, e: import traceback traceback.print_exc(file=sys.stderr)
def __init__(self, project=options.project, author=AUTHOR, maxage=options.maxage, url=options.url): self.env = open_environment(project) db = self.env.get_db_cnx() cursor = db.cursor() if url is None: url = self.env.config.get('trac', 'base_url') self.env.href = Href(url) self.env.abs_href = Href(url) self.msg = MESSAGE % (maxage) self.now = int(time.time()) maxtime = int(time.time()) - (60 * 60 * 24 * maxage) cursor.execute("SELECT id FROM ticket t, ticket_custom c " \ "WHERE t.status <> %s " \ "AND t.changetime < %s " \ "AND t.id = c.ticket " \ "AND c.name = %s " \ "AND c.value = %s ", ('closed', maxtime, 'pending', '1')) rows = cursor.fetchall() for row in rows: id = row[0] try: ticket = Ticket(self.env, id, db); ticket['status'] = 'closed' ticket['pending'] = '0'; # determine sequence number... cnum = 0 tm = TicketModule(self.env) for change in tm.grouped_changelog_entries(ticket, db): if change['permanent']: cnum += 1 ticket.save_changes(author, self.msg, self.now, db, cnum + 1) db.commit() print 'Closing Ticket %s (%s)\n' % (id, ticket['summary']) tn = TicketNotifyEmail(self.env) tn.notify(ticket, newticket=0, modtime=self.now) except Exception, e: import traceback traceback.print_exc(file=sys.stderr) print>>sys.stderr, 'Unexpected error while processing ticket ' \ 'ID %s: %s' % (id, e)
def process(self, commit, status, enable_revmap, reponame): self.closestatus = status msg = commit['message'] self.env.log.debug("Processing Commit: %s", msg) note = "Changeset: [/changeset/%s %s]" % (commit['id'], commit['id']) url = "URL: %s" % commit['url'] msg = "%s \n * %s \n * %s" % (msg, note, url) author = commit['author']['name'] timestamp = datetime.now(utc) if int(enable_revmap): self.env.log.debug("adding commit %s to revmap", commit['id']) db = self.env.get_db_cnx() cursor = db.cursor() cursor.execute( "INSERT INTO svn_revmap (svn_rev, git_hash, commit_msg) VALUES (0, %s, %s);", (commit['id'], commit['message'])) db.commit() cmd_groups = command_re.findall(msg) self.env.log.debug("Function Handlers: %s" % cmd_groups) tickets = {} for cmd, tkts in cmd_groups: funcname = self.__class__._supported_cmds.get(cmd.lower(), '') self.env.log.debug("Function Handler: %s" % funcname) if funcname: for tkt_id in ticket_re.findall(tkts): func = getattr(self, funcname) tickets.setdefault(tkt_id, []).append(func) for tkt_id, cmds in tickets.iteritems(): try: db = self.env.get_db_cnx() ticket = Ticket(self.env, int(tkt_id), db) for cmd in cmds: cmd(ticket) # determine sequence number... cnum = 0 tm = TicketModule(self.env) for change in tm.grouped_changelog_entries(ticket, db): if change['permanent']: cnum += 1 ticket.save_changes(author, msg, timestamp, db, cnum + 1) db.commit() tn = TicketNotifyEmail(self.env) tn.notify(ticket, newticket=0, modtime=timestamp) except Exception, e: import traceback traceback.print_exc(file=sys.stderr)
def process(self, commit, status, enable_revmap,reponame): self.closestatus = status msg = commit['message'] self.env.log.debug("Processing Commit: %s", msg) note = "Changeset: [/changeset/%s %s]" % (commit['id'], commit['id']) url = "URL: %s" % commit['url'] msg = "%s \n * %s \n * %s" % (msg, note, url) author = commit['author']['name'] timestamp = datetime.now(utc) if int(enable_revmap): self.env.log.debug("adding commit %s to revmap", commit['id']) db = self.env.get_db_cnx() cursor = db.cursor() cursor.execute("INSERT INTO svn_revmap (svn_rev, git_hash, commit_msg) VALUES (0, %s, %s);", (commit['id'], commit['message'])) db.commit() cmd_groups = command_re.findall(msg) self.env.log.debug("Function Handlers: %s" % cmd_groups) tickets = {} for cmd, tkts in cmd_groups: funcname = self.__class__._supported_cmds.get(cmd.lower(), '') self.env.log.debug("Function Handler: %s" % funcname) if funcname: for tkt_id in ticket_re.findall(tkts): func = getattr(self, funcname) tickets.setdefault(tkt_id, []).append(func) for tkt_id, cmds in tickets.iteritems(): try: db = self.env.get_db_cnx() ticket = Ticket(self.env, int(tkt_id), db) for cmd in cmds: cmd(ticket) # determine sequence number... cnum = 0 tm = TicketModule(self.env) for change in tm.grouped_changelog_entries(ticket, db): if change['permanent']: cnum += 1 ticket.save_changes(author, msg, timestamp, db, cnum+1) db.commit() tn = TicketNotifyEmail(self.env) tn.notify(ticket, newticket=0, modtime=timestamp) except Exception, e: import traceback traceback.print_exc(file=sys.stderr)
def __init__(self, project=options.project, author=options.user, rev=options.rev, url=options.url): self.env = open_environment(project) repos = self.env.get_repository() repos.sync() # Instead of bothering with the encoding, we'll use unicode data # as provided by the Trac versioncontrol API (#1310). try: chgset = repos.get_changeset(rev) except NoSuchChangeset: return # out of scope changesets are not cached self.author = chgset.author self.rev = rev self.msg = "(In [%s]) %s" % (rev, chgset.message) self.now = datetime.now(utc) cmd_groups = command_re.findall(self.msg) tickets = {} for cmd, tkts in cmd_groups: funcname = CommitHook._supported_cmds.get(cmd.lower(), '') if funcname: for tkt_id in ticket_re.findall(tkts): func = getattr(self, funcname) tickets.setdefault(tkt_id, []).append(func) for tkt_id, cmds in tickets.iteritems(): try: db = self.env.get_db_cnx() ticket = Ticket(self.env, int(tkt_id), db) for cmd in cmds: cmd(ticket) # determine sequence number... cnum = 0 tm = TicketModule(self.env) for change in tm.grouped_changelog_entries(ticket, db): if change['permanent']: cnum += 1 ticket.save_changes(self.author, self.msg, self.now, db, cnum+1) db.commit() tn = TicketNotifyEmail(self.env) tn.notify(ticket, newticket=0, modtime=self.now) except Exception, e: # import traceback # traceback.print_exc(file=sys.stderr) print>>sys.stderr, 'Unexpected error while processing ticket ' \ 'ID %s: %s' % (tkt_id, e)
def _update_ticket(ticket): # Determine sequence number. cnum = 0 tm = TicketModule(self.env) db = self.env.get_db_cnx() for change in tm.grouped_changelog_entries(ticket, db): # FIXME - should this say "and change['cnum'] > cnum? if change['permanent']: cnum = change['cnum'] # FIXME - Put something in the message? # FIXME - the ticket_changed method gets an author, should # this say "value propagation on behalf of <author>"? ticket.save_changes('value propagation', '', when, db, cnum+1)
def _update_controls(self, req, action_controls, actions, tkt): """Given list of actions, update action_controls w/all requiring input. control[2] represents HTML inputs required before an action can be completed. If it exists, we make a note of the action operation.""" tm = TicketModule(self.env) for (act, act_ops) in actions.itervalues(): for act_op in act_ops: if act_op not in action_controls: control = list(tm.get_action_control(req, act, tkt)) control[2] = str(control[2]) if control[2]: action_controls[act_op] = control
def __init__(self, project=options.project, author=AUTHOR, maxage=options.maxage): try: self.env = open_environment(project) db = self.env.get_db_cnx() cursor = db.cursor() msg = MESSAGE % (maxage) now = datetime.now(utc) maxtime = to_utimestamp(now - timedelta(days=maxage)) cursor.execute("SELECT id FROM ticket " \ "WHERE status = %s " \ "AND changetime < %s ", ('pending', maxtime)) rows = cursor.fetchall() for row in rows: id = row[0] try: ticket = Ticket(self.env, id, db); ticket['status'] = 'closed' # determine sequence number... cnum = 0 tm = TicketModule(self.env) for change in tm.grouped_changelog_entries(ticket, db): c_cnum = change.get('cnum', None) if c_cnum and int(c_cnum) > cnum: cnum = int(c_cnum) ticket.save_changes(author, msg, now, db, str(cnum + 1)) db.commit() print 'Closing Ticket %s (%s)' % (id, ticket['summary']) tn = TicketNotifyEmail(self.env) tn.notify(ticket, newticket=0, modtime=now) except Exception, e: import traceback traceback.print_exc(file=sys.stderr) print>>sys.stderr, 'Unexpected error while processing ticket ' \ 'ID %s: %s' % (id, e) except Exception, e: import traceback traceback.print_exc(file=sys.stderr) print>>sys.stderr, 'Unexpected error while retrieving tickets '
def process_request(self, req): id = int(req.args.get('id')) req.perm('ticket', id).require('TICKET_ADMIN') ticket = Ticket(self.env, id) action = req.args['action'] cnum = req.args.get('cnum') if req.method == 'POST': if 'cancel' in req.args: href = req.href.ticket(id) if action == 'delete-comment': href += '#comment:%s' % cnum req.redirect(href) if action == 'delete': ticket.delete() add_notice(req, _('The ticket #%(id)s has been deleted.', id=ticket.id)) req.redirect(req.href()) elif action == 'delete-comment': cdate = from_utimestamp(long(req.args.get('cdate'))) ticket.delete_change(cdate=cdate) add_notice(req, _('The ticket comment %(num)s on ticket ' '#%(id)s has been deleted.', num=cnum, id=ticket.id)) req.redirect(req.href.ticket(id)) tm = TicketModule(self.env) data = tm._prepare_data(req, ticket) tm._insert_ticket_data(req, ticket, data, get_reporter_id(req, 'author'), {}) data.update(action=action, cdate=None) if action == 'delete-comment': data['cdate'] = req.args.get('cdate') cdate = from_utimestamp(long(data['cdate'])) for change in data['changes']: if change.get('date') == cdate: data['change'] = change data['cnum'] = change.get('cnum') break else: raise TracError(_('Comment %(num)s not found', num=cnum)) elif action == 'delete': attachments = Attachment.select(self.env, ticket.realm, ticket.id) data.update(attachments=list(attachments)) add_stylesheet(req, 'common/css/ticket.css') return 'ticket_delete.html', data, None
def process_request(self, req): id = int(req.args.get('id')) req.perm('ticket', id).require('TICKET_ADMIN') ticket = Ticket(self.env, id) action = req.args['action'] if req.method == 'POST': if 'cancel' in req.args: href = req.href.ticket(id) if action == 'delete-comment': href += '#comment:%s' % req.args.get('cnum') req.redirect(href) if action == 'delete': ticket.delete() add_notice( req, _('The ticket #%(id)s has been deleted.', id=ticket.id)) req.redirect(req.href()) elif action == 'delete-comment': cnum = int(req.args.get('cnum')) ticket.delete_change(cnum) add_notice( req, _( 'The ticket comment %(num)s on ticket ' '#%(id)s has been deleted.', num=cnum, id=ticket.id)) req.redirect(req.href.ticket(id)) tm = TicketModule(self.env) data = tm._prepare_data(req, ticket) tm._insert_ticket_data(req, ticket, data, get_reporter_id(req, 'author'), {}) data.update(action=action, del_cnum=None) if action == 'delete-comment': cnum = int(req.args.get('cnum')) data['del_cnum'] = cnum for change in data['changes']: if change.get('cnum') == cnum: data['change'] = change break else: raise TracError(_('Comment %(num)s not found', num=cnum)) add_stylesheet(req, 'common/css/ticket.css') return 'ticket_delete.html', data, None
def _render_editor(self, req, db, milestone): data = { 'milestone': milestone, 'ticket': milestone.ticket, 'datefields': self.date_fields, 'date_hint': get_date_format_hint(), 'datetime_hint': get_datetime_format_hint(), 'milestone_groups': [], 'jump_to': req.args.get('jump_to') or referer_module(req) } if milestone.exists: req.perm(milestone.resource).require('MILESTONE_VIEW') milestones = [ m for m in StructuredMilestone.select(self.env, db=db) if m.name != milestone.name and 'MILESTONE_VIEW' in req.perm(m.resource) ] data['milestone_groups'] = group_milestones( milestones, 'TICKET_ADMIN' in req.perm) else: req.perm(milestone.resource).require('MILESTONE_CREATE') TicketModule(self.env)._insert_ticket_data( req, milestone.ticket, data, get_reporter_id(req, 'author'), {}) self._add_tickets_report_data(milestone, req, data) context = Context.from_request(req, milestone.resource) data['attachments'] = AttachmentModule( self.env).attachment_data(context) return 'itteco_milestone_edit.html', data, None
def decorate_message(self, event, message, charset): if event.realm != 'ticket': return if event.category == 'batchmodify': notify = BatchTicketNotifyEmail(self.env) tickets = event.target tickets_descr = ', '.join(['#%s' % t for t in tickets]) subject = notify.format_subj(tickets_descr) else: notify = TicketNotifyEmail(self.env) ticket = event.target notify.ticket = ticket summary = ticket['summary'] from trac.ticket.web_ui import TicketModule for change in TicketModule(self.env) \ .grouped_changelog_entries(ticket, when=event.time): if 'summary' in change['fields']: values = change['fields']['summary'] summary = "%s (was: %s)" % (values['new'], values['old']) subject = notify.format_subj(summary, event.category == 'created') msgid = self._get_message_id(event, newticket=True) url = self.env.abs_href.ticket(ticket.id) if event.category == 'created': set_header(message, 'Message-ID', msgid, charset) else: set_header(message, 'Message-ID', self._get_message_id(event), charset) set_header(message, 'In-Reply-To', msgid, charset) set_header(message, 'References', msgid, charset) cnum = ticket.get_comment_number(event.time) if cnum is not None: url += '#comment:%d' % cnum set_header(message, 'X-Trac-Ticket-ID', ticket.id, charset) set_header(message, 'X-Trac-Ticket-URL', url, charset) set_header(message, 'Subject', subject, charset)
def setUp(self): self.env = EnvironmentStub(default_data=True) config = self.env.config config.set('ticket-workflow', 'change_owner', 'new -> new') config.set('ticket-workflow', 'change_owner.operations', 'set_owner') self.ctlr = TicketSystem(self.env).action_controllers[0] self.ticket_module = TicketModule(self.env)
def pre_process_request(self, req, handler): if handler is not TicketModule(self.env): return handler action = req.args.get('action') if action in ('delete', 'delete-comment'): return self else: return handler
def process(self, commit, status, payload): self.closestatus = status self.env.log.debug("Processing Commit: %s", commit['id']) comment = (commit['message'] + "\n\n" + self.comment_template.format(commit=commit,**payload)) self.env.log.debug("Prepared Comment: %s", comment) author = commit['author']['name'] timestamp = datetime.now(utc) cmd_groups = command_re.findall(comment) self.env.log.debug("Function Handlers: %s" % cmd_groups) tickets = {} for cmd, tkts in cmd_groups: funcname = self.__class__._supported_cmds.get(cmd.lower(), '') self.env.log.debug("Function Handler: %s" % funcname) if funcname: for tkt_id in ticket_re.findall(tkts): func = getattr(self, funcname) tickets.setdefault(tkt_id, []).append(func) for tkt_id, cmds in tickets.iteritems(): try: db = self.env.get_db_cnx() ticket = Ticket(self.env, int(tkt_id), db) for cmd in cmds: cmd(ticket) # determine sequence number... cnum = 0 tm = TicketModule(self.env) for change in tm.grouped_changelog_entries(ticket, db): if change['permanent']: cnum += 1 ticket.save_changes(author, comment, timestamp, db, cnum+1) db.commit() tn = TicketNotifyEmail(self.env) tn.notify(ticket, newticket=0, modtime=timestamp) except Exception, e: import traceback traceback.print_exc(file=sys.stderr)
def setUp(self): self.env = EnvironmentStub() self.env.config.set( 'trac', 'templates_dir', os.path.join(os.path.dirname(self.env.path), 'templates')) self.ticket_module = TicketModule(self.env) self.mimeview = Mimeview(self.env) self.req = MockRequest(self.env, authname='anonymous')
def setUp(self): self.env = EnvironmentStub() self.ticket_module = TicketModule(self.env) self.mimeview = Mimeview(self.env) self.req = Mock(hdf=HDFWrapper(['./templates']), base_path='/trac.cgi', path_info='', href=Href('/trac.cgi'))
def process(self, commit, status): self.closestatus = status msg = commit['message'] self.env.log.debug("Processing Commit: %s", msg) note = "Changeset: %s" % commit['id'] msg = "%s[[BR]]\n%s" % (msg, note) author = commit['author']['name'] timestamp = datetime.now(utc) cmd_groups = command_re.findall(msg) self.env.log.debug("Function Handlers: %s" % cmd_groups) tickets = {} for cmd, tkts in cmd_groups: funcname = self.__class__._supported_cmds.get(cmd.lower(), '') self.env.log.debug("Function Handler: %s" % funcname) if funcname: for tkt_id in ticket_re.findall(tkts): func = getattr(self, funcname) tickets.setdefault(tkt_id, []).append(func) for tkt_id, cmds in tickets.iteritems(): try: db = self.env.get_db_cnx() ticket = Ticket(self.env, int(tkt_id), db) for cmd in cmds: cmd(ticket) # determine sequence number... cnum = 0 tm = TicketModule(self.env) for change in tm.grouped_changelog_entries(ticket, db): if change['permanent']: cnum += 1 ticket.save_changes(author, msg, timestamp, db, cnum + 1) db.commit() tn = TicketNotifyEmail(self.env) tn.notify(ticket, newticket=0, modtime=timestamp) except Exception, e: import traceback traceback.print_exc(file=sys.stderr)
def handle_commit(commit, env, repo): from trac.ticket.notification import TicketNotifyEmail from trac.ticket import Ticket from trac.ticket.web_ui import TicketModule from trac.util.text import to_unicode from trac.util.datefmt import utc commit_log = call_git("rev-list", ["-n", "1", commit, "--pretty=medium"]) commit_log = process_commit_log(commit_log, repo) commit_log = commit_log.rstrip() msg = to_unicode(commit_log) eml = to_unicode(call_git("rev-list", ["-n", "1", commit, "--pretty=format:%ae"]).splitlines()[1]) now = datetime.now(utc) tickets = {} for cmd, tkts in command_re.findall(msg.split("\n\n", 1)[1]): action = COMMANDS.get(cmd.lower()) if action: for tkt_id in ticket_re.findall(tkts): tickets.setdefault(tkt_id, []).append(action) for tkt_id, actions in tickets.iteritems(): try: db = env.get_db_cnx() ticket = Ticket(env, int(tkt_id), db) if "close" in actions: ticket["status"] = "closed" ticket["resolution"] = "fixed" # determine sequence number... cnum = 0 tm = TicketModule(env) for change in tm.grouped_changelog_entries(ticket, db): if change["permanent"]: cnum += 1 ticket.save_changes(eml, msg, now, db, cnum + 1) db.commit() tn = TicketNotifyEmail(env) tn.notify(ticket, newticket=0, modtime=now) except Exception, e: print >>sys.stderr, "Unexpected error while processing ticket ID %s: %s" % (tkt_id, e)
def on_change(self, env, chgset): self.env = env self.author = chgset.author self.rev = chgset.rev self.msg = "(In [%s]) %s" % (self.rev, chgset.message) self.now = chgset.date cmd_groups = command_re.findall(self.msg) tickets = {} for cmd, tkts in cmd_groups: funcname = self._supported_cmds.get(cmd.lower(), '') if funcname: for tkt_id in ticket_re.findall(tkts): func = getattr(self, funcname) tickets.setdefault(tkt_id, []).append(func) for tkt_id, cmds in tickets.iteritems(): try: db = self.env.get_db_cnx() ticket = Ticket(self.env, int(tkt_id), db) for cmd in cmds: cmd(ticket) # determine sequence number... cnum = 0 tm = TicketModule(self.env) for change in tm.grouped_changelog_entries(ticket, db): if change['permanent']: cnum += 1 ticket.save_changes(self.author, self.msg, self.now, db, cnum + 1) db.commit() tn = TicketNotifyEmail(self.env) tn.notify(ticket, newticket=0, modtime=self.now) except Exception, e: # import traceback # traceback.print_exc(file=sys.stderr) print>>sys.stderr, 'Unexpected error while processing ticket ' \ 'ID %s: %s' % (tkt_id, e)
def test_get_moved_attributes(self): """The attributes `max_comment_size`, `max_description_size` and `max_summary_size` have been moved to TicketSystem but are accessible on TicketModule for backward compatibility. """ ts = TicketSystem(self.env) tm = TicketModule(self.env) self.assertEqual(ts.max_comment_size, tm.max_comment_size) self.assertEqual(ts.max_description_size, tm.max_description_size) self.assertEqual(ts.max_summary_size, tm.max_summary_size)
def _preserve_newlines(env): ticket = TicketModule(env) # Trac 0.11.2 later if hasattr(ticket, 'must_preserve_newlines'): return ticket.must_preserve_newlines preserve_newlines = ticket.preserve_newlines if preserve_newlines == 'default': preserve_newlines = env.get_version(initial=True) >= 21 # 0.11 return preserve_newlines in _TRUE_VALUES
def on_change(self, env, chgset): self.env = env self.author = chgset.author self.rev = chgset.rev self.msg = "(In [%s]) %s" % (self.rev, chgset.message) self.now = chgset.date cmd_groups = command_re.findall(self.msg) tickets = {} for cmd, tkts in cmd_groups: funcname = self._supported_cmds.get(cmd.lower(), '') if funcname: for tkt_id in ticket_re.findall(tkts): func = getattr(self, funcname) tickets.setdefault(tkt_id, []).append(func) for tkt_id, cmds in tickets.iteritems(): try: db = self.env.get_db_cnx() ticket = Ticket(self.env, int(tkt_id), db) for cmd in cmds: cmd(ticket) # determine sequence number... cnum = 0 tm = TicketModule(self.env) for change in tm.grouped_changelog_entries(ticket, db): if change['permanent']: cnum += 1 ticket.save_changes(self.author, self.msg, self.now, db, cnum+1) db.commit() tn = TicketNotifyEmail(self.env) tn.notify(ticket, newticket=0, modtime=self.now) except Exception, e: # import traceback # traceback.print_exc(file=sys.stderr) print>>sys.stderr, 'Unexpected error while processing ticket ' \ 'ID %s: %s' % (tkt_id, e)
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))
def _comment_ticket(self, req, tkt_id): tkt = Ticket(self.env, tkt_id) if not tkt.exists: raise ResourceNotFound('Ticket %s does not exist.' % tkt_id, 'Invalid Ticket Id') req.perm.require('TICKET_MODIFY', Resource('ticket', tkt.resource)) changes = TicketModule(self.env).rendered_changelog_entries(req, tkt) return 'itteco_ticket_comment.html', { 'ticket': tkt, 'changes': changes }, 'text/html'
def handle_commit(commit, env): from trac.ticket.notification import TicketNotifyEmail from trac.ticket import Ticket from trac.ticket.web_ui import TicketModule from trac.util.text import to_unicode from trac.util.datefmt import utc msg = to_unicode(call_git('rev-list', ['-n', '1', commit, '--pretty=medium']).rstrip()) eml = to_unicode(call_git('rev-list', ['-n', '1', commit, '--pretty=format:%ae']).splitlines()[1]) now = datetime.now(utc) tickets = {} for cmd, tkts in command_re.findall(msg.split('\n\n', 1)[1]): action = COMMANDS.get(cmd.lower()) if action: for tkt_id in ticket_re.findall(tkts): tickets.setdefault(tkt_id, []).append(action) for tkt_id, actions in tickets.iteritems(): try: db = env.get_db_cnx() ticket = Ticket(env, int(tkt_id), db) if 'close' in actions: ticket['status'] = 'closed' ticket['resolution'] = 'fixed' # determine sequence number... cnum = 0 tm = TicketModule(env) for change in tm.grouped_changelog_entries(ticket, db): if change['permanent']: cnum += 1 ticket.save_changes(eml, msg, now, db, cnum + 1) db.commit() tn = TicketNotifyEmail(env) tn.notify(ticket, newticket=0, modtime=now) except Exception, e: print >>sys.stderr, 'Unexpected error while processing ticket ID %s: %s' % (tkt_id, e)
def setUp(self): self.env = EnvironmentStub() self.env.config.set('trac', 'templates_dir', os.path.join(os.path.dirname(self.env.path), 'templates')) self.ticket_module = TicketModule(self.env) self.mimeview = Mimeview(self.env) self.req = Mock(base_path='/trac.cgi', path_info='', href=Href('/trac.cgi'), chrome={'logo': {}}, abs_href=Href('http://example.org/trac.cgi'), environ={}, perm=[], authname='-', args={}, tz=None, locale='', session=None, form_token=None)
def _comment_milestone(self, req, mil_id): milestone = StructuredMilestone(self.env, mil_id) if not milestone.exists: raise ResourceNotFound('Milestone %s does not exist.' % mil_id, 'Invalid Milestone Name') req.perm.require('MILESTONE_MODIFY', milestone.resource) changes = TicketModule(self.env).rendered_changelog_entries( req, milestone.ticket) return 'itteco_milestone_comment.html', { 'milestone': milestone, 'changes': changes }, 'text/html'
def validate_ticket(self, req, ticket): """Validate any user fields by checking to see if the specified user belongs to any of the fields' allowed groups""" for field in self._user_fields(): flc = TicketModule(self.env).field_layout_controller fl_config = flc.get_layout_for_field_on_type(ticket['type'],field) manual_entry = self.config.get("ticket-custom", field + ".manual") if not manual_entry and fl_config: username = (ticket[field] or u'').strip() valid_groups = self._get_valid_groups(field) valid = False if not username and not fl_config.get("mandatory"): continue try: spsystem = SimplifiedPermissionsSystem(self.env) for provider in spsystem.user_lookup_providers: info = provider.fetch_user_data(username) if info and "groups" in info: if any(g in info["groups"] for g in valid_groups): valid = True break if valid: continue else: yield field, ("User '%s', selected for field '%s' does" " not appear to be a member of any of" " the valid groups '%s'" % (username, field, ", ".join(valid_groups))) except Exception: self.log.warn('UserFieldModule: Got an exception, ' 'assuming it is a validation failure.\n%s', format_exc()) yield field, ("Field %s does not appear to contain a valid" " user" % (field))
def process(self, commit): msg = commit['message'] author = commit['author']['name'] timestamp = datetime.now(utc) cmd_groups = command_re.findall(msg) tickets = {} for cmd, tkts in cmd_groups: funcname = self.__class__._supported_cmds.get(cmd.lower(), '') if funcname: for tkt_id in ticket_re.findall(tkts): func = getattr(self, funcname) tickets.setdefault(tkt_id, []).append(func) for tkt_id, cmds in tickets.iteritems(): try: db = self.env.get_db_cnx() ticket = Ticket(self.env, int(tkt_id), db) for cmd in cmds: cmd(ticket) # determine sequence number... cnum = 0 tm = TicketModule(self.env) for change in tm.grouped_changelog_entries(ticket, db): if change['permanent']: cnum += 1 ticket.save_changes(author, msg, timestamp, db, cnum+1) db.commit() tn = TicketNotifyEmail(self.env) tn.notify(ticket, newticket=0, modtime=timestamp) except Exception, e: import traceback traceback.print_exc(file=sys.stderr)
def _get_search_filters(self, req): filters = [] if TicketModule(self.env).get_search_filters(req) is not None: filters.extend([{ 'name': ticket.name, 'label': ticket.name, 'active': True } for ticket in Type.select(self.env)]) wikifilters = WikiModule(self.env).get_search_filters(req) if wikifilters: filters.extend([{ 'name': f[0], 'label': f[1], 'active': True } for f in wikifilters]) return filters
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 get_navigation_items(self, req): # Don't allow this to be exposed if 'DO_PRIVATETICKETS_FILTER' in req.args.keys(): del req.args['DO_PRIVATETICKETS_FILTER'] # Various ways to allow access if not req.perm.has_permission('TICKET_VIEW') and \ (req.perm.has_permission('TICKET_VIEW_REPORTER') or \ req.perm.has_permission('TICKET_VIEW_OWNER') or \ req.perm.has_permission('TICKET_VIEW_CC') or \ req.perm.has_permission('TICKET_VIEW_REPORTER_GROUP') or \ req.perm.has_permission('TICKET_VIEW_OWNER_GROUP') or \ req.perm.has_permission('TICKET_VIEW_CC_GROUP')): if TicketModule(self.env).match_request(req): if PrivateTicketsSystem(self.env).check_ticket_access( req, req.args['id']): self._grant_view(req) elif AttachmentModule(self.env).match_request(req): if req.args['type'] == 'ticket' and PrivateTicketsSystem( self.env).check_ticket_access( req, req.args['path'].split('/')[0]): self._grant_view(req) elif QueryModule(self.env).match_request(req): req.args['DO_PRIVATETICKETS_FILTER'] = 'query' self._grant_view(req) # Further filtering in query.py elif SearchModule(self.env).match_request(req): if 'ticket' in req.args.keys(): req.args['pticket'] = req.args['ticket'] del req.args['ticket'] elif ReportModule(self.env).match_request(req): self._grant_view(req) # So they can see the query page link if req.args.get('id'): req.args['DO_PRIVATETICKETS_FILTER'] = 'report' # NOTE: Send this back here because the button would be hidden otherwise. <NPK t:1129> if not self.env.is_component_enabled( ReportModule) or not req.perm.has_permission( 'REPORT_VIEW'): return [('mainnav', 'tickets', html.A('View Tickets', href=req.href.query()))] return []
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 _create_sheet_history(self, req, context, data, book): sheet = book.add_sheet(dgettext("messages", "Change History")) writer = WorksheetWriter(sheet, req) groups = data['groups'] headers = [header for header in data['headers'] if header['name'] not in ('id', 'time', 'changetime')] headers[0:0] = [ {'name': 'id', 'label': dgettext("messages", "Ticket")}, {'name': 'time', 'label': dgettext("messages", "Time")}, {'name': 'author', 'label': dgettext("messages", "Author")}, {'name': 'comment', 'label': dgettext("messages", "Comment")}, ] writer.write_row( (header['label'], 'thead', None, None) for idx, header in enumerate(headers)) mod = TicketModule(self.env) for result in chain(*[results for groupname, results in groups]): id = result['id'] ticket = Ticket(self.env, id) ticket_context = context('ticket', id) values = ticket.values.copy() changes = [] for change in mod.rendered_changelog_entries(req, ticket): if change['permanent']: changes.append(change) for change in reversed(changes): change['values'] = values values = values.copy() for name, field in change['fields'].iteritems(): if name in values: values[name] = field['old'] changes[0:0] = [{'date': ticket.time_created, 'fields': {}, 'values': values, 'cnum': None, 'comment': '', 'author': ticket['reporter']}] for change in changes: cells = [] for idx, header in enumerate(headers): name = header['name'] if name == 'id': value = id elif name == 'time': value = change.get('date', '') elif name == 'comment': value = change.get('comment', '') elif name == 'author': value = change.get('author', '') else: value = change['values'].get(name, '') value, style, width, line = \ self._get_cell_data(name, value, req, ticket_context, writer) if name in change['fields']: style = '%s:change' % style cells.append((value, style, width, line)) writer.write_row(cells) writer.set_col_widths()
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))
def setUp(self): self.env = EnvironmentStub(default_data=True, enable=['trac.*', 'advancedworkflow.*']) self.tktmod = TicketModule(self.env)
class AdvancedTicketWorkflowTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub(default_data=True, enable=['trac.*', 'advancedworkflow.*']) self.tktmod = TicketModule(self.env) def tearDown(self): self.env.reset_db() def _config_set(self, section, entries): for option, value in entries: self.env.config.set(section, option, value) def _insert_ticket(self, when=None, **values): values.setdefault('status', 'new') values.setdefault('type', 'defect') ticket = Ticket(self.env) ticket.populate(values) return ticket.insert(when=when) def _insert_component(self, name, owner): component = model.Component(self.env) component.name = name component.owner = owner component.insert() def _post_req(self, action, ticket): form_token = 'x' * 40 args = {'action': action, 'submit': '1', '__FORM_TOKEN': form_token, 'view_time': str(to_utimestamp(ticket['changetime']))} args.update(('field_' + f['name'], ticket[f['name']]) for f in ticket.fields) return MockRequest(self.env, method='POST', form_token=form_token, path_info='/ticket/%d' % ticket.id, args=args) def test_set_owner_to_reporter(self): self.env.config.set('ticket', 'workflow', 'ConfigurableTicketWorkflow,TicketWorkflowOpOwnerReporter') self._config_set('ticket-workflow', [ ('needinfo', '* -> needinfo'), ('needinfo.name', 'Need info'), ('needinfo.operations', 'set_owner_to_reporter'), ]) tktid = self._insert_ticket(summary='set owner to reporter', reporter='john', owner='joe') ticket = Ticket(self.env, tktid) req = self._post_req('needinfo', ticket) self.assertTrue(self.tktmod.match_request(req)) self.assertRaises(RequestDone, self.tktmod.process_request, req) ticket = Ticket(self.env, tktid) self.assertEqual('john', ticket['owner']) self.assertEqual('needinfo', ticket['status']) def test_set_owner_to_component_owner(self): self.env.config.set('ticket', 'workflow', 'ConfigurableTicketWorkflow,TicketWorkflowOpOwnerComponent') self._config_set('ticket-workflow', [ ('to-c-owner', '* -> assigned'), ('to-c-owner.operations', 'set_owner_to_component_owner'), ]) self._insert_component('component3', 'foo') tktid = self._insert_ticket(summary='set owner to component owner', reporter='anonymous', owner='joe', component='component3') ticket = Ticket(self.env, tktid) req = self._post_req('to-c-owner', ticket) self.assertTrue(self.tktmod.match_request(req)) self.assertRaises(RequestDone, self.tktmod.process_request, req) ticket = Ticket(self.env, tktid) self.assertEqual('foo', ticket['owner']) self.assertEqual('assigned', ticket['status']) def test_set_owner_to_component_owner_with_missing_component(self): self.env.config.set('ticket', 'workflow', 'ConfigurableTicketWorkflow,TicketWorkflowOpOwnerComponent') self._config_set('ticket-workflow', [ ('to-c-owner', '* -> assigned'), ('to-c-owner.operations', 'set_owner_to_component_owner'), ]) tktid = self._insert_ticket(summary='set owner to component owner', reporter='anonymous', owner='joe', component='component3') ticket = Ticket(self.env, tktid) req = self._post_req('to-c-owner', ticket) self.assertTrue(self.tktmod.match_request(req)) self.assertRaises(RequestDone, self.tktmod.process_request, req) ticket = Ticket(self.env, tktid) self.assertEqual('', ticket['owner']) self.assertEqual('assigned', ticket['status']) def test_set_owner_to_field(self): self.env.config.set('ticket', 'workflow', 'ConfigurableTicketWorkflow,TicketWorkflowOpOwnerField') self._config_set('ticket-workflow', [ ('to-owner', '* -> assigned'), ('to-owner.operations', 'set_owner_to_field'), ('to-owner.set_owner_to_field', 'keywords'), ]) tktid = self._insert_ticket(summary='set owner to field', reporter='anonymous', owner='joe', keywords='john') ticket = Ticket(self.env, tktid) req = self._post_req('to-owner', ticket) self.assertTrue(self.tktmod.match_request(req)) self.assertRaises(RequestDone, self.tktmod.process_request, req) ticket = Ticket(self.env, tktid) self.assertEqual('john', ticket['owner']) self.assertEqual('assigned', ticket['status']) def test_set_owner_to_previous(self): self.env.config.set('ticket', 'workflow', 'ConfigurableTicketWorkflow,TicketWorkflowOpOwnerPrevious') self._config_set('ticket-workflow', [ ('to-prev', '* -> assigned'), ('to-prev.operations', 'set_owner_to_previous'), ]) tktid = self._insert_ticket(when=datetime(2017, 3, 9, tzinfo=utc), summary='set owner to previous', reporter='anonymous', owner='joe') ticket = Ticket(self.env, tktid) req = self._post_req('to-prev', ticket) self.assertTrue(self.tktmod.match_request(req)) self.assertRaises(RequestDone, self.tktmod.process_request, req) ticket = Ticket(self.env, tktid) self.assertEqual('joe', ticket['owner']) self.assertEqual('assigned', ticket['status']) ticket = Ticket(self.env, tktid) ticket['owner'] = 'alice' ticket.save_changes(when=datetime(2017, 3, 9, 1, tzinfo=utc)) ticket['owner'] = 'john' ticket.save_changes(when=datetime(2017, 3, 9, 2, tzinfo=utc)) ticket = Ticket(self.env, tktid) req = self._post_req('to-prev', ticket) self.assertTrue(self.tktmod.match_request(req)) self.assertRaises(RequestDone, self.tktmod.process_request, req) ticket = Ticket(self.env, tktid) self.assertEqual('alice', ticket['owner']) self.assertEqual('assigned', ticket['status']) ticket = Ticket(self.env, tktid) req = self._post_req('to-prev', ticket) self.assertTrue(self.tktmod.match_request(req)) self.assertRaises(RequestDone, self.tktmod.process_request, req) ticket = Ticket(self.env, tktid) self.assertEqual('john', ticket['owner']) self.assertEqual('assigned', ticket['status']) def test_set_status_to_previous(self): self.env.config.set('ticket', 'workflow', 'ConfigurableTicketWorkflow,TicketWorkflowOpStatusPrevious') self._config_set('ticket-workflow', [ ('revert-status', '* -> *'), ('revert-status.operations', 'set_status_to_previous'), ]) tktid = self._insert_ticket(when=datetime(2017, 3, 9, tzinfo=utc), summary='set status to previous', reporter='anonymous', owner='joe') ticket = Ticket(self.env, tktid) req = self._post_req('revert-status', ticket) self.assertTrue(self.tktmod.match_request(req)) self.assertRaises(RequestDone, self.tktmod.process_request, req) ticket = Ticket(self.env, tktid) self.assertEqual('new', ticket['status']) ticket = Ticket(self.env, tktid) ticket['status'] = 'assigned' ticket.save_changes(when=datetime(2017, 3, 9, 1, tzinfo=utc)) ticket['status'] = 'closed' ticket.save_changes(when=datetime(2017, 3, 9, 2, tzinfo=utc)) ticket = Ticket(self.env, tktid) req = self._post_req('revert-status', ticket) self.assertTrue(self.tktmod.match_request(req)) self.assertRaises(RequestDone, self.tktmod.process_request, req) ticket = Ticket(self.env, tktid) self.assertEqual('assigned', ticket['status']) def test_reset_milestone(self): self.env.config.set('ticket', 'workflow', 'ConfigurableTicketWorkflow,TicketWorkflowOpResetMilestone') self._config_set('ticket-workflow', [ ('reset-milestone', '* -> *'), ('reset-milestone.operations', 'reset_milestone'), ]) tktid = self._insert_ticket(when=datetime(2017, 3, 9, tzinfo=utc), summary='reset milestone', milestone='milestone1', reporter='anonymous', owner='joe') ticket = Ticket(self.env, tktid) req = self._post_req('reset-milestone', ticket) self.assertTrue(self.tktmod.match_request(req)) self.assertRaises(RequestDone, self.tktmod.process_request, req) ticket = Ticket(self.env, tktid) self.assertEqual('milestone1', ticket['milestone']) milestone = Milestone(self.env, ticket['milestone']) milestone.completed = datetime(2017, 3, 8, tzinfo=utc) milestone.update() req = self._post_req('reset-milestone', ticket) self.assertTrue(self.tktmod.match_request(req)) self.assertRaises(RequestDone, self.tktmod.process_request, req) ticket = Ticket(self.env, tktid) self.assertEqual('', ticket['milestone']) ticket['milestone'] = 'unknown-milestone' ticket.save_changes(when=datetime(2017, 3, 8, 1, tzinfo=utc)) req = self._post_req('reset-milestone', ticket) self.assertTrue(self.tktmod.match_request(req)) self.assertRaises(RequestDone, self.tktmod.process_request, req) ticket = Ticket(self.env, tktid) self.assertEqual('unknown-milestone', ticket['milestone'])
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))
env = open_environment(project) for command, ticketList in commandPattern.findall(message): if commands.has_key(command.lower()): for ticketId in ticketPattern.findall(ticketList): tickets.setdefault(ticketId, []).append(commands[command.lower()]) for ticketId, commands in tickets.iteritems(): db = env.get_db_cnx() ticket = Ticket(env, int(ticketId), db) for command in commands: command(ticket) # determine sequence number... cnum = 0 tm = TicketModule(env) for change in tm.grouped_changelog_entries(ticket, db): c_cnum = change.get('cnum', None) if c_cnum and int(c_cnum) > cnum: cnum = int(c_cnum) username = authorPattern.findall(author)[0] now = datetime.now(utc) message = "(On %s [changeset:%s %s]) %s" % (refname, rev, describe_tags, message) ticket['branch'] = refname ticket.save_changes(username, message, now, db, cnum+1) db.commit() tn = TicketNotifyEmail(env) tn.notify(ticket, newticket=0, modtime=now)
def handle_trac(self): if ('is_draft' in self.options_dict and self.options.is_draft == 'true'): return if self.trac_over_rpc: import xmlrpclib else: if not (os.path.exists(self.trac_env) and os.path.isdir(self.trac_env)): print "trac_env (%s) is not a directory." % self.trac_env sys.exit(1) # trac specific imports from trac.ticket import Ticket from trac.env import open_environment from trac.ticket.notification import TicketNotifyEmail from trac.ticket.web_ui import TicketModule from trac.util.datefmt import utc # should never be used. but why not... if len(self.options.commit) == 0: return # get actual commit and extract ticket number(s) self.commit_msg = call_git('show',['--format=%s%n%b', '--summary', self.options.commit]) # get author for trac comment if 'uploader' in self.options_dict and self.options.uploader: author = self.options.uploader elif 'author' in self.options_dict and self.options.author: author = self.options.author else: author = call_git('rev-list', ['-n', '1', self.options.commit, '--pretty=format:%an <%ae>'] ).splitlines()[1] # find ticket numbers referenced in commit message ticket_numbers = TICKET_RE.findall(self.commit_msg) # create trac comment for every referenced ticket if (ticket_numbers): for ticket_id in ticket_numbers: if self.hook_name.endswith('patchset-created'): msg = self.trac_new_patchset() elif self.hook_name.endswith('change-merged'): msg = self.trac_merge_success() elif self.hook_name.endswith('comment-added'): if self.comment_always: msg = self.trac_new_review() else: if self.options.verified or self.options.review: if self.options.verified_oldValue == None: continue elif self.options.review_oldValue == None: continue msg = self.trac_new_review() if self.debug: print "you should be able to copy and paste the output " \ "to trac-comment-preview:" print "---------------------------------------------------" print "%s\n" % msg print "---------------------------------------------------" print "the author of the comment would be: %s" if self.trac_over_rpc: try: server = xmlrpclib.ServerProxy(self.trac_env) ticket = {} if self.hook_name.endswith('patchset-created'): if re.search( "(close|closed|closes|fix|fixed|fixes) #" + \ ticket_id, self.commit_msg, re.IGNORECASE): ticket['status'] = "testing" elif self.hook_name.endswith('change-merged'): ticket['status'] = "closed" ticket['resolution'] = "fixed" server.ticket.update(int(ticket_id), msg, ticket, True, author) except Exception, e: sys.stderr.write('Unexpected error while handling Trac ' \ 'ticket ID %s: %s (RPC)' \ % (ticket_id, e)) else: try: db = self.env.get_db_cnx() ticket = Ticket(self.env, ticket_id, db) now = datetime.now(utc) if self.hook_name.endswith('patchset-created'): if re.search( "(close|closed|closes|fix|fixed|fixes) #" + \ ticket_id, self.commit_msg, re.IGNORECASE): ticket['status'] = "testing" elif self.hook_name.endswith('change-merged'): ticket['status'] = "closed" ticket['resolution'] = "fixed" cnum = 0 tm = TicketModule(self.env) for change in tm.grouped_changelog_entries(ticket, db): if change['permanent']: cnum += 1 ticket.save_changes(author, msg, now, db, str(cnum+1)) db.commit() tn = TicketNotifyEmail(self.env) tn.notify(ticket, newticket=0, modtime=now) except Exception, e: sys.stderr.write('Unexpected error while handling Trac ' \ 'ticket ID %s: %s (MODULE)' \ % (ticket_id, e))
def __init__(self, project=options.project, author=options.user, rev=options.rev, url=options.url): self.init_env( project ) repos = self.env.get_repository() repos.sync() # Instead of bothering with the encoding, we'll use unicode data # as provided by the Trac versioncontrol API (#1310). try: chgset = repos.get_changeset(rev) except NoSuchChangeset: return # out of scope changesets are not cached self.author = chgset.author self.rev = rev self.msg = "(In [%s]) %s" % (rev, chgset.message) self.now = int(time.time()) cmd_groups = command_re.findall(self.msg) log ("cmd_groups:%s", cmd_groups) tickets = {} for cmd, tkts, xxx1, xxx2 in cmd_groups: log ("cmd:%s, tkts%s ", cmd, tkts) funcname = _supported_cmds.get(cmd.lower(), '') if funcname: for tkt_id, spent in ticket_re.findall(tkts): func = getattr(self, funcname) lst = tickets.setdefault(tkt_id, []) lst.append([func, spent]) for tkt_id, vals in tickets.iteritems(): log ("tkt_id:%s, vals%s ", tkt_id, vals) spent_total = 0.0 try: db = self.env.get_db_cnx() ticket = Ticket(self.env, int(tkt_id), db) for (cmd, spent) in vals: cmd(ticket) if spent: spent_total += float(spent) # determine sequence number... cnum = 0 tm = TicketModule(self.env) for change in tm.grouped_changelog_entries(ticket, db): if change['permanent']: cnum += 1 if spent_total: self._setTimeTrackerFields(ticket, spent_total) ticket.save_changes(self.author, self.msg, self.now, db, cnum+1) db.commit() tn = TicketNotifyEmail(self.env) tn.notify(ticket, newticket=0, modtime=self.now) except Exception, e: # import traceback # traceback.print_exc(file=sys.stderr) log('Unexpected error while processing ticket ' \ 'ID %s: %s' % (tkt_id, e)) print>>sys.stderr, 'Unexpected error while processing ticket ' \ 'ID %s: %s' % (tkt_id, e)
def __init__(self): TicketModule.__init__(self, self.compmgr)