Example #1
0
 def add_matches(doc):
     m = matches.get(doc['id'], {})
     title = h.get_first(m, 'title')
     text = h.get_first(m, 'text')
     if title:
         title = (jinja2.escape(title)
                        .replace('#ALLURA-HIGHLIGHT-START#', jinja2.Markup('<strong>'))
                        .replace('#ALLURA-HIGHLIGHT-END#', jinja2.Markup('</strong>')))
     if text:
         text = (jinja2.escape(text)
                       .replace('#ALLURA-HIGHLIGHT-START#', jinja2.Markup('<strong>'))
                       .replace('#ALLURA-HIGHLIGHT-END#', jinja2.Markup('</strong>')))
     doc['title_match'] = title
     doc['text_match'] = text or h.get_first(doc, 'text')
     return doc
Example #2
0
 def add_matches(doc):
     m = matches.get(doc['id'], {})
     title = h.get_first(m, 'title')
     text = h.get_first(m, 'text')
     if title:
         title = (jinja2.escape(title)
                        .replace('#ALLURA-HIGHLIGHT-START#', jinja2.Markup('<strong>'))
                        .replace('#ALLURA-HIGHLIGHT-END#', jinja2.Markup('</strong>')))
     if text:
         text = (jinja2.escape(text)
                       .replace('#ALLURA-HIGHLIGHT-START#', jinja2.Markup('<strong>'))
                       .replace('#ALLURA-HIGHLIGHT-END#', jinja2.Markup('</strong>')))
     doc['title_match'] = title
     doc['text_match'] = text or h.get_first(doc, 'text')
     return doc
Example #3
0
def test_post_methods():
    d = M.Discussion(shortname='test', name='test')
    t = M.Thread.new(discussion_id=d._id, subject='Test Thread')
    p = t.post('This is a post')
    p2 = t.post('This is another post')
    assert p.discussion_class() == M.Discussion
    assert p.thread_class() == M.Thread
    assert p.attachment_class() == M.DiscussionAttachment
    p.commit()
    assert p.parent is None
    assert p.subject == 'Test Thread'
    assert_equals(p.attachments, [])
    assert 'wiki/_discuss' in p.url()
    assert p.reply_subject() == 'Re: Test Thread'
    assert p.link_text() == p.subject

    ss = p.history().first()
    assert 'version' in h.get_first(ss.index(), 'title')
    assert '#' in ss.shorthand_id()

    jsn = p.__json__()
    assert jsn["thread_id"] == t._id

    (p.approve() for p in (p, p2))
    ThreadLocalORMSession.flush_all()
    assert t.num_replies == 2
    p.spam()
    assert t.num_replies == 1
    p.undo('ok')
    assert t.num_replies == 2
    p.delete()
    assert t.num_replies == 1
def test_post_methods():
    d = M.Discussion(shortname="test", name="test")
    t = M.Thread.new(discussion_id=d._id, subject="Test Thread")
    p = t.post("This is a post")
    p2 = t.post("This is another post")
    assert p.discussion_class() == M.Discussion
    assert p.thread_class() == M.Thread
    assert p.attachment_class() == M.DiscussionAttachment
    p.commit()
    assert p.parent is None
    assert p.subject == "Test Thread"
    assert p.attachments.count() == 0
    assert "wiki/_discuss" in p.url()
    assert p.reply_subject() == "Re: Test Thread"
    assert p.link_text() == p.subject

    ss = p.history().first()
    assert "version" in h.get_first(ss.index(), "title")
    assert "#" in ss.shorthand_id()

    jsn = p.__json__()
    assert jsn["thread_id"] == t._id

    (p.approve() for p in (p, p2))
    assert t.num_replies == 1
    p2.spam()
    assert t.num_replies == 0
    p.spam()
    assert t.num_replies == 0
    p.delete()
    assert t.num_replies == 0
Example #5
0
 def handle_shortlink(self, lnk, sender, rcpt):
     art = lnk.ref.artifact
     if security.has_access(art, 'read', user=M.User.anonymous())():
         index = art.index()
         text = index['snippet_s'] or h.get_first(index, 'title')
         url = urljoin(tg.config['base_url'], index['url_s'])
         self.notice(rcpt, '[%s] - [%s](%s)' % (lnk.link, text, url))
Example #6
0
 def handle_shortlink(self, lnk, sender, rcpt):
     art = lnk.ref.artifact
     if security.has_access(art, 'read', user=M.User.anonymous())():
         index = art.index()
         text = index['snippet_s'] or h.get_first(index, 'title')
         url = urljoin(tg.config.get('base_url', 'http://sourceforge.net'), index['url_s'])
         self.notice(rcpt, '[%s] - [%s](%s)' % (lnk.link, text,url))
Example #7
0
 def subscribe(
         cls,
         user_id=None, project_id=None, app_config_id=None,
         artifact=None, topic=None,
         type='direct', n=1, unit='day'):
     if user_id is None:
         user_id = c.user._id
     if project_id is None:
         project_id = c.project._id
     if app_config_id is None:
         app_config_id = c.app.config._id
     tool_already_subscribed = cls.query.get(user_id=user_id,
                                             project_id=project_id,
                                             app_config_id=app_config_id,
                                             artifact_index_id=None)
     if tool_already_subscribed:
         return
     if artifact is None:
         artifact_title = 'All artifacts'
         artifact_url = None
         artifact_index_id = None
     else:
         i = artifact.index()
         artifact_title = h.get_first(i, 'title')
         artifact_url = artifact.url()
         artifact_index_id = i['id']
         artifact_already_subscribed = cls.query.get(user_id=user_id,
                                                     project_id=project_id,
                                                     app_config_id=app_config_id,
                                                     artifact_index_id=artifact_index_id)
         if artifact_already_subscribed:
             return
     d = dict(
         user_id=user_id, project_id=project_id, app_config_id=app_config_id,
         artifact_index_id=artifact_index_id, topic=topic)
     sess = session(cls)
     try:
         mbox = cls(
             type=type, frequency=dict(n=n, unit=unit),
             artifact_title=artifact_title,
             artifact_url=artifact_url,
             **d)
         sess.flush(mbox)
     except pymongo.errors.DuplicateKeyError:
         sess.expunge(mbox)
         mbox = cls.query.get(**d)
         mbox.artifact_title = artifact_title
         mbox.artifact_url = artifact_url
         mbox.type = type
         mbox.frequency.n = n
         mbox.frequency.unit = unit
         sess.flush(mbox)
     if not artifact_index_id:
         # Unsubscribe from individual artifacts when subscribing to the
         # tool
         for other_mbox in cls.query.find(dict(
                 user_id=user_id, project_id=project_id, app_config_id=app_config_id)):
             if other_mbox != mbox:
                 other_mbox.delete()
     return mbox
Example #8
0
def test_post_methods():
    d = M.Discussion(shortname='test', name='test')
    t = M.Thread.new(discussion_id=d._id, subject='Test Thread')
    p = t.post('This is a post')
    p2 = t.post('This is another post')
    assert p.discussion_class() == M.Discussion
    assert p.thread_class() == M.Thread
    assert p.attachment_class() == M.DiscussionAttachment
    p.commit()
    assert p.parent is None
    assert p.subject == 'Test Thread'
    assert p.attachments.count() == 0
    assert 'wiki/_discuss' in p.url()
    assert p.reply_subject() == 'Re: Test Thread'
    assert p.link_text() == p.subject

    ss = p.history().first()
    assert 'version' in h.get_first(ss.index(), 'title')
    assert '#' in ss.shorthand_id()

    jsn = p.__json__()
    assert jsn["thread_id"] == t._id

    (p.approve() for p in (p, p2))
    assert t.num_replies == 1
    p2.spam()
    assert t.num_replies == 0
    p.spam()
    assert t.num_replies == 0
    p.delete()
    assert t.num_replies == 0
def test_get_first():
    assert_equals(h.get_first({}, "title"), None)
    assert_equals(h.get_first({"title": None}, "title"), None)
    assert_equals(h.get_first({"title": "Value"}, "title"), "Value")
    assert_equals(h.get_first({"title": ["Value"]}, "title"), "Value")
    assert_equals(h.get_first({"title": []}, "title"), None)
    assert_equals(h.get_first({"title": ["Value"]}, "title"), "Value")
Example #10
0
def test_get_first():
    assert_equals(h.get_first({}, 'title'), None)
    assert_equals(h.get_first({'title': None}, 'title'), None)
    assert_equals(h.get_first({'title': 'Value'}, 'title'), 'Value')
    assert_equals(h.get_first({'title': ['Value']}, 'title'), 'Value')
    assert_equals(h.get_first({'title': []}, 'title'), None)
    assert_equals(h.get_first({'title': ['Value']}, 'title'), 'Value')
Example #11
0
 def post(cls,
          artifact,
          title=None,
          description=None,
          author=None,
          author_link=None,
          author_name=None,
          pubdate=None,
          link=None,
          **kw):
     """
     Create a Feed item.  Returns the item.
     But if anon doesn't have read access, create does not happen and None is returned
     """
     # TODO: fix security system so we can do this correctly and fast
     from allura import model as M
     anon = M.User.anonymous()
     if not security.has_access(artifact, 'read', user=anon):
         return
     if not security.has_access(c.project, 'read', user=anon):
         return
     idx = artifact.index()
     if author is None:
         author = c.user
     if author_name is None:
         author_name = author.get_pref('display_name')
     if title is None:
         title = '%s modified by %s' % (h.get_first(idx,
                                                    'title'), author_name)
     if description is None:
         description = title
     if pubdate is None:
         pubdate = datetime.utcnow()
     if link is None:
         link = artifact.url()
     item = cls(ref_id=artifact.index_id(),
                neighborhood_id=artifact.app_config.project.neighborhood_id,
                project_id=artifact.app_config.project_id,
                app_config_id=artifact.app_config_id,
                tool_name=artifact.app_config.tool_name,
                title=title,
                description=g.markdown.convert(description),
                link=link,
                pubdate=pubdate,
                author_name=author_name,
                author_link=author_link or author.url())
     unique_id = kw.pop('unique_id', None)
     if unique_id:
         item.unique_id = unique_id
     return item
Example #12
0
 def index(self):
     result = Artifact.index(self)
     original = self.original()
     if original:
         original_index = original.index()
         result.update(original_index)
         result['title'] = '%s (version %d)' % (h.get_first(
             original_index, 'title'), self.version)
     result.update(id=self.index_id(),
                   version_i=self.version,
                   author_username_t=self.author.username,
                   author_display_name_t=self.author.display_name,
                   timestamp_dt=self.timestamp,
                   is_history_b=True)
     return result
Example #13
0
 def index(self):
     result = Artifact.index(self)
     original = self.original()
     if original:
         original_index = original.index()
         result.update(original_index)
         result['title'] = '%s (version %d)' % (
                 h.get_first(original_index, 'title'), self.version)
     result.update(
         id=self.index_id(),
         version_i=self.version,
         author_username_t=self.author.username,
         author_display_name_t=self.author.display_name,
         timestamp_dt=self.timestamp,
         is_history_b=True)
     return result
Example #14
0
    def get_discussion_thread(self, data=None):
        """Return the discussion thread and parent_id for this artifact.

        :return: (:class:`allura.model.discuss.Thread`, parent_thread_id (int))

        """
        from .discuss import Thread
        threads = Thread.query.find(dict(ref_id=self.index_id())).all()
        if not threads:
            idx = self.index()
            t = Thread.new(app_config_id=self.app_config_id,
                           discussion_id=self.app_config.discussion_id,
                           ref_id=idx['id'],
                           subject='%s discussion' % h.get_first(idx, 'title'))
        elif len(threads) == 1:
            t = threads[0]
        else:
            # there should not be multiple threads, we'll merge them
            destination = threads.pop()
            for thread in threads:
                for post in thread.posts:
                    post.thread_id = destination._id
                    destination.num_replies += 1
                    destination.last_post_date = max(
                        destination.last_post_date, post.mod_date)
                    session(post).flush(post)
                    session(post).expunge(
                        post
                    )  # so thread.posts ref later in the code doesn't use stale posts
                Thread.query.remove({
                    '_id': thread._id
                })  # NOT thread.delete() since that would remove its posts too
                thread.attachment_class().query.update(
                    {'thread_id': thread._id},
                    {'$set': {
                        'thread_id': destination._id
                    }},
                    multi=True)
            t = destination

        parent_id = None
        if data:
            in_reply_to = data.get('in_reply_to', [])
            if in_reply_to:
                parent_id = in_reply_to[0]

        return t, parent_id
Example #15
0
 def get_discussion_thread(self, data=None):
     '''Return the discussion thread for this artifact (possibly made more
     specific by the message_data)'''
     from .discuss import Thread
     t = Thread.query.get(ref_id=self.index_id())
     if t is None:
         idx = self.index()
         t = Thread.new(
             discussion_id=self.app_config.discussion_id,
             ref_id=idx['id'],
             subject='%s discussion' % h.get_first(idx, 'title'))
     parent_id = None
     if data:
         in_reply_to = data.get('in_reply_to', [])
         if in_reply_to:
             parent_id = in_reply_to[0]
     return t, parent_id
Example #16
0
 def post(cls,
          artifact,
          title=None,
          description=None,
          author=None,
          author_link=None,
          author_name=None,
          pubdate=None,
          link=None,
          **kw):
     """
     Create a Feed item.  Returns the item.
     But if anon doesn't have read access, create does not happen and None is
     returned.
     """
     if not Feed.has_access(artifact):
         return
     idx = artifact.index()
     if author is None:
         author = c.user
     if author_name is None:
         author_name = author.get_pref('display_name')
     if title is None:
         title = '%s modified by %s' % (h.get_first(idx,
                                                    'title'), author_name)
     if description is None:
         description = title
     if pubdate is None:
         pubdate = datetime.utcnow()
     if link is None:
         link = artifact.url()
     item = cls(ref_id=artifact.index_id(),
                neighborhood_id=artifact.app_config.project.neighborhood_id,
                project_id=artifact.app_config.project_id,
                app_config_id=artifact.app_config_id,
                tool_name=artifact.app_config.tool_name,
                title=title,
                description=g.markdown.convert(description),
                link=link,
                pubdate=pubdate,
                author_name=author_name,
                author_link=author_link or author.url())
     unique_id = kw.pop('unique_id', None)
     if unique_id:
         item.unique_id = unique_id
     return item
Example #17
0
 def post(cls, artifact, title=None, description=None, author=None, author_link=None, author_name=None, pubdate=None, link=None, **kw):
     """
     Create a Feed item.  Returns the item.
     But if anon doesn't have read access, create does not happen and None is returned
     """
     # TODO: fix security system so we can do this correctly and fast
     from allura import model as M
     anon = M.User.anonymous()
     if not security.has_access(artifact, 'read', user=anon):
         return
     if not security.has_access(c.project, 'read', user=anon):
         return
     idx = artifact.index()
     if author is None:
         author = c.user
     if author_name is None:
         author_name = author.get_pref('display_name')
     if title is None:
         title = '%s modified by %s' % (
             h.get_first(idx, 'title'), author_name)
     if description is None:
         description = title
     if pubdate is None:
         pubdate = datetime.utcnow()
     if link is None:
         link = artifact.url()
     item = cls(
         ref_id=artifact.index_id(),
         neighborhood_id=artifact.app_config.project.neighborhood_id,
         project_id=artifact.app_config.project_id,
         app_config_id=artifact.app_config_id,
         tool_name=artifact.app_config.tool_name,
         title=title,
         description=g.markdown.convert(description),
         link=link,
         pubdate=pubdate,
         author_name=author_name,
         author_link=author_link or author.url())
     unique_id = kw.pop('unique_id', None)
     if unique_id:
         item.unique_id = unique_id
     return item
Example #18
0
    def get_discussion_thread(self, data=None):
        """Return the discussion thread and parent_id for this artifact.

        :return: (:class:`allura.model.discuss.Thread`, parent_thread_id (int))

        """
        from .discuss import Thread
        t = Thread.query.get(ref_id=self.index_id())
        if t is None:
            idx = self.index()
            t = Thread.new(
                discussion_id=self.app_config.discussion_id,
                ref_id=idx['id'],
                subject='%s discussion' % h.get_first(idx, 'title'))
        parent_id = None
        if data:
            in_reply_to = data.get('in_reply_to', [])
            if in_reply_to:
                parent_id = in_reply_to[0]
        return t, parent_id
Example #19
0
    def get_discussion_thread(self, data=None):
        """Return the discussion thread and parent_id for this artifact.

        :return: (:class:`allura.model.discuss.Thread`, parent_thread_id (int))

        """
        from .discuss import Thread
        t = Thread.query.get(ref_id=self.index_id())
        if t is None:
            idx = self.index()
            t = Thread.new(
                app_config_id=self.app_config_id,
                discussion_id=self.app_config.discussion_id,
                ref_id=idx['id'],
                subject='%s discussion' % h.get_first(idx, 'title'))
        parent_id = None
        if data:
            in_reply_to = data.get('in_reply_to', [])
            if in_reply_to:
                parent_id = in_reply_to[0]
        return t, parent_id
Example #20
0
    def get_discussion_thread(self, data=None):
        """Return the discussion thread and parent_id for this artifact.

        :return: (:class:`allura.model.discuss.Thread`, parent_thread_id (int))

        """
        from .discuss import Thread
        threads = Thread.query.find(dict(ref_id=self.index_id())).all()
        if not threads:
            idx = self.index()
            t = Thread.new(
                app_config_id=self.app_config_id,
                discussion_id=self.app_config.discussion_id,
                ref_id=idx['id'],
                subject='%s discussion' % h.get_first(idx, 'title'))
        elif len(threads) == 1:
            t = threads[0]
        else:
            # there should not be multiple threads, we'll merge them
            destination = threads.pop()
            for thread in threads:
                for post in thread.posts:
                    post.thread_id = destination._id
                    destination.num_replies += 1
                    destination.last_post_date = max(destination.last_post_date, post.mod_date)
                    session(post).flush(post)
                    session(post).expunge(post)  # so thread.posts ref later in the code doesn't use stale posts
                Thread.query.remove({'_id': thread._id})  # NOT thread.delete() since that would remove its posts too
                thread.attachment_class().query.update({'thread_id': thread._id},
                                                       {'$set': {'thread_id': destination._id}},
                                                       multi=True)
            t = destination

        parent_id = None
        if data:
            in_reply_to = data.get('in_reply_to', [])
            if in_reply_to:
                parent_id = in_reply_to[0]

        return t, parent_id
Example #21
0
 def post(cls, artifact, title=None, description=None, author=None,
          author_link=None, author_name=None, pubdate=None, link=None, **kw):
     """
     Create a Feed item.  Returns the item.
     But if anon doesn't have read access, create does not happen and None is
     returned.
     """
     if not Feed.has_access(artifact):
         return
     idx = artifact.index()
     if author is None:
         author = c.user
     if author_name is None:
         author_name = author.get_pref('display_name')
     if title is None:
         title = '%s modified by %s' % (
             h.get_first(idx, 'title'), author_name)
     if description is None:
         description = title
     if pubdate is None:
         pubdate = datetime.utcnow()
     if link is None:
         link = artifact.url()
     item = cls(
         ref_id=artifact.index_id(),
         neighborhood_id=artifact.app_config.project.neighborhood_id,
         project_id=artifact.app_config.project_id,
         app_config_id=artifact.app_config_id,
         tool_name=artifact.app_config.tool_name,
         title=title,
         description=g.markdown.convert(description),
         link=link,
         pubdate=pubdate,
         author_name=author_name,
         author_link=author_link or author.url())
     unique_id = kw.pop('unique_id', None)
     if unique_id:
         item.unique_id = unique_id
     return item
Example #22
0
    def _make_notification(cls, artifact, topic, **kwargs):
        '''
        Create a Notification instance based on an artifact.  Special handling
        for comments when topic=='message'
        '''

        from allura.model import Project
        idx = artifact.index() if artifact else None
        subject_prefix = '[%s:%s] ' % (
            c.project.shortname, c.app.config.options.mount_point)
        post = ''
        if topic == 'message':
            post = kwargs.pop('post')
            text = post.text
            file_info = kwargs.pop('file_info', None)
            if file_info is not None:
                text = "%s\n\n\nAttachment:" % text
                if not isinstance(file_info, list):
                    file_info = [file_info]
                for attach in file_info:
                    attach.file.seek(0, 2)
                    bytecount = attach.file.tell()
                    attach.file.seek(0)
                    text = "%s %s (%s; %s) " % (text, attach.filename, h.do_filesizeformat(bytecount), attach.type)

            subject = post.subject or ''
            if post.parent_id and not subject.lower().startswith('re:'):
                subject = 'Re: ' + subject
            author = post.author()
            msg_id = artifact.url() + post._id
            parent_msg_id = artifact.url() + post.parent_id if post.parent_id else artifact.message_id()
            d = dict(
                _id=msg_id,
                from_address=str(author._id) if author != User.anonymous() else None,
                reply_to_address='"%s" <%s>' % (
                    subject_prefix, getattr(artifact, 'email_address', u'*****@*****.**')),
                subject=subject_prefix + subject,
                text=text,
                in_reply_to=parent_msg_id,
                author_id=author._id,
                pubdate=datetime.utcnow())
        elif topic == 'flash':
            n = cls(topic=topic,
                    text=kwargs['text'],
                    subject=kwargs.pop('subject', ''))
            return n
        else:
            subject = kwargs.pop('subject', '%s modified by %s' % (
                    h.get_first(idx, 'title'),c.user.get_pref('display_name')))
            reply_to = '"%s" <%s>' % (
                h.get_first(idx, 'title'),
                getattr(artifact, 'email_address', u'*****@*****.**'))
            d = dict(
                from_address=reply_to,
                reply_to_address=reply_to,
                subject=subject_prefix + subject,
                text=kwargs.pop('text', subject),
                author_id=c.user._id,
                pubdate=datetime.utcnow())
            if kwargs.get('message_id'):
                d['_id'] = kwargs['message_id']
            if c.user.get_pref('email_address'):
                d['from_address'] = '"%s" <%s>' % (
                    c.user.get_pref('display_name'),
                    c.user.get_pref('email_address'))
            elif c.user.email_addresses:
                d['from_address'] = '"%s" <%s>' % (
                    c.user.get_pref('display_name'),
                    c.user.email_addresses[0])
        if not d.get('text'):
            d['text'] = ''
        try:
            ''' Add addional text to the notification e-mail based on the artifact type '''
            template = cls.view.get_template('mail/' + artifact.type_s + '.txt')
            d['text'] += template.render(dict(c=c, g=g, config=config, data=artifact, post=post, h=h))
        except jinja2.TemplateNotFound:
            pass
        except:
            ''' Catch any errors loading or rendering the template,
            but the notification still gets sent if there is an error
            '''
            log.warn('Could not render notification template %s' % artifact.type_s, exc_info=True)

        assert d['reply_to_address'] is not None
        project = c.project
        if d.get('project_id', c.project._id) != c.project._id:
            project = Project.query.get(_id=d['project_id'])
        if project.notifications_disabled:
            log.info('Notifications disabled for project %s, not sending %s(%r)',
                     project.shortname, topic, artifact)
            return None
        n = cls(ref_id=artifact.index_id(),
                topic=topic,
                link=kwargs.pop('link', artifact.url()),
                **d)
        return n
Example #23
0
    def _make_notification(cls, artifact, topic, **kwargs):
        '''
        Create a Notification instance based on an artifact.  Special handling
        for comments when topic=='message'
        '''

        from allura.model import Project
        idx = artifact.index() if artifact else None
        subject_prefix = '[%s:%s] ' % (
            c.project.shortname, c.app.config.options.mount_point)
        post = ''
        if topic == 'message':
            post = kwargs.pop('post')
            text = kwargs.get('text') or post.text
            file_info = kwargs.pop('file_info', None)
            if file_info is not None:
                text = "%s\n\n\nAttachments:\n" % text
                if not isinstance(file_info, list):
                    file_info = [file_info]
                for attach in file_info:
                    attach.file.seek(0, 2)
                    bytecount = attach.file.tell()
                    attach.file.seek(0)
                    url = h.absurl('{}attachment/{}'.format(
                        post.url(), h.urlquote(attach.filename)))
                    text = "%s\n- [%s](%s) (%s; %s)" % (
                        text, attach.filename, url,
                        h.do_filesizeformat(bytecount), attach.type)

            subject = post.subject or ''
            if post.parent_id and not subject.lower().startswith('re:'):
                subject = 'Re: ' + subject
            author = post.author()
            msg_id = kwargs.get('message_id') or artifact.url() + post._id
            parent_msg_id = artifact.url() + \
                post.parent_id if post.parent_id else artifact.message_id()
            d = dict(
                _id=msg_id,
                from_address=str(
                    author._id) if author != User.anonymous() else None,
                reply_to_address='"%s" <%s>' % (
                    subject_prefix, getattr(
                        artifact, 'email_address', g.noreply)),
                subject=subject_prefix + subject,
                text=text,
                in_reply_to=parent_msg_id,
                references=cls._references(artifact, post),
                author_id=author._id,
                pubdate=datetime.utcnow())
        elif topic == 'flash':
            n = cls(topic=topic,
                    text=kwargs['text'],
                    subject=kwargs.pop('subject', ''))
            return n
        else:
            subject = kwargs.pop('subject', '%s modified by %s' % (
                h.get_first(idx, 'title'), c.user.get_pref('display_name')))
            reply_to = '"%s" <%s>' % (
                h.get_first(idx, 'title'),
                getattr(artifact, 'email_address', g.noreply))
            d = dict(
                from_address=reply_to,
                reply_to_address=reply_to,
                subject=subject_prefix + subject,
                text=kwargs.pop('text', subject),
                author_id=c.user._id,
                pubdate=datetime.utcnow())
            if kwargs.get('message_id'):
                d['_id'] = kwargs['message_id']
            if c.user.get_pref('email_address'):
                d['from_address'] = '"%s" <%s>' % (
                    c.user.get_pref('display_name'),
                    c.user.get_pref('email_address'))
            elif c.user.email_addresses:
                d['from_address'] = '"%s" <%s>' % (
                    c.user.get_pref('display_name'),
                    c.user.email_addresses[0])
        if not d.get('text'):
            d['text'] = ''
        try:
            ''' Add addional text to the notification e-mail based on the artifact type '''
            template = cls.view.get_template(
                'mail/' + artifact.type_s + '.txt')
            d['text'] += template.render(dict(c=c, g=g,
                                         config=config, data=artifact, post=post, h=h))
        except jinja2.TemplateNotFound:
            pass
        except:
            ''' Catch any errors loading or rendering the template,
            but the notification still gets sent if there is an error
            '''
            log.warn('Could not render notification template %s' %
                     artifact.type_s, exc_info=True)

        assert d['reply_to_address'] is not None
        project = c.project
        if d.get('project_id', c.project._id) != c.project._id:
            project = Project.query.get(_id=d['project_id'])
        if project.notifications_disabled:
            log.debug(
                'Notifications disabled for project %s, not sending %s(%r)',
                project.shortname, topic, artifact)
            return None
        n = cls(ref_id=artifact.index_id(),
                topic=topic,
                link=kwargs.pop('link', artifact.url()),
                **d)
        return n
    def _make_notification(cls, artifact, topic, **kwargs):
        """
        Create a Notification instance based on an artifact.  Special handling
        for comments when topic=='message'
        """

        from allura.model import Project

        idx = artifact.index() if artifact else None
        subject_prefix = "[%s:%s] " % (c.project.shortname, c.app.config.options.mount_point)
        post = ""
        if topic == "message":
            post = kwargs.pop("post")
            text = post.text
            file_info = kwargs.pop("file_info", None)
            if file_info is not None:
                text = "%s\n\n\nAttachment:" % text
                if not isinstance(file_info, list):
                    file_info = [file_info]
                for attach in file_info:
                    attach.file.seek(0, 2)
                    bytecount = attach.file.tell()
                    attach.file.seek(0)
                    text = "%s %s (%s; %s) " % (text, attach.filename, h.do_filesizeformat(bytecount), attach.type)

            subject = post.subject or ""
            if post.parent_id and not subject.lower().startswith("re:"):
                subject = "Re: " + subject
            author = post.author()
            msg_id = artifact.url() + post._id
            parent_msg_id = artifact.url() + post.parent_id if post.parent_id else artifact.message_id()
            d = dict(
                _id=msg_id,
                from_address=str(author._id) if author != User.anonymous() else None,
                reply_to_address='"%s" <%s>'
                % (subject_prefix, getattr(artifact, "email_address", u"*****@*****.**")),
                subject=subject_prefix + subject,
                text=text,
                in_reply_to=parent_msg_id,
                author_id=author._id,
                pubdate=datetime.utcnow(),
            )
        elif topic == "flash":
            n = cls(topic=topic, text=kwargs["text"], subject=kwargs.pop("subject", ""))
            return n
        else:
            subject = kwargs.pop(
                "subject", "%s modified by %s" % (h.get_first(idx, "title"), c.user.get_pref("display_name"))
            )
            reply_to = '"%s" <%s>' % (
                h.get_first(idx, "title"),
                getattr(artifact, "email_address", u"*****@*****.**"),
            )
            d = dict(
                from_address=reply_to,
                reply_to_address=reply_to,
                subject=subject_prefix + subject,
                text=kwargs.pop("text", subject),
                author_id=c.user._id,
                pubdate=datetime.utcnow(),
            )
            if kwargs.get("message_id"):
                d["_id"] = kwargs["message_id"]
            if c.user.get_pref("email_address"):
                d["from_address"] = '"%s" <%s>' % (c.user.get_pref("display_name"), c.user.get_pref("email_address"))
            elif c.user.email_addresses:
                d["from_address"] = '"%s" <%s>' % (c.user.get_pref("display_name"), c.user.email_addresses[0])
        if not d.get("text"):
            d["text"] = ""
        try:
            """ Add addional text to the notification e-mail based on the artifact type """
            template = cls.view.get_template("mail/" + artifact.type_s + ".txt")
            d["text"] += template.render(dict(c=c, g=g, config=config, data=artifact, post=post, h=h))
        except jinja2.TemplateNotFound:
            pass
        except:
            """ Catch any errors loading or rendering the template,
            but the notification still gets sent if there is an error
            """
            log.warn("Could not render notification template %s" % artifact.type_s, exc_info=True)

        assert d["reply_to_address"] is not None
        project = c.project
        if d.get("project_id", c.project._id) != c.project._id:
            project = Project.query.get(_id=d["project_id"])
        if project.notifications_disabled:
            log.info("Notifications disabled for project %s, not sending %s(%r)", project.shortname, topic, artifact)
            return None
        n = cls(ref_id=artifact.index_id(), topic=topic, link=kwargs.pop("link", artifact.url()), **d)
        return n