Changeset - 4a9184804089
[Not reviewed]
default
0 3 0
Mads Kiilerich - 11 years ago 2014-07-04 14:12:07
madski@unity3d.com
comments: linkify revision hashes and issue tracker references

This was hard with RST markup - much easier to do reasonably safely in plain text.
3 files changed with 9 insertions and 5 deletions:
0 comments (0 inline, 0 general)
kallithea/lib/helpers.py
Show inline comments
 
@@ -1357,107 +1357,111 @@ def urlify_issues(newtext, repository, l
 
        and 'issue_prefix%s' % x.group(1) in conf
 
    ]
 

	
 
    if valid_indices:
 
        log.debug('found issue server suffixes `%s` during valuation of: %s',
 
                  ','.join(valid_indices), newtext)
 

	
 
    for pattern_index in valid_indices:
 
        ISSUE_PATTERN = conf.get('issue_pat%s' % pattern_index)
 
        ISSUE_SERVER_LNK = conf.get('issue_server_link%s' % pattern_index)
 
        ISSUE_PREFIX = conf.get('issue_prefix%s' % pattern_index)
 

	
 
        log.debug('pattern suffix `%s` PAT:%s SERVER_LINK:%s PREFIX:%s',
 
                  pattern_index, ISSUE_PATTERN, ISSUE_SERVER_LNK,
 
                  ISSUE_PREFIX)
 

	
 
        URL_PAT = re.compile(ISSUE_PATTERN)
 

	
 
        urlify_issues_replace = _urlify_issues_replace_f(repository, ISSUE_SERVER_LNK, ISSUE_PREFIX)
 
        newtext = URL_PAT.sub(urlify_issues_replace, newtext)
 
        log.debug('processed prefix:`%s` => %s', pattern_index, newtext)
 

	
 
    # if we actually did something above
 
    if link_:
 
        # wrap not links into final link => link_
 
        newtext = linkify_others(newtext, link_)
 
    return newtext
 

	
 

	
 
def rst(source):
 
    return literal('<div class="rst-block">%s</div>' %
 
                   MarkupRenderer.rst(source))
 

	
 

	
 
def rst_w_mentions(source):
 
    """
 
    Wrapped rst renderer with @mention highlighting
 

	
 
    :param source:
 
    """
 
    return literal('<div class="rst-block">%s</div>' %
 
                   MarkupRenderer.rst_with_mentions(source))
 

	
 

	
 
def _mentions_replace(match_obj):
 
    return '<b>@%s</b>' % match_obj.group(1)
 

	
 

	
 
def render_w_mentions(source):
 
def render_w_mentions(source, repo_name=None):
 
    """
 
    Render plain text with @mention highlighting.
 
    Render plain text with revision hashes and issue references urlified
 
    and with @mention highlighting.
 
    """
 
    s = source.rstrip()
 
    s = safe_unicode(s)
 
    s = '\n'.join(s.splitlines())
 
    s = html_escape(s)
 
    # this sequence of html-ifications seems to be safe and non-conflicting
 
    # if the issues regexp is sane
 
    s = _urlify_text(s)
 
    if repo_name is not None:
 
        s = urlify_changesets(s, repo_name)
 
    s = urlify_issues(s, repo_name)
 
    s = MENTIONS_REGEX.sub(_mentions_replace, s)
 
    return literal('<code style="white-space:pre-wrap">%s</code>' % s)
 

	
 

	
 
def short_ref(ref_type, ref_name):
 
    if ref_type == 'rev':
 
        return short_id(ref_name)
 
    return ref_name
 

	
 
def link_to_ref(repo_name, ref_type, ref_name, rev=None):
 
    """
 
    Return full markup for a href to changeset_home for a changeset.
 
    If ref_type is branch it will link to changelog.
 
    ref_name is shortened if ref_type is 'rev'.
 
    if rev is specified show it too, explicitly linking to that revision.
 
    """
 
    txt = short_ref(ref_type, ref_name)
 
    if ref_type == 'branch':
 
        u = url('changelog_home', repo_name=repo_name, branch=ref_name)
 
    else:
 
        u = url('changeset_home', repo_name=repo_name, revision=ref_name)
 
    l = link_to(repo_name + '#' + txt, u)
 
    if rev and ref_type != 'rev':
 
        l = literal('%s (%s)' % (l, link_to(short_id(rev), url('changeset_home', repo_name=repo_name, revision=rev))))
 
    return l
 

	
 
def changeset_status(repo, revision):
 
    return ChangesetStatusModel().get_status(repo, revision)
 

	
 

	
 
def changeset_status_lbl(changeset_status):
 
    return dict(ChangesetStatus.STATUSES).get(changeset_status)
 

	
 

	
 
def get_permission_name(key):
 
    return dict(Permission.PERMS).get(key)
 

	
 

	
 
def journal_filter_help():
 
    return _(textwrap.dedent('''
 
        Example filter terms:
 
            repository:vcs
 
            username:developer
 
            action:*push*
 
            ip:127.0.0.1
 
            date:20120101
 
            date:[20120101100000 TO 20120102]
 

	
kallithea/model/notification.py
Show inline comments
 
@@ -14,161 +14,161 @@
 
"""
 
kallithea.model.notification
 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 

	
 
Model for notifications
 

	
 

	
 
This file was forked by the Kallithea project in July 2014.
 
Original author and date, and relevant copyright and licensing information is below:
 
:created_on: Nov 20, 2011
 
:author: marcink
 
:copyright: (c) 2013 RhodeCode GmbH, and others.
 
:license: GPLv3, see LICENSE.md for more details.
 
"""
 

	
 
import logging
 
import traceback
 

	
 
from pylons import tmpl_context as c
 
from pylons.i18n.translation import _
 
from sqlalchemy.orm import joinedload, subqueryload
 

	
 
import kallithea
 
from kallithea.lib import helpers as h
 
from kallithea.lib.utils2 import safe_unicode
 
from kallithea.model import BaseModel
 
from kallithea.model.db import Notification, User, UserNotification
 
from kallithea.model.meta import Session
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
class NotificationModel(BaseModel):
 

	
 
    cls = Notification
 

	
 
    def __get_notification(self, notification):
 
        if isinstance(notification, Notification):
 
            return notification
 
        elif isinstance(notification, (int, long)):
 
            return Notification.get(notification)
 
        else:
 
            if notification is not None:
 
                raise Exception('notification must be int, long or Instance'
 
                                ' of Notification got %s' % type(notification))
 

	
 
    def create(self, created_by, subject, body, recipients=None,
 
               type_=Notification.TYPE_MESSAGE, with_email=True,
 
               email_kwargs=None):
 
               email_kwargs=None, repo_name=None):
 
        """
 

	
 
        Creates notification of given type
 

	
 
        :param created_by: int, str or User instance. User who created this
 
            notification
 
        :param subject:
 
        :param body:
 
        :param recipients: list of int, str or User objects, when None
 
            is given send to all admins
 
        :param type_: type of notification
 
        :param with_email: send email with this notification
 
        :param email_kwargs: additional dict to pass as args to email template
 
        """
 
        from kallithea.lib.celerylib import tasks, run_task
 
        email_kwargs = email_kwargs or {}
 
        if recipients and not getattr(recipients, '__iter__', False):
 
            raise Exception('recipients must be a list or iterable')
 

	
 
        created_by_obj = self._get_user(created_by)
 

	
 
        recipients_objs = []
 
        if recipients:
 
            for u in recipients:
 
                obj = self._get_user(u)
 
                if obj is not None:
 
                    recipients_objs.append(obj)
 
                else:
 
                    # TODO: inform user that requested operation couldn't be completed
 
                    log.error('cannot email unknown user %r', u)
 
            recipients_objs = set(recipients_objs)
 
            log.debug('sending notifications %s to %s',
 
                type_, recipients_objs
 
            )
 
        elif recipients is None:
 
            # empty recipients means to all admins
 
            recipients_objs = User.query().filter(User.admin == True).all()
 
            log.debug('sending notifications %s to admins: %s',
 
                type_, recipients_objs
 
            )
 
        #else: silently skip notification mails?
 

	
 
        # TODO: inform user who are notified
 
        notif = Notification.create(
 
            created_by=created_by_obj, subject=subject,
 
            body=body, recipients=recipients_objs, type_=type_
 
        )
 

	
 
        if not with_email:
 
            return notif
 

	
 
        #don't send email to person who created this comment
 
        rec_objs = set(recipients_objs).difference(set([created_by_obj]))
 

	
 
        headers = None
 
        if 'threading' in email_kwargs:
 
            headers = {'References': ' '.join('<%s>' % x for x in email_kwargs['threading'])}
 

	
 
        # send email with notification to all other participants
 
        for rec in rec_objs:
 
            ## this is passed into template
 
            html_kwargs = {
 
                      'subject': subject,
 
                      'body': h.render_w_mentions(body),
 
                      'body': h.render_w_mentions(body, repo_name),
 
                      'when': h.fmt_date(notif.created_on),
 
                      'user': notif.created_by_user.username,
 
                      }
 

	
 
            txt_kwargs = {
 
                      'subject': subject,
 
                      'body': body,
 
                      'when': h.fmt_date(notif.created_on),
 
                      'user': notif.created_by_user.username,
 
                      }
 

	
 
            html_kwargs.update(email_kwargs)
 
            txt_kwargs.update(email_kwargs)
 
            email_subject = EmailNotificationModel() \
 
                                .get_email_description(type_, **txt_kwargs)
 
            email_txt_body = EmailNotificationModel() \
 
                                .get_email_tmpl(type_, 'txt', **txt_kwargs)
 
            email_html_body = EmailNotificationModel() \
 
                                .get_email_tmpl(type_, 'html', **html_kwargs)
 

	
 
            run_task(tasks.send_email, [rec.email], email_subject, email_txt_body,
 
                     email_html_body, headers, author=created_by_obj)
 

	
 
        return notif
 

	
 
    def delete(self, user, notification):
 
        # we don't want to remove actual notification just the assignment
 
        try:
 
            notification = self.__get_notification(notification)
 
            user = self._get_user(user)
 
            if notification and user:
 
                obj = UserNotification.query() \
 
                        .filter(UserNotification.user == user) \
 
                        .filter(UserNotification.notification
 
                                == notification) \
 
                        .one()
 
                Session().delete(obj)
 
                return True
 
        except Exception:
 
            log.error(traceback.format_exc())
 
            raise
 

	
 
    def get_for_user(self, user, filter_=None):
 
        """
 
        Get notifications for given user, filter them if filter dict is given
 

	
 
        :param user:
 
        :param filter:
kallithea/templates/changeset/changeset_file_comment.html
Show inline comments
 
## -*- coding: utf-8 -*-
 
## usage:
 
## <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
 
## ${comment.comment_block(co)}
 
##
 
<%def name="comment_block(co)">
 
  <div class="comment" id="comment-${co.comment_id}" line="${co.line_no}">
 
    <div class="comment-prev-next-links"></div>
 
    <div class="comment-wrapp">
 
      <div class="meta">
 
          <div style="float:left">
 
               ${h.gravatar(co.author.email, size=20)}
 
          </div>
 
          <div class="user">
 
              ${co.author.full_name_and_username}
 
          </div>
 

	
 
          <span>
 
              ${h.age(co.modified_at)}
 
              %if co.pull_request:
 
                ${_('on pull request')}
 
                <a href="${co.pull_request.url()}">"${co.pull_request.title or _("No title")}"</a>
 
              %else:
 
                ${_('on this changeset')}
 
              %endif
 
              <a class="permalink" href="${co.url()}">&para;</a>
 
          </span>
 

	
 
          %if h.HasPermissionAny('hg.admin')() or h.HasRepoPermissionAny('repository.admin')(c.repo_name) or co.author.user_id == c.authuser.user_id:
 
            <div onClick="confirm('${_("Delete comment?")}') && deleteComment(${co.comment_id})" class="buttons delete-comment btn btn-mini">${_('Delete')}</div>
 
          %endif
 
      </div>
 
      <div class="text">
 
        %if co.status_change:
 
           <div class="automatic-comment">
 
             <p>
 
               <span title="${_('Changeset status')}" class="changeset-status-lbl">${_("Status change")}: ${co.status_change[0].status_lbl}</span>
 
               <span class="changeset-status-ico"><i class="icon-circle changeset-status-${co.status_change[0].status}"></i></span>
 
             </p>
 
           </div>
 
        %endif
 
        %if co.text:
 
          ${h.render_w_mentions(co.text)|n}
 
          ${h.render_w_mentions(co.text, c.repo_name)|n}
 
        %endif
 
      </div>
 
    </div>
 
  </div>
 
</%def>
 

	
 

	
 
## expanded with .format(f_path, line_no)
 
## TODO: don't assume line_no is globally unique ...
 
<%def name="comment_inline_form()">
 
<div id='comment-inline-form-template' style="display:none">
 
  <div class="ac">
 
  %if c.authuser.username != 'default':
 
    ${h.form('#', class_='inline-form')}
 
      <div id="edit-container_{1}" class="clearfix">
 
        <div class="comment-help">${_('Commenting on line {1}.')}
 
          <span style="color:#577632" class="tooltip">${_('Comments are in plain text. Use @username inside this text to notify another user.')|n}</span>
 
        </div>
 
        <div class="mentions-container" id="mentions_container_{1}"></div>
 
        <textarea id="text_{1}" name="text" class="comment-block-ta yui-ac-input"></textarea>
 
      </div>
 
      <div id="preview-container_{1}" class="clearfix" style="display:none">
 
        <div class="comment-help">
 
            ${_('Comment preview')}
 
        </div>
 
        <div id="preview-box_{1}" class="preview-box"></div>
 
      </div>
 
      <div class="comment-button">
 
        <div class="submitting-overlay">${_('Submitting ...')}</div>
 
        <input type="hidden" name="f_path" value="{0}">
 
        <input type="hidden" name="line" value="{1}">
 
        ${h.submit('save', _('Comment'), class_='btn btn-small save-inline-form')}
 
        ${h.reset('hide-inline-form', _('Cancel'), class_='btn btn-small hide-inline-form')}
 
        <div id="preview-btn_{1}" class="preview-btn btn btn-small">${_('Preview')}</div>
 
        <div id="edit-btn_{1}" class="edit-btn btn btn-small" style="display:none">${_('Edit')}</div>
 
      </div>
 
    ${h.end_form()}
 
  %else:
 
      ${h.form('')}
 
      <div class="clearfix">
 
          <div class="comment-help">
 
            ${_('You need to be logged in to comment.')} <a href="${h.url('login_home', came_from=request.path_qs)}">${_('Login now')}</a>
 
          </div>
 
      </div>
 
      <div class="comment-button">
 
      ${h.reset('hide-inline-form', _('Hide'), class_='btn btn-small hide-inline-form')}
 
      </div>
 
      ${h.end_form()}
0 comments (0 inline, 0 general)