action_parser.py
349 lines
| 12.5 KiB
| text/x-python
|
PythonLexer
r1 | # -*- coding: utf-8 -*- | |||
r1271 | # Copyright (C) 2010-2017 RhodeCode GmbH | |||
r1 | # | |||
# This program is free software: you can redistribute it and/or modify | ||||
# it under the terms of the GNU Affero General Public License, version 3 | ||||
# (only), as published by the Free Software Foundation. | ||||
# | ||||
# This program is distributed in the hope that it will be useful, | ||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
# GNU General Public License for more details. | ||||
# | ||||
# You should have received a copy of the GNU Affero General Public License | ||||
# along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
# | ||||
# This program is dual-licensed. If you wish to learn more about the | ||||
# RhodeCode Enterprise Edition, including its added features, Support services, | ||||
# and proprietary license terms, please see https://rhodecode.com/licenses/ | ||||
import logging | ||||
from pylons import url | ||||
from pylons.i18n.translation import _ | ||||
from webhelpers.html.builder import literal | ||||
from webhelpers.html.tags import link_to | ||||
from rhodecode.lib.utils2 import AttributeDict | ||||
from rhodecode.lib.vcs.backends.base import BaseCommit | ||||
from rhodecode.lib.vcs.exceptions import CommitDoesNotExistError | ||||
log = logging.getLogger(__name__) | ||||
def action_parser(user_log, feed=False, parse_cs=False): | ||||
""" | ||||
This helper will action_map the specified string action into translated | ||||
fancy names with icons and links | ||||
:param user_log: user log instance | ||||
:param feed: use output for feeds (no html and fancy icons) | ||||
:param parse_cs: parse Changesets into VCS instances | ||||
""" | ||||
r1834 | if user_log.version == 'v2': | |||
ap = AuditLogParser(user_log) | ||||
return ap.callbacks() | ||||
else: | ||||
# old style | ||||
ap = ActionParser(user_log, feed=False, parse_commits=False) | ||||
return ap.callbacks() | ||||
r1 | ||||
class ActionParser(object): | ||||
commits_limit = 3 # display this amount always | ||||
commits_top_limit = 50 # show up to this amount of commits hidden | ||||
def __init__(self, user_log, feed=False, parse_commits=False): | ||||
self.user_log = user_log | ||||
self.feed = feed | ||||
self.parse_commits = parse_commits | ||||
self.action = user_log.action | ||||
self.action_params = ' ' | ||||
x = self.action.split(':', 1) | ||||
if len(x) > 1: | ||||
self.action, self.action_params = x | ||||
def callbacks(self): | ||||
action_str = self.action_map.get(self.action, self.action) | ||||
if self.feed: | ||||
action = action_str[0].replace('[', '').replace(']', '') | ||||
else: | ||||
action = action_str[0]\ | ||||
.replace('[', '<span class="journal_highlight">')\ | ||||
.replace(']', '</span>') | ||||
action_params_func = _no_params_func | ||||
if callable(action_str[1]): | ||||
action_params_func = action_str[1] | ||||
# returned callbacks we need to call to get | ||||
return [ | ||||
lambda: literal(action), action_params_func, | ||||
self.action_parser_icon] | ||||
@property | ||||
def action_map(self): | ||||
# action : translated str, callback(extractor), icon | ||||
action_map = { | ||||
'user_deleted_repo': ( | ||||
_('[deleted] repository'), | ||||
None, 'icon-trash'), | ||||
'user_created_repo': ( | ||||
_('[created] repository'), | ||||
None, 'icon-plus icon-plus-colored'), | ||||
'user_created_fork': ( | ||||
_('[created] repository as fork'), | ||||
None, 'icon-code-fork'), | ||||
'user_forked_repo': ( | ||||
_('[forked] repository'), | ||||
self.get_fork_name, 'icon-code-fork'), | ||||
'user_updated_repo': ( | ||||
_('[updated] repository'), | ||||
None, 'icon-pencil icon-pencil-colored'), | ||||
'user_downloaded_archive': ( | ||||
_('[downloaded] archive from repository'), | ||||
self.get_archive_name, 'icon-download-alt'), | ||||
'admin_deleted_repo': ( | ||||
_('[delete] repository'), | ||||
None, 'icon-trash'), | ||||
'admin_created_repo': ( | ||||
_('[created] repository'), | ||||
None, 'icon-plus icon-plus-colored'), | ||||
'admin_forked_repo': ( | ||||
_('[forked] repository'), | ||||
None, 'icon-code-fork icon-fork-colored'), | ||||
'admin_updated_repo': ( | ||||
_('[updated] repository'), | ||||
None, 'icon-pencil icon-pencil-colored'), | ||||
'admin_created_user': ( | ||||
_('[created] user'), | ||||
self.get_user_name, 'icon-user icon-user-colored'), | ||||
'admin_updated_user': ( | ||||
_('[updated] user'), | ||||
self.get_user_name, 'icon-user icon-user-colored'), | ||||
'admin_created_users_group': ( | ||||
_('[created] user group'), | ||||
self.get_users_group, 'icon-pencil icon-pencil-colored'), | ||||
'admin_updated_users_group': ( | ||||
_('[updated] user group'), | ||||
self.get_users_group, 'icon-pencil icon-pencil-colored'), | ||||
'user_commented_revision': ( | ||||
_('[commented] on commit in repository'), | ||||
self.get_cs_links, 'icon-comment icon-comment-colored'), | ||||
'user_commented_pull_request': ( | ||||
_('[commented] on pull request for'), | ||||
self.get_pull_request, 'icon-comment icon-comment-colored'), | ||||
'user_closed_pull_request': ( | ||||
_('[closed] pull request for'), | ||||
self.get_pull_request, 'icon-check'), | ||||
'user_merged_pull_request': ( | ||||
_('[merged] pull request for'), | ||||
self.get_pull_request, 'icon-check'), | ||||
'push': ( | ||||
_('[pushed] into'), | ||||
self.get_cs_links, 'icon-arrow-up'), | ||||
'push_local': ( | ||||
_('[committed via RhodeCode] into repository'), | ||||
self.get_cs_links, 'icon-pencil icon-pencil-colored'), | ||||
'push_remote': ( | ||||
_('[pulled from remote] into repository'), | ||||
self.get_cs_links, 'icon-arrow-up'), | ||||
'pull': ( | ||||
_('[pulled] from'), | ||||
None, 'icon-arrow-down'), | ||||
'started_following_repo': ( | ||||
_('[started following] repository'), | ||||
None, 'icon-heart icon-heart-colored'), | ||||
'stopped_following_repo': ( | ||||
_('[stopped following] repository'), | ||||
None, 'icon-heart-empty icon-heart-colored'), | ||||
} | ||||
return action_map | ||||
def get_fork_name(self): | ||||
r1785 | from rhodecode.lib import helpers as h | |||
r1 | repo_name = self.action_params | |||
r1785 | _url = h.route_path('repo_summary', repo_name=repo_name) | |||
r1 | return _('fork name %s') % link_to(self.action_params, _url) | |||
def get_user_name(self): | ||||
user_name = self.action_params | ||||
return user_name | ||||
def get_users_group(self): | ||||
group_name = self.action_params | ||||
return group_name | ||||
def get_pull_request(self): | ||||
r1813 | from rhodecode.lib import helpers as h | |||
r1 | pull_request_id = self.action_params | |||
if self.is_deleted(): | ||||
repo_name = self.user_log.repository_name | ||||
else: | ||||
repo_name = self.user_log.repository.repo_name | ||||
return link_to( | ||||
_('Pull request #%s') % pull_request_id, | ||||
r1813 | h.route_path('pullrequest_show', repo_name=repo_name, | |||
pull_request_id=pull_request_id)) | ||||
r1 | ||||
def get_archive_name(self): | ||||
archive_name = self.action_params | ||||
return archive_name | ||||
def action_parser_icon(self): | ||||
tmpl = """<i class="%s" alt="%s"></i>""" | ||||
ico = self.action_map.get(self.action, ['', '', ''])[2] | ||||
return literal(tmpl % (ico, self.action)) | ||||
def get_cs_links(self): | ||||
if self.is_deleted(): | ||||
return self.action_params | ||||
repo_name = self.user_log.repository.repo_name | ||||
commit_ids = self.action_params.split(',') | ||||
commits = self.get_commits(commit_ids) | ||||
link_generator = ( | ||||
self.lnk(commit, repo_name) | ||||
for commit in commits[:self.commits_limit]) | ||||
commit_links = [" " + ', '.join(link_generator)] | ||||
_op1, _name1 = _get_op(commit_ids[0]) | ||||
_op2, _name2 = _get_op(commit_ids[-1]) | ||||
commit_id_range = '%s...%s' % (_name1, _name2) | ||||
compare_view = ( | ||||
' <div class="compare_view tooltip" title="%s">' | ||||
'<a href="%s">%s</a> </div>' % ( | ||||
_('Show all combined commits %s->%s') % ( | ||||
commit_ids[0][:12], commit_ids[-1][:12] | ||||
), | ||||
url('changeset_home', repo_name=repo_name, | ||||
revision=commit_id_range), _('compare view') | ||||
) | ||||
) | ||||
if len(commit_ids) > self.commits_limit: | ||||
more_count = len(commit_ids) - self.commits_limit | ||||
commit_links.append( | ||||
_(' and %(num)s more commits') % {'num': more_count} | ||||
) | ||||
if len(commits) > 1: | ||||
commit_links.append(compare_view) | ||||
return ''.join(commit_links) | ||||
def get_commits(self, commit_ids): | ||||
commits = [] | ||||
if not filter(lambda v: v != '', commit_ids): | ||||
return commits | ||||
repo = None | ||||
if self.parse_commits: | ||||
repo = self.user_log.repository.scm_instance() | ||||
for commit_id in commit_ids[:self.commits_top_limit]: | ||||
_op, _name = _get_op(commit_id) | ||||
# we want parsed commits, or new log store format is bad | ||||
if self.parse_commits: | ||||
try: | ||||
commit = repo.get_commit(commit_id=commit_id) | ||||
commits.append(commit) | ||||
except CommitDoesNotExistError: | ||||
log.error( | ||||
'cannot find commit id %s in this repository', | ||||
commit_id) | ||||
commits.append(commit_id) | ||||
continue | ||||
else: | ||||
fake_commit = AttributeDict({ | ||||
'short_id': commit_id[:12], | ||||
'raw_id': commit_id, | ||||
'message': '', | ||||
'op': _op, | ||||
'ref_name': _name | ||||
}) | ||||
commits.append(fake_commit) | ||||
return commits | ||||
def lnk(self, commit_or_id, repo_name): | ||||
from rhodecode.lib.helpers import tooltip | ||||
if isinstance(commit_or_id, (BaseCommit, AttributeDict)): | ||||
lazy_cs = True | ||||
if (getattr(commit_or_id, 'op', None) and | ||||
getattr(commit_or_id, 'ref_name', None)): | ||||
lazy_cs = False | ||||
lbl = '?' | ||||
if commit_or_id.op == 'delete_branch': | ||||
lbl = '%s' % _('Deleted branch: %s') % commit_or_id.ref_name | ||||
title = '' | ||||
elif commit_or_id.op == 'tag': | ||||
lbl = '%s' % _('Created tag: %s') % commit_or_id.ref_name | ||||
title = '' | ||||
_url = '#' | ||||
else: | ||||
lbl = '%s' % (commit_or_id.short_id[:8]) | ||||
_url = url('changeset_home', repo_name=repo_name, | ||||
revision=commit_or_id.raw_id) | ||||
title = tooltip(commit_or_id.message) | ||||
else: | ||||
# commit cannot be found/striped/removed etc. | ||||
lbl = ('%s' % commit_or_id)[:12] | ||||
_url = '#' | ||||
title = _('Commit not found') | ||||
if self.parse_commits: | ||||
return link_to(lbl, _url, title=title, class_='tooltip') | ||||
return link_to(lbl, _url, raw_id=commit_or_id.raw_id, repo_name=repo_name, | ||||
class_='lazy-cs' if lazy_cs else '') | ||||
def is_deleted(self): | ||||
return self.user_log.repository is None | ||||
r1834 | class AuditLogParser(object): | |||
def __init__(self, audit_log_entry): | ||||
self.audit_log_entry = audit_log_entry | ||||
def get_icon(self, action): | ||||
return 'icon-rhodecode' | ||||
def callbacks(self): | ||||
action_str = self.audit_log_entry.action | ||||
def callback(): | ||||
# returned callbacks we need to call to get | ||||
action = action_str \ | ||||
.replace('[', '<span class="journal_highlight">')\ | ||||
.replace(']', '</span>') | ||||
return literal(action) | ||||
def icon(): | ||||
tmpl = """<i class="%s" alt="%s"></i>""" | ||||
ico = self.get_icon(action_str) | ||||
return literal(tmpl % (ico, action_str)) | ||||
action_params_func = _no_params_func | ||||
return [ | ||||
callback, action_params_func, icon] | ||||
r1 | def _no_params_func(): | |||
return "" | ||||
def _get_op(commit_id): | ||||
_op = None | ||||
_name = commit_id | ||||
if len(commit_id.split('=>')) == 2: | ||||
_op, _name = commit_id.split('=>') | ||||
return _op, _name | ||||