def commit(self): ss = VersionedArtifact.commit(self) session(self).flush() if self.version > 1: v1 = self.get_version(self.version - 1) v2 = self la = [line + '\n' for line in v1.text.splitlines()] lb = [line + '\n' for line in v2.text.splitlines()] diff = ''.join(difflib.unified_diff( la, lb, 'v%d' % v1.version, 'v%d' % v2.version)) description = '<pre>' + diff + '</pre>' if v1.title != v2.title: subject = '%s renamed page %s to %s' % ( context.user.username, v1.title, v2.title) else: subject = '%s modified page %s' % ( context.user.username, self.title) else: description = self.text subject = '%s created page %s' % ( context.user.username, self.title) Feed.post(self, title=None, description=description) Notification.post( artifact=self, topic='metadata', text=description, subject=subject) return ss
def delete(self): subject = '%s removed page %s' % ( context.user.username, self.title) description = self.text Notification.post( artifact=self, topic='metadata', text=description, subject=subject) Shortlink.query.remove(dict(ref_id=self.index_id())) self.deleted = True suffix = " {:%Y-%m-%d %H:%M:%S.%f}".format(datetime.utcnow()) self.title += suffix
def send_notifications(repo, commit_ids): '''Create appropriate notification and feed objects for a refresh''' from allura.model import Feed, Notification commit_msgs = [] base_url = tg.config['base_url'] for oids in utils.chunked_iter(commit_ids, QSIZE): chunk = list(oids) index = dict( (doc._id, doc) for doc in Commit.query.find(dict(_id={'$in': chunk}))) for oid in chunk: ci = index[oid] href = repo.url_for_commit(oid) title = _title(ci.message) summary = _summarize(ci.message) Feed.post( repo, title=title, description='%s<br><a href="%s">View Changes</a>' % ( summary, href), author_link=ci.author_url, author_name=ci.authored.name, link=href, unique_id=href) branches = repo.symbolics_for_commit(ci)[0] commit_msgs.append('%s: %s by %s %s%s' % ( ",".join(b for b in branches), summary, ci.authored.name, base_url, ci.url())) if commit_msgs: if len(commit_msgs) > 1: subject = '%d new commits to %s %s' % ( len(commit_msgs), repo.app.project.name, repo.app.config.options.mount_label) text = '\n\n'.join(commit_msgs) else: subject = '{0} - {1}: {2}'.format( repo.shorthand_for_commit(ci._id), ci.authored.name, summary) branches = repo.symbolics_for_commit(ci)[0] text_branches = ('%s: ' % ",".join(b for b in branches) if branches else '') text = "%s%s %s%s" % (text_branches, ci.message, base_url, ci.url()) Notification.post( artifact=repo, topic='metadata', subject=subject, text=text)
def commit(self): VersionedArtifact.commit(self) monitoring_email = self.app.config.options.get('TicketMonitoringEmail') if self.version > 1: hist = TicketHistory.query.get(artifact_id=self._id, version=self.version-1) old = hist.data changes = ['Ticket %s has been modified: %s' % ( self.ticket_num, self.summary), 'Edited By: %s (%s)' % (c.user.get_pref('display_name'), c.user.username)] fields = [ ('Summary', old.summary, self.summary), ('Status', old.status, self.status) ] if old.status != self.status and self.status in c.app.globals.set_of_closed_status_names: h.log_action(log, 'closed').info('') g.statsUpdater.ticketEvent("closed", self, self.project, self.assigned_to) for key in self.custom_fields: fields.append((key, old.custom_fields.get(key, ''), self.custom_fields[key])) for title, o, n in fields: if o != n: changes.append('%s updated: %r => %r' % ( title, o, n)) o = hist.assigned_to n = self.assigned_to if o != n: changes.append('Owner updated: %r => %r' % ( o and o.username, n and n.username)) self.subscribe(user=n) g.statsUpdater.ticketEvent("assigned", self, self.project, n) if o: g.statsUpdater.ticketEvent("revoked", self, self.project, o) if old.description != self.description: changes.append('Description updated:') changes.append('\n'.join( difflib.unified_diff( a=old.description.split('\n'), b=self.description.split('\n'), fromfile='description-old', tofile='description-new'))) description = '\n'.join(changes) else: self.subscribe() if self.assigned_to_id: user = User.query.get(_id=self.assigned_to_id) g.statsUpdater.ticketEvent("assigned", self, self.project, user) self.subscribe(user=user) description = '' subject = self.email_subject Thread.new(discussion_id=self.app_config.discussion_id, ref_id=self.index_id()) n = Notification.post(artifact=self, topic='metadata', text=description, subject=subject) if monitoring_email and n and (not self.private or self.app.config.options.get('TicketMonitoringType') in ( 'NewTicketsOnly', 'AllTicketChanges')): n.send_simple(monitoring_email) Feed.post( self, title=self.summary, description=description if description else self.description, author=self.reported_by, pubdate=self.created_date)
def send_notifications(repo, commit_ids): '''Create appropriate notification and feed objects for a refresh''' from allura.model import Feed, Notification commit_msgs = [] base_url = tg.config.get('base_url', 'sourceforge.net') for oids in utils.chunked_iter(commit_ids, QSIZE): chunk = list(oids) index = dict( (doc._id, doc) for doc in Commit.query.find(dict(_id={'$in':chunk}))) for oid in chunk: ci = index[oid] href = repo.url_for_commit(oid) summary = _summarize(ci.message) Feed.post( repo, title='New commit', description='%s<br><a href="%s">View Changes</a>' % ( summary, href), author_link=ci.author_url, author_name=ci.authored.name) branches = repo.symbolics_for_commit(ci)[0] commit_msgs.append('%s: %s by %s %s%s' % ( ",".join(b for b in branches), summary, ci.authored.name, base_url, ci.url())) if commit_msgs: if len(commit_msgs) > 1: subject = '%d new commits to %s %s' % ( len(commit_msgs), repo.app.project.name, repo.app.config.options.mount_label) text='\n\n'.join(commit_msgs) else: subject = '%s committed to %s %s: %s' % ( ci.authored.name, repo.app.project.name, repo.app.config.options.mount_label, summary) branches = repo.symbolics_for_commit(ci)[0] text = "%s: %s %s%s" % (",".join(b for b in branches), ci.message, base_url, ci.url()) Notification.post( artifact=repo, topic='metadata', subject=subject, text=text)
def commit(self): VersionedArtifact.commit(self) monitoring_email = self.app.config.options.get('TicketMonitoringEmail') if self.version > 1: hist = TicketHistory.query.get(artifact_id=self._id, version=self.version - 1) old = hist.data changes = [ 'Ticket %s has been modified: %s' % (self.ticket_num, self.summary), 'Edited By: %s (%s)' % (c.user.get_pref('display_name'), c.user.username) ] fields = [('Summary', old.summary, self.summary), ('Status', old.status, self.status)] if old.status != self.status and self.status in c.app.globals.set_of_closed_status_names: h.log_action(log, 'closed').info('') for key in self.custom_fields: fields.append( (key, old.custom_fields.get(key, ''), self.custom_fields[key])) for title, o, n in fields: if o != n: changes.append('%s updated: %r => %r' % (title, o, n)) o = hist.assigned_to n = self.assigned_to if o != n: changes.append('Owner updated: %r => %r' % (o and o.username, n and n.username)) self.subscribe(user=n) if old.description != self.description: changes.append('Description updated:') changes.append('\n'.join( difflib.unified_diff(a=old.description.split('\n'), b=self.description.split('\n'), fromfile='description-old', tofile='description-new'))) description = '\n'.join(changes) else: self.subscribe() if self.assigned_to_id: self.subscribe(user=User.query.get(_id=self.assigned_to_id)) description = '' subject = self.email_subject Thread(discussion_id=self.app_config.discussion_id, ref_id=self.index_id()) n = Notification.post(artifact=self, topic='metadata', text=description, subject=subject) if monitoring_email and n: n.send_simple(monitoring_email) Feed.post(self, description)
def feed(self, since=None, until=None, page=None, limit=None): username = c.project.shortname.split('/')[1] user = User.by_username(username) if request.environ['PATH_INFO'].endswith('.atom'): feed_type = 'atom' else: feed_type = 'rss' title = 'Recent posts by %s' % user.display_name feed = Notification.feed( {'author_id':user._id}, feed_type, title, c.project.url(), title, since, until, page, limit) response.headers['Content-Type'] = '' response.content_type = 'application/xml' return feed.writeString('utf-8')
def update_tickets(self, **post_data): from forgetracker.tracker_main import get_change_text, get_label tickets = Ticket.query.find( dict(_id={ '$in': [ObjectId(id) for id in aslist(post_data['__ticket_ids'])] }, app_config_id=self.app_config_id)).all() fields = set(['status', 'private']) values = {} labels = post_data.get('labels', []) for k in fields: v = post_data.get(k) if v: values[k] = v assigned_to = post_data.get('assigned_to') if assigned_to == '-': values['assigned_to_id'] = None elif assigned_to: user = c.project.user_in_project(assigned_to) if user: values['assigned_to_id'] = user._id private = post_data.get('private') if private: values['private'] = asbool(private) custom_values = {} custom_fields = {} for cf in self.custom_fields or []: v = post_data.get(cf.name) if v: custom_values[cf.name] = v custom_fields[cf.name] = cf changes = {} changed_tickets = {} for ticket in tickets: message = '' if labels: values['labels'] = self.append_new_labels( ticket.labels, labels.split(',')) for k, v in sorted(values.iteritems()): if k == 'assigned_to_id': new_user = User.query.get(_id=v) old_user = User.query.get(_id=getattr(ticket, k)) if new_user: message += get_change_text(get_label(k), new_user.display_name, old_user.display_name) elif k == 'private': def private_text(val): if val: return 'Yes' else: return 'No' message += get_change_text( get_label(k), private_text(v), private_text(getattr(ticket, k))) else: message += get_change_text(get_label(k), v, getattr(ticket, k)) setattr(ticket, k, v) for k, v in sorted(custom_values.iteritems()): def cf_val(cf): return ticket.get_custom_user(cf.name) \ if cf.type == 'user' \ else ticket.custom_fields.get(cf.name) cf = custom_fields[k] old_value = cf_val(cf) if cf.type == 'boolean': v = asbool(v) ticket.custom_fields[k] = v new_value = cf_val(cf) message += get_change_text(cf.label, new_value, old_value) if message != '': changes[ticket._id] = message changed_tickets[ticket._id] = ticket ticket.discussion_thread.post(message, notify=False) ticket.commit() filtered_changes = self.filtered_by_subscription(changed_tickets) users = User.query.find({ '_id': { '$in': filtered_changes.keys() } }).all() def changes_iter(user): for t_id in filtered_changes.get(user._id, []): # mark changes text as safe, thus it wouldn't be escaped in plain-text emails # html part of email is handled by markdown and it'll be properly escaped yield (changed_tickets[t_id], jinja2.Markup(changes[t_id])) mail = dict( sender=c.project.app_instance(self.app_config).email_address, fromaddr=str(c.user._id), reply_to=str(c.user._id), subject='[%s:%s] Mass edit changes by %s' % (c.project.shortname, self.app_config.options.mount_point, c.user.display_name), ) tmpl = g.jinja2_env.get_template('forgetracker:data/mass_report.html') head = [] for f, v in sorted(values.iteritems()): if f == 'assigned_to_id': user = User.query.get(_id=v) v = user.display_name if user else v head.append('- **%s**: %s' % (get_label(f), v)) for f, v in sorted(custom_values.iteritems()): cf = custom_fields[f] if cf.type == 'user': user = User.by_username(v) v = user.display_name if user else v head.append('- **%s**: %s' % (cf.label, v)) tmpl_context = { 'context': c, 'data': { 'header': jinja2.Markup('\n'.join(['Mass edit changing:', ''] + head)) } } for user in users: tmpl_context['data'].update({'changes': changes_iter(user)}) mail.update( dict(message_id=h.gen_message_id(), text=tmpl.render(tmpl_context), destinations=[str(user._id)])) mail_tasks.sendmail.post(**mail) if self.app_config.options.get('TicketMonitoringType') in ( 'AllTicketChanges', 'AllPublicTicketChanges'): monitoring_email = self.app_config.options.get( 'TicketMonitoringEmail') visible_changes = [] for t_id, t in changed_tickets.items(): if (not t.private or self.app_config.options.get('TicketMonitoringType') == 'AllTicketChanges'): visible_changes.append( (changed_tickets[t_id], jinja2.Markup(changes[t_id]))) if visible_changes: tmpl_context['data'].update({'changes': visible_changes}) mail.update( dict(message_id=h.gen_message_id(), text=tmpl.render(tmpl_context), destinations=[monitoring_email])) mail_tasks.sendmail.post(**mail) self.invalidate_bin_counts() ThreadLocalORMSession.flush_all() app = '%s/%s' % (c.project.shortname, self.app_config.options.mount_point) count = len(tickets) text = 'Updated {} ticket{} in {}'.format(count, 's' if count != 1 else '', app) Notification.post_user(c.user, None, 'flash', text=text)
def send_notifications(repo, commit_ids): """Create appropriate notification and feed objects for a refresh :param repo: A repository artifact instance. :type repo: Repository :param commit_ids: A list of commit hash strings, oldest to newest :type commit_ids: list """ from allura.model import Feed, Notification commit_msgs = [] base_url = tg.config['base_url'] for oids in utils.chunked_iter(commit_ids, QSIZE): chunk = list(oids) index = dict((doc._id, doc) for doc in Commit.query.find(dict(_id={'$in': chunk}))) for oid in chunk: ci = index[oid] href = repo.url_for_commit(oid) title = _title(ci.message) summary = _summarize(ci.message) Feed.post(repo, title=title, description='%s<br><a href="%s">View Changes</a>' % (summary, href), author_link=ci.author_url, author_name=ci.authored.name, link=href, unique_id=href) summary = g.markdown_commit.convert( ci.message) if ci.message else "" current_branch = repo.symbolics_for_commit(ci)[ 0] # only the head of a branch will have this commit_msgs.append( dict(author=ci.authored.name, date=ci.authored.date.strftime("%m/%d/%Y %H:%M"), summary=summary, branches=current_branch, commit_url=base_url + href, shorthand_id=ci.shorthand_id())) # fill out the branch info for all the other commits prev_branch = None for c_msg in reversed(commit_msgs): if not c_msg['branches']: c_msg['branches'] = prev_branch prev_branch = c_msg['branches'] # mark which ones are first on a branch and need the branch name shown last_branch = None for c_msg in commit_msgs: if c_msg['branches'] != last_branch: c_msg['show_branch_name'] = True last_branch = c_msg['branches'] if commit_msgs: if len(commit_msgs) > 1: subject = u"{} new commits to {}".format( len(commit_msgs), repo.app.config.options.mount_label) else: commit = commit_msgs[0] subject = u'New commit {} by {}'.format(commit['shorthand_id'], commit['author']) text = g.jinja2_env.get_template( "allura:templates/mail/commits.md").render( commit_msgs=commit_msgs, max_num_commits=asint( tg.config.get('scm.notify.max_commits', 100)), ) Notification.post(artifact=repo, topic='metadata', subject=subject, text=text)
def update_tickets(self, **post_data): from forgetracker.tracker_main import get_change_text, get_label tickets = Ticket.query.find(dict( _id={'$in':[ObjectId(id) for id in aslist(post_data['__ticket_ids'])]}, app_config_id=self.app_config_id)).all() fields = set(['status', 'private']) values = {} labels = post_data.get('labels', []) for k in fields: v = post_data.get(k) if v: values[k] = v assigned_to = post_data.get('assigned_to') if assigned_to == '-': values['assigned_to_id'] = None elif assigned_to: user = c.project.user_in_project(assigned_to) if user: values['assigned_to_id'] = user._id private = post_data.get('private') if private: values['private'] = asbool(private) custom_values = {} custom_fields = {} for cf in self.custom_fields or []: v = post_data.get(cf.name) if v: custom_values[cf.name] = v custom_fields[cf.name] = cf changes = {} changed_tickets = {} for ticket in tickets: message = '' if labels: values['labels'] = self.append_new_labels(ticket.labels, labels.split(',')) for k, v in sorted(values.iteritems()): if k == 'assigned_to_id': new_user = User.query.get(_id=v) old_user = User.query.get(_id=getattr(ticket, k)) if new_user: message += get_change_text( get_label(k), new_user.display_name, old_user.display_name) elif k == 'private': def private_text(val): if val: return 'Yes' else: return 'No' message += get_change_text( get_label(k), private_text(v), private_text(getattr(ticket, k))) else: message += get_change_text( get_label(k), v, getattr(ticket, k)) setattr(ticket, k, v) for k, v in sorted(custom_values.iteritems()): def cf_val(cf): return ticket.get_custom_user(cf.name) \ if cf.type == 'user' \ else ticket.custom_fields.get(cf.name) cf = custom_fields[k] old_value = cf_val(cf) if cf.type == 'boolean': v = asbool(v) ticket.custom_fields[k] = v new_value = cf_val(cf) message += get_change_text( cf.label, new_value, old_value) if message != '': changes[ticket._id] = message changed_tickets[ticket._id] = ticket ticket.discussion_thread.post(message, notify=False) ticket.commit() filtered_changes = self.filtered_by_subscription(changed_tickets) users = User.query.find({'_id': {'$in': filtered_changes.keys()}}).all() def changes_iter(user): for t_id in filtered_changes.get(user._id, []): # mark changes text as safe, thus it wouldn't be escaped in plain-text emails # html part of email is handled by markdown and it'll be properly escaped yield (changed_tickets[t_id], jinja2.Markup(changes[t_id])) mail = dict( sender = c.project.app_instance(self.app_config).email_address, fromaddr = str(c.user._id), reply_to = tg_config['forgemail.return_path'], subject = '[%s:%s] Mass edit changes by %s' % (c.project.shortname, self.app_config.options.mount_point, c.user.display_name), ) tmpl = g.jinja2_env.get_template('forgetracker:data/mass_report.html') head = [] for f, v in sorted(values.iteritems()): if f == 'assigned_to_id': user = User.query.get(_id=v) v = user.display_name if user else v head.append('- **%s**: %s' % (get_label(f), v)) for f, v in sorted(custom_values.iteritems()): cf = custom_fields[f] if cf.type == 'user': user = User.by_username(v) v = user.display_name if user else v head.append('- **%s**: %s' % (cf.label, v)) tmpl_context = {'context': c, 'data': {'header': jinja2.Markup('\n'.join(['Mass edit changing:', ''] + head))}} for user in users: tmpl_context['data'].update({'changes': changes_iter(user)}) mail.update(dict( message_id = h.gen_message_id(), text = tmpl.render(tmpl_context), destinations = [str(user._id)])) mail_tasks.sendmail.post(**mail) if self.app_config.options.get('TicketMonitoringType') in ( 'AllTicketChanges', 'AllPublicTicketChanges'): monitoring_email = self.app_config.options.get('TicketMonitoringEmail') visible_changes = [] for t_id, t in changed_tickets.items(): if (not t.private or self.app_config.options.get('TicketMonitoringType') == 'AllTicketChanges'): visible_changes.append( (changed_tickets[t_id], jinja2.Markup(changes[t_id]))) if visible_changes: tmpl_context['data'].update({'changes': visible_changes}) mail.update(dict( message_id = h.gen_message_id(), text = tmpl.render(tmpl_context), destinations = [monitoring_email])) mail_tasks.sendmail.post(**mail) self.invalidate_bin_counts() ThreadLocalORMSession.flush_all() app = '%s/%s' % (c.project.shortname, self.app_config.options.mount_point) count = len(tickets) text = 'Updated {} ticket{} in {}'.format(count, 's' if count != 1 else '', app) Notification.post_user(c.user, None, 'flash', text=text)
def move_tickets(self, ticket_ids, destination_tracker_id): tracker = AppConfig.query.get(_id=destination_tracker_id) tickets = Ticket.query.find(dict( _id={'$in': [ObjectId(id) for id in ticket_ids]}, app_config_id=self.app_config_id)).sort('ticket_num').all() filtered = self.filtered_by_subscription({t._id: t for t in tickets}) original_ticket_nums = {t._id: t.ticket_num for t in tickets} users = User.query.find({'_id': {'$in': filtered.keys()}}).all() moved_tickets = {} for ticket in tickets: moved = ticket.move(tracker, notify=False) moved_tickets[moved._id] = moved mail = dict( sender = c.project.app_instance(self.app_config).email_address, fromaddr = str(c.user.email_address_header()), reply_to = str(c.user.email_address_header()), subject = '[%s:%s] Mass ticket moving by %s' % (c.project.shortname, self.app_config.options.mount_point, c.user.display_name)) tmpl = g.jinja2_env.get_template('forgetracker:data/mass_move_report.html') tmpl_context = { 'original_tracker': '%s:%s' % (c.project.shortname, self.app_config.options.mount_point), 'destination_tracker': '%s:%s' % (tracker.project.shortname, tracker.options.mount_point), 'tickets': [], } for user in users: tmpl_context['tickets'] = ({ 'original_num': original_ticket_nums[_id], 'destination_num': moved_tickets[_id].ticket_num, 'summary': moved_tickets[_id].summary } for _id in filtered.get(user._id, [])) mail.update(dict( message_id = h.gen_message_id(), text = tmpl.render(tmpl_context), destinations = [str(user._id)])) mail_tasks.sendmail.post(**mail) if self.app_config.options.get('TicketMonitoringType') in ( 'AllTicketChanges', 'AllPublicTicketChanges'): monitoring_email = self.app_config.options.get('TicketMonitoringEmail') tmpl_context['tickets'] = [{ 'original_num': original_ticket_nums[_id], 'destination_num': moved_tickets[_id].ticket_num, 'summary': moved_tickets[_id].summary } for _id, t in moved_tickets.iteritems() if (not t.private or self.app_config.options.get('TicketMonitoringType') == 'AllTicketChanges')] if len(tmpl_context['tickets']) > 0: mail.update(dict( message_id = h.gen_message_id(), text = tmpl.render(tmpl_context), destinations = [monitoring_email])) mail_tasks.sendmail.post(**mail) moved_from = '%s/%s' % (c.project.shortname, self.app_config.options.mount_point) moved_to = '%s/%s' % (tracker.project.shortname, tracker.options.mount_point) text = 'Tickets moved from %s to %s' % (moved_from, moved_to) Notification.post_user(c.user, None, 'flash', text=text)
def send_notifications(repo, commit_ids): """Create appropriate notification and feed objects for a refresh :param repo: A repository artifact instance. :type repo: Repository :param commit_ids: A list of commit hash strings, oldest to newest :type commit_ids: list """ from allura.model import Feed, Notification commit_msgs = [] base_url = tg.config['base_url'] for oids in utils.chunked_iter(commit_ids, QSIZE): chunk = list(oids) index = dict( (doc._id, doc) for doc in Commit.query.find(dict(_id={'$in': chunk}))) for oid in chunk: ci = index[oid] href = repo.url_for_commit(oid) title = _title(ci.message) summary = _summarize(ci.message) Feed.post( repo, title=title, description='%s<br><a href="%s">View Changes</a>' % ( summary, href), author_link=ci.author_url, author_name=ci.authored.name, link=href, unique_id=href) summary = g.markdown_commit.convert(ci.message) if ci.message else "" current_branch = repo.symbolics_for_commit(ci)[0] # only the head of a branch will have this commit_msgs.append(dict( author=ci.authored.name, date=ci.authored.date.strftime("%m/%d/%Y %H:%M"), summary=summary, branches=current_branch, commit_url=base_url + href, shorthand_id=ci.shorthand_id())) # fill out the branch info for all the other commits prev_branch = None for c_msg in reversed(commit_msgs): if not c_msg['branches']: c_msg['branches'] = prev_branch prev_branch = c_msg['branches'] # mark which ones are first on a branch and need the branch name shown last_branch = None for c_msg in commit_msgs: if c_msg['branches'] != last_branch: c_msg['show_branch_name'] = True last_branch = c_msg['branches'] if commit_msgs: if len(commit_msgs) > 1: subject = u"{} new commits to {}".format(len(commit_msgs), repo.app.config.options.mount_label) else: commit = commit_msgs[0] subject = u'New commit {} by {}'.format(commit['shorthand_id'], commit['author']) text = g.jinja2_env.get_template("allura:templates/mail/commits.md").render( commit_msgs=commit_msgs, max_num_commits=asint(tg.config.get('scm.notify.max_commits', 100)), ) Notification.post( artifact=repo, topic='metadata', subject=subject, text=text)
def send_notifications(repo, commit_ids): """Create appropriate notification and feed objects for a refresh :param repo: A repository artifact instance. :type repo: Repository :param commit_ids: A list of commit hash strings. :type commit_ids: list """ from allura.model import Feed, Notification commit_msgs = [] base_url = tg.config['base_url'] last_branch = [] for oids in utils.chunked_iter(commit_ids, QSIZE): chunk = list(oids) index = dict( (doc._id, doc) for doc in Commit.query.find(dict(_id={'$in': chunk}))) for oid in chunk: ci = index[oid] href = repo.url_for_commit(oid) title = _title(ci.message) summary = _summarize(ci.message) Feed.post( repo, title=title, description='%s<br><a href="%s">View Changes</a>' % ( summary, href), author_link=ci.author_url, author_name=ci.authored.name, link=href, unique_id=href) summary = g.markdown_commit.convert(ci.message) if ci.message else "" current_branch = repo.symbolics_for_commit(ci)[0] if last_branch == current_branch: branches = [] else: branches = current_branch last_branch = branches commit_msgs.append(dict( author=ci.authored.name, date=ci.authored.date.strftime("%m/%d/%Y %H:%M"), summary=summary, branches=branches, commit_url=base_url + href)) if commit_msgs: if len(commit_msgs) > 1: subject = u"{} new commits to {}".format(len(commit_msgs), repo.app.config.options.mount_label) else: subject = u'New commit by {}'.format(commit_msgs[0]['author']) template = g.jinja2_env.get_template("allura:templates/mail/commits.md") text = u"\n\n-----".join([template.render(d) for d in commit_msgs]) Notification.post( artifact=repo, topic='metadata', subject=subject, text=text)
def commit(self): VersionedArtifact.commit(self) monitoring_email = self.app.config.options.get('TicketMonitoringEmail') if self.version > 1: hist = TicketHistory.query.get(artifact_id=self._id, version=self.version - 1) old = hist.data changes = [ 'Ticket %s has been modified: %s' % (self.ticket_num, self.summary), 'Edited By: %s (%s)' % (c.user.get_pref('display_name'), c.user.username) ] fields = [('Summary', old.summary, self.summary), ('Status', old.status, self.status)] if old.status != self.status and self.status in c.app.globals.set_of_closed_status_names: h.log_action(log, 'closed').info('') g.statsUpdater.ticketEvent("closed", self, self.project, self.assigned_to) for key in self.custom_fields: fields.append( (key, old.custom_fields.get(key, ''), self.custom_fields[key])) for title, o, n in fields: if o != n: changes.append('%s updated: %r => %r' % (title, o, n)) o = hist.assigned_to n = self.assigned_to if o != n: changes.append('Owner updated: %r => %r' % (o and o.username, n and n.username)) self.subscribe(user=n) g.statsUpdater.ticketEvent("assigned", self, self.project, n) if o: g.statsUpdater.ticketEvent("revoked", self, self.project, o) if old.description != self.description: changes.append('Description updated:') changes.append('\n'.join( difflib.unified_diff(a=old.description.split('\n'), b=self.description.split('\n'), fromfile='description-old', tofile='description-new'))) description = '\n'.join(changes) else: self.subscribe() if self.assigned_to_id: user = User.query.get(_id=self.assigned_to_id) g.statsUpdater.ticketEvent("assigned", self, self.project, user) self.subscribe(user=user) description = '' subject = self.email_subject Thread.new(discussion_id=self.app_config.discussion_id, ref_id=self.index_id()) # First ticket notification. Use persistend Message-ID (self.message_id()). # Thus we can group notification emails in one thread later. n = Notification.post(message_id=self.message_id(), artifact=self, topic='metadata', text=description, subject=subject) if monitoring_email and n and ( not self.private or self.app.config.options.get('TicketMonitoringType') in ('NewTicketsOnly', 'AllTicketChanges')): n.send_simple(monitoring_email) Feed.post(self, title=self.summary, description=description if description else self.description, author=self.reported_by, pubdate=self.created_date)
def move_tickets(self, ticket_ids, destination_tracker_id): tracker = AppConfig.query.get(_id=destination_tracker_id) tickets = Ticket.query.find( dict(_id={'$in': [ObjectId(id) for id in ticket_ids]}, app_config_id=self.app_config_id)).sort('ticket_num').all() filtered = self.filtered_by_subscription({t._id: t for t in tickets}) original_ticket_nums = {t._id: t.ticket_num for t in tickets} users = User.query.find({'_id': {'$in': filtered.keys()}}).all() moved_tickets = {} for ticket in tickets: moved = ticket.move(tracker, notify=False) moved_tickets[moved._id] = moved mail = dict(sender=c.project.app_instance( self.app_config).email_address, fromaddr=str(c.user.email_address_header()), reply_to=str(c.user.email_address_header()), subject='[%s:%s] Mass ticket moving by %s' % (c.project.shortname, self.app_config.options.mount_point, c.user.display_name)) tmpl = g.jinja2_env.get_template( 'forgetracker:data/mass_move_report.html') tmpl_context = { 'original_tracker': '%s:%s' % (c.project.shortname, self.app_config.options.mount_point), 'destination_tracker': '%s:%s' % (tracker.project.shortname, tracker.options.mount_point), 'tickets': [], } for user in users: tmpl_context['tickets'] = ({ 'original_num': original_ticket_nums[_id], 'destination_num': moved_tickets[_id].ticket_num, 'summary': moved_tickets[_id].summary } for _id in filtered.get(user._id, [])) mail.update( dict(message_id=h.gen_message_id(), text=tmpl.render(tmpl_context), destinations=[str(user._id)])) mail_tasks.sendmail.post(**mail) if self.app_config.options.get('TicketMonitoringType') in ( 'AllTicketChanges', 'AllPublicTicketChanges'): monitoring_email = self.app_config.options.get( 'TicketMonitoringEmail') tmpl_context['tickets'] = [ { 'original_num': original_ticket_nums[_id], 'destination_num': moved_tickets[_id].ticket_num, 'summary': moved_tickets[_id].summary } for _id, t in moved_tickets.iteritems() if (not t.private or self.app_config.options.get( 'TicketMonitoringType') == 'AllTicketChanges') ] if len(tmpl_context['tickets']) > 0: mail.update( dict(message_id=h.gen_message_id(), text=tmpl.render(tmpl_context), destinations=[monitoring_email])) mail_tasks.sendmail.post(**mail) moved_from = '%s/%s' % (c.project.shortname, self.app_config.options.mount_point) moved_to = '%s/%s' % (tracker.project.shortname, tracker.options.mount_point) text = 'Tickets moved from %s to %s' % (moved_from, moved_to) Notification.post_user(c.user, None, 'flash', text=text)
def move_tickets(self, ticket_ids, destination_tracker_id): tracker = AppConfig.query.get(_id=destination_tracker_id) tickets = ( Ticket.query.find(dict(_id={"$in": [ObjectId(id) for id in ticket_ids]}, app_config_id=self.app_config_id)) .sort("ticket_num") .all() ) filtered = self.filtered_by_subscription({t._id: t for t in tickets}) original_ticket_nums = {t._id: t.ticket_num for t in tickets} users = User.query.find({"_id": {"$in": filtered.keys()}}).all() moved_tickets = {} for ticket in tickets: moved = ticket.move(tracker, notify=False) moved_tickets[moved._id] = moved mail = dict( sender=c.project.app_instance(self.app_config).email_address, fromaddr=str(c.user.email_address_header()), reply_to=str(c.user.email_address_header()), subject="[%s:%s] Mass ticket moving by %s" % (c.project.shortname, self.app_config.options.mount_point, c.user.display_name), ) tmpl = g.jinja2_env.get_template("forgetracker:data/mass_move_report.html") tmpl_context = { "original_tracker": "%s:%s" % (c.project.shortname, self.app_config.options.mount_point), "destination_tracker": "%s:%s" % (tracker.project.shortname, tracker.options.mount_point), "tickets": [], } for user in users: tmpl_context["tickets"] = ( { "original_num": original_ticket_nums[_id], "destination_num": moved_tickets[_id].ticket_num, "summary": moved_tickets[_id].summary, } for _id in filtered.get(user._id, []) ) mail.update( dict(message_id=h.gen_message_id(), text=tmpl.render(tmpl_context), destinations=[str(user._id)]) ) mail_tasks.sendmail.post(**mail) if self.app_config.options.get("TicketMonitoringType") in ("AllTicketChanges", "AllPublicTicketChanges"): monitoring_email = self.app_config.options.get("TicketMonitoringEmail") tmpl_context["tickets"] = [ { "original_num": original_ticket_nums[_id], "destination_num": moved_tickets[_id].ticket_num, "summary": moved_tickets[_id].summary, } for _id, t in moved_tickets.iteritems() if (not t.private or self.app_config.options.get("TicketMonitoringType") == "AllTicketChanges") ] if len(tmpl_context["tickets"]) > 0: mail.update( dict(message_id=h.gen_message_id(), text=tmpl.render(tmpl_context), destinations=[monitoring_email]) ) mail_tasks.sendmail.post(**mail) moved_from = "%s/%s" % (c.project.shortname, self.app_config.options.mount_point) moved_to = "%s/%s" % (tracker.project.shortname, tracker.options.mount_point) text = "Tickets moved from %s to %s" % (moved_from, moved_to) Notification.post_user(c.user, None, "flash", text=text)
def commit(self, **kwargs): VersionedArtifact.commit(self) monitoring_email = self.app.config.options.get("TicketMonitoringEmail") if self.version > 1: hist = TicketHistory.query.get(artifact_id=self._id, version=self.version - 1) old = hist.data changes = [ "Ticket %s has been modified: %s" % (self.ticket_num, self.summary), "Edited By: %s (%s)" % (c.user.get_pref("display_name"), c.user.username), ] fields = [("Summary", old.summary, self.summary), ("Status", old.status, self.status)] if old.status != self.status and self.status in c.app.globals.set_of_closed_status_names: h.log_action(log, "closed").info("") g.statsUpdater.ticketEvent("closed", self, self.project, self.assigned_to) for key in self.custom_fields: fields.append((key, old.custom_fields.get(key, ""), self.custom_fields[key])) for title, o, n in fields: if o != n: changes.append("%s updated: %r => %r" % (title, o, n)) o = hist.assigned_to n = self.assigned_to if o != n: changes.append("Owner updated: %r => %r" % (o and o.username, n and n.username)) self.subscribe(user=n) g.statsUpdater.ticketEvent("assigned", self, self.project, n) if o: g.statsUpdater.ticketEvent("revoked", self, self.project, o) if old.description != self.description: changes.append("Description updated:") changes.append( "\n".join( difflib.unified_diff( a=old.description.split("\n"), b=self.description.split("\n"), fromfile="description-old", tofile="description-new", ) ) ) description = "\n".join(changes) else: self.subscribe() if self.assigned_to_id: user = User.query.get(_id=self.assigned_to_id) g.statsUpdater.ticketEvent("assigned", self, self.project, user) self.subscribe(user=user) description = "" subject = self.email_subject Thread.new(discussion_id=self.app_config.discussion_id, ref_id=self.index_id()) # First ticket notification. Use persistend Message-ID (self.message_id()). # Thus we can group notification emails in one thread later. n = Notification.post( message_id=self.message_id(), artifact=self, topic="metadata", text=description, subject=subject ) if ( monitoring_email and n and ( not self.private or self.app.config.options.get("TicketMonitoringType") in ("NewTicketsOnly", "AllTicketChanges") ) ): n.send_simple(monitoring_email) Feed.post( self, title=self.summary, description=description if description else self.description, author=self.reported_by, pubdate=self.created_date, )
def update_tickets(self, **post_data): from forgetracker.tracker_main import get_change_text, get_label tickets = Ticket.query.find( dict( _id={"$in": [ObjectId(id) for id in aslist(post_data["__ticket_ids"])]}, app_config_id=self.app_config_id, ) ).all() fields = set(["status", "private"]) values = {} labels = post_data.get("labels", []) for k in fields: v = post_data.get(k) if v: values[k] = v assigned_to = post_data.get("assigned_to") if assigned_to == "-": values["assigned_to_id"] = None elif assigned_to: user = c.project.user_in_project(assigned_to) if user: values["assigned_to_id"] = user._id private = post_data.get("private") if private: values["private"] = asbool(private) discussion_disabled = post_data.get("discussion_disabled") if discussion_disabled: values["disabled_discussion"] = asbool(discussion_disabled) custom_values = {} custom_fields = {} for cf in self.custom_fields or []: v = post_data.get(cf.name) if v: custom_values[cf.name] = v custom_fields[cf.name] = cf changes = {} changed_tickets = {} for ticket in tickets: message = "" if labels: values["labels"] = self.append_new_labels(ticket.labels, labels.split(",")) for k, v in sorted(values.iteritems()): if k == "assigned_to_id": new_user = User.query.get(_id=v) old_user = User.query.get(_id=getattr(ticket, k)) if new_user: message += get_change_text(get_label(k), new_user.display_name, old_user.display_name) elif k == "private" or k == "discussion_disabled": def _text(val): if val: return "Yes" else: return "No" message += get_change_text(get_label(k), _text(v), _text(getattr(ticket, k))) else: message += get_change_text(get_label(k), v, getattr(ticket, k)) setattr(ticket, k, v) for k, v in sorted(custom_values.iteritems()): def cf_val(cf): return ticket.get_custom_user(cf.name) if cf.type == "user" else ticket.custom_fields.get(cf.name) cf = custom_fields[k] old_value = cf_val(cf) if cf.type == "boolean": v = asbool(v) ticket.custom_fields[k] = v new_value = cf_val(cf) message += get_change_text(cf.label, new_value, old_value) if message != "": changes[ticket._id] = message changed_tickets[ticket._id] = ticket ticket.discussion_thread.post(message, notify=False) ticket.commit() filtered_changes = self.filtered_by_subscription(changed_tickets) users = User.query.find({"_id": {"$in": filtered_changes.keys()}}).all() def changes_iter(user): for t_id in filtered_changes.get(user._id, []): # mark changes text as safe, thus it wouldn't be escaped in plain-text emails # html part of email is handled by markdown and it'll be # properly escaped yield (changed_tickets[t_id], jinja2.Markup(changes[t_id])) mail = dict( sender=c.project.app_instance(self.app_config).email_address, fromaddr=str(c.user._id), reply_to=tg_config["forgemail.return_path"], subject="[%s:%s] Mass edit changes by %s" % (c.project.shortname, self.app_config.options.mount_point, c.user.display_name), ) tmpl = g.jinja2_env.get_template("forgetracker:data/mass_report.html") head = [] for f, v in sorted(values.iteritems()): if f == "assigned_to_id": user = User.query.get(_id=v) v = user.display_name if user else v head.append("- **%s**: %s" % (get_label(f), v)) for f, v in sorted(custom_values.iteritems()): cf = custom_fields[f] if cf.type == "user": user = User.by_username(v) v = user.display_name if user else v head.append("- **%s**: %s" % (cf.label, v)) tmpl_context = {"context": c, "data": {"header": jinja2.Markup("\n".join(["Mass edit changing:", ""] + head))}} for user in users: tmpl_context["data"].update({"changes": changes_iter(user)}) mail.update( dict(message_id=h.gen_message_id(), text=tmpl.render(tmpl_context), destinations=[str(user._id)]) ) mail_tasks.sendmail.post(**mail) if self.app_config.options.get("TicketMonitoringType") in ("AllTicketChanges", "AllPublicTicketChanges"): monitoring_email = self.app_config.options.get("TicketMonitoringEmail") visible_changes = [] for t_id, t in changed_tickets.items(): if not t.private or self.app_config.options.get("TicketMonitoringType") == "AllTicketChanges": visible_changes.append((changed_tickets[t_id], jinja2.Markup(changes[t_id]))) if visible_changes: tmpl_context["data"].update({"changes": visible_changes}) mail.update( dict(message_id=h.gen_message_id(), text=tmpl.render(tmpl_context), destinations=[monitoring_email]) ) mail_tasks.sendmail.post(**mail) self.invalidate_bin_counts() ThreadLocalORMSession.flush_all() app = "%s/%s" % (c.project.shortname, self.app_config.options.mount_point) count = len(tickets) text = "Updated {} ticket{} in {}".format(count, "s" if count != 1 else "", app) Notification.post_user(c.user, None, "flash", text=text)