Beispiel #1
0
 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
Beispiel #2
0
 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
Beispiel #3
0
 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)
Beispiel #5
0
 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')
Beispiel #9
0
    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)
Beispiel #10
0
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)
Beispiel #11
0
    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)
Beispiel #12
0
    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)
Beispiel #13
0
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)
Beispiel #14
0
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)
Beispiel #15
0
 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)
Beispiel #16
0
    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)
Beispiel #17
0
    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)
Beispiel #18
0
 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,
     )
Beispiel #19
0
    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)