##// END OF EJS Templates
hovercards: allow hovercards on parsed !PR patterns....
dan -
r4046:a792f9c3 default
parent child Browse files
Show More
@@ -0,0 +1,26 b''
1 <%namespace name="base" file="/base/base.mako"/>
2 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
3
4 % if c.can_view_pr:
5 <div class="pr-hovercard-header">
6 <div class="pull-left tagtag">
7 ${c.pull_request.status}
8 </div>
9 <div class="pr-hovercard-user">
10 ${_('Created')}: ${h.format_date(c.pull_request.created_on)}
11 </div>
12 </div>
13
14 <div class="pr-hovercard-title">
15 <h3><a href="${h.route_path('pull_requests_global', pull_request_id=c.pull_request.pull_request_id)}">!${c.pull_request.pull_request_id}</a> - ${c.pull_request.title}</h3>
16 </div>
17
18 <div class="pr-hovercard-footer">
19 ${_('repo')}: ${c.pull_request.target_repo.repo_name}
20 </div>
21 % else:
22 ## user cannot view this PR we just show the generic info, without any exposed data
23 <div class="pr-hovercard-title">
24 <h3>${_('Pull Request')} !${c.pull_request.pull_request_id}</h3>
25 </div>
26 % endif No newline at end of file
@@ -30,6 +30,10 b' def includeme(config):'
30 pattern='/_hovercard/user_group/{user_group_id}')
30 pattern='/_hovercard/user_group/{user_group_id}')
31
31
32 config.add_route(
32 config.add_route(
33 name='hovercard_pull_request',
34 pattern='/_hovercard/pull_request/{pull_request_id}')
35
36 config.add_route(
33 name='hovercard_repo_commit',
37 name='hovercard_repo_commit',
34 pattern='/_hovercard/commit/{repo_name:.*?[^/]}/{commit_id}', repo_route=True)
38 pattern='/_hovercard/commit/{repo_name:.*?[^/]}/{commit_id}', repo_route=True)
35
39
@@ -35,7 +35,7 b' from rhodecode.lib.utils2 import safe_un'
35 from rhodecode.lib.ext_json import json
35 from rhodecode.lib.ext_json import json
36 from rhodecode.lib.vcs.nodes import FileNode
36 from rhodecode.lib.vcs.nodes import FileNode
37 from rhodecode.model.db import (
37 from rhodecode.model.db import (
38 func, true, or_, case, in_filter_generator, Repository, RepoGroup, User, UserGroup)
38 func, true, or_, case, in_filter_generator, Repository, RepoGroup, User, UserGroup, PullRequest)
39 from rhodecode.model.repo import RepoModel
39 from rhodecode.model.repo import RepoModel
40 from rhodecode.model.repo_group import RepoGroupModel
40 from rhodecode.model.repo_group import RepoGroupModel
41 from rhodecode.model.scm import RepoGroupList, RepoList
41 from rhodecode.model.scm import RepoGroupList, RepoList
@@ -71,6 +71,19 b' class HoverCardsView(BaseAppView):'
71 c.user_group = UserGroup.get_or_404(user_group_id)
71 c.user_group = UserGroup.get_or_404(user_group_id)
72 return self._get_template_context(c)
72 return self._get_template_context(c)
73
73
74 @LoginRequired()
75 @view_config(
76 route_name='hovercard_pull_request', request_method='GET', xhr=True,
77 renderer='rhodecode:templates/hovercards/hovercard_pull_request.mako')
78 def hovercard_pull_request(self):
79 c = self.load_default_context()
80 c.pull_request = PullRequest.get_or_404(
81 self.request.matchdict['pull_request_id'])
82 perms = ['repository.read', 'repository.write', 'repository.admin']
83 c.can_view_pr = h.HasRepoPermissionAny(*perms)(
84 c.pull_request.target_repo.repo_name)
85 return self._get_template_context(c)
86
74
87
75 class HoverCardsRepoView(RepoAppView):
88 class HoverCardsRepoView(RepoAppView):
76 def load_default_context(self):
89 def load_default_context(self):
@@ -74,9 +74,10 b' from webhelpers2.number import format_by'
74 from rhodecode.lib.action_parser import action_parser
74 from rhodecode.lib.action_parser import action_parser
75 from rhodecode.lib.ext_json import json
75 from rhodecode.lib.ext_json import json
76 from rhodecode.lib.utils import repo_name_slug, get_custom_lexer
76 from rhodecode.lib.utils import repo_name_slug, get_custom_lexer
77 from rhodecode.lib.utils2 import str2bool, safe_unicode, safe_str, \
77 from rhodecode.lib.utils2 import (
78 get_commit_safe, datetime_to_time, time_to_datetime, time_to_utcdatetime, \
78 str2bool, safe_unicode, safe_str,
79 AttributeDict, safe_int, md5, md5_safe
79 get_commit_safe, datetime_to_time, time_to_datetime, time_to_utcdatetime,
80 AttributeDict, safe_int, md5, md5_safe, get_host_info)
80 from rhodecode.lib.markup_renderer import MarkupRenderer, relative_links
81 from rhodecode.lib.markup_renderer import MarkupRenderer, relative_links
81 from rhodecode.lib.vcs.exceptions import CommitDoesNotExistError
82 from rhodecode.lib.vcs.exceptions import CommitDoesNotExistError
82 from rhodecode.lib.vcs.backends.base import BaseChangeset, EmptyCommit
83 from rhodecode.lib.vcs.backends.base import BaseChangeset, EmptyCommit
@@ -1632,10 +1633,10 b' def _process_url_func(match_obj, repo_na'
1632 '%(pref)s<a class="tooltip-hovercard %(cls)s" href="%(url)s" data-hovercard-url="%(hovercard_url)s">'
1633 '%(pref)s<a class="tooltip-hovercard %(cls)s" href="%(url)s" data-hovercard-url="%(hovercard_url)s">'
1633 '%(issue-prefix)s%(id-repr)s'
1634 '%(issue-prefix)s%(id-repr)s'
1634 '</a>')
1635 '</a>')
1635 elif link_format == 'rst':
1636 elif link_format in ['rst', 'rst+hovercard']:
1636 tmpl = '`%(issue-prefix)s%(id-repr)s <%(url)s>`_'
1637 tmpl = '`%(issue-prefix)s%(id-repr)s <%(url)s>`_'
1637 elif link_format == 'markdown':
1638 elif link_format in ['markdown', 'markdown+hovercard']:
1638 tmpl = '[%(issue-prefix)s%(id-repr)s](%(url)s)'
1639 tmpl = '[%(pref)s%(issue-prefix)s%(id-repr)s](%(url)s)'
1639 else:
1640 else:
1640 raise ValueError('Bad link_format:{}'.format(link_format))
1641 raise ValueError('Bad link_format:{}'.format(link_format))
1641
1642
@@ -1648,11 +1649,23 b' def _process_url_func(match_obj, repo_na'
1648 'repo': repo_name,
1649 'repo': repo_name,
1649 'repo_name': repo_name_cleaned,
1650 'repo_name': repo_name_cleaned,
1650 'group_name': parent_group_name,
1651 'group_name': parent_group_name,
1652 # set dummy keys so we always have them
1653 'hostname': '',
1654 'netloc': '',
1655 'scheme': ''
1651 }
1656 }
1657
1658 request = get_current_request()
1659 if request:
1660 # exposes, hostname, netloc, scheme
1661 host_data = get_host_info(request)
1662 named_vars.update(host_data)
1663
1652 # named regex variables
1664 # named regex variables
1653 named_vars.update(match_obj.groupdict())
1665 named_vars.update(match_obj.groupdict())
1654 _url = string.Template(entry['url']).safe_substitute(**named_vars)
1666 _url = string.Template(entry['url']).safe_substitute(**named_vars)
1655 desc = string.Template(entry['desc']).safe_substitute(**named_vars)
1667 desc = string.Template(entry['desc']).safe_substitute(**named_vars)
1668 hovercard_url = string.Template(entry.get('hovercard_url', '')).safe_substitute(**named_vars)
1656
1669
1657 def quote_cleaner(input_str):
1670 def quote_cleaner(input_str):
1658 """Remove quotes as it's HTML"""
1671 """Remove quotes as it's HTML"""
@@ -1666,8 +1679,9 b' def _process_url_func(match_obj, repo_na'
1666 'issue-prefix': entry['pref'],
1679 'issue-prefix': entry['pref'],
1667 'serv': entry['url'],
1680 'serv': entry['url'],
1668 'title': desc,
1681 'title': desc,
1669 'hovercard_url': ''
1682 'hovercard_url': hovercard_url
1670 }
1683 }
1684
1671 if return_raw_data:
1685 if return_raw_data:
1672 return {
1686 return {
1673 'id': issue_id,
1687 'id': issue_id,
@@ -1690,7 +1704,8 b' def get_active_pattern_entries(repo_name'
1690
1704
1691 def process_patterns(text_string, repo_name, link_format='html', active_entries=None):
1705 def process_patterns(text_string, repo_name, link_format='html', active_entries=None):
1692
1706
1693 allowed_formats = ['html', 'rst', 'markdown']
1707 allowed_formats = ['html', 'rst', 'markdown',
1708 'html+hovercard', 'rst+hovercard', 'markdown+hovercard']
1694 if link_format not in allowed_formats:
1709 if link_format not in allowed_formats:
1695 raise ValueError('Link format can be only one of:{} got {}'.format(
1710 raise ValueError('Link format can be only one of:{} got {}'.format(
1696 allowed_formats, link_format))
1711 allowed_formats, link_format))
@@ -1732,13 +1747,16 b' def process_patterns(text_string, repo_n'
1732
1747
1733 # finally use global replace, eg !123 -> pr-link, those will not catch
1748 # finally use global replace, eg !123 -> pr-link, those will not catch
1734 # if already similar pattern exists
1749 # if already similar pattern exists
1750 server_url = '${scheme}://${netloc}'
1735 pr_entry = {
1751 pr_entry = {
1736 'pref': '!',
1752 'pref': '!',
1737 'url': '/_admin/pull-requests/${id}',
1753 'url': server_url + '/_admin/pull-requests/${id}',
1738 'desc': 'Pull Request !${id}'
1754 'desc': 'Pull Request !${id}',
1755 'hovercard_url': server_url + '/_hovercard/pull_request/${id}'
1739 }
1756 }
1740 pr_url_func = partial(
1757 pr_url_func = partial(
1741 _process_url_func, repo_name=repo_name, entry=pr_entry, uid=None)
1758 _process_url_func, repo_name=repo_name, entry=pr_entry, uid=None,
1759 link_format=link_format+'+hovercard')
1742 new_text = re.compile(r'(?:(?:^!)|(?: !))(\d+)').sub(pr_url_func, new_text)
1760 new_text = re.compile(r'(?:(?:^!)|(?: !))(\d+)').sub(pr_url_func, new_text)
1743 log.debug('processed !pr pattern')
1761 log.debug('processed !pr pattern')
1744
1762
@@ -629,9 +629,29 b' def credentials_filter(uri):'
629 return ''.join(uri)
629 return ''.join(uri)
630
630
631
631
632 def get_host_info(request):
633 """
634 Generate host info, to obtain full url e.g https://server.com
635 use this
636 `{scheme}://{netloc}`
637 """
638 if not request:
639 return {}
640
641 qualified_home_url = request.route_url('home')
642 parsed_url = urlobject.URLObject(qualified_home_url)
643 decoded_path = safe_unicode(urllib.unquote(parsed_url.path.rstrip('/')))
644
645 return {
646 'scheme': parsed_url.scheme,
647 'netloc': parsed_url.netloc+decoded_path,
648 'hostname': parsed_url.hostname,
649 }
650
651
632 def get_clone_url(request, uri_tmpl, repo_name, repo_id, **override):
652 def get_clone_url(request, uri_tmpl, repo_name, repo_id, **override):
633 qualifed_home_url = request.route_url('home')
653 qualified_home_url = request.route_url('home')
634 parsed_url = urlobject.URLObject(qualifed_home_url)
654 parsed_url = urlobject.URLObject(qualified_home_url)
635 decoded_path = safe_unicode(urllib.unquote(parsed_url.path.rstrip('/')))
655 decoded_path = safe_unicode(urllib.unquote(parsed_url.path.rstrip('/')))
636
656
637 args = {
657 args = {
@@ -27,6 +27,10 b' a { cursor: pointer; }'
27 display: block;
27 display: block;
28 }
28 }
29
29
30 .clear-both {
31 clear: both;
32 }
33
30 .pull-right {
34 .pull-right {
31 float: right !important;
35 float: right !important;
32 }
36 }
@@ -2895,3 +2895,19 b' form.markup-form {'
2895 clear: both;
2895 clear: both;
2896 min-height: 10px;
2896 min-height: 10px;
2897 }
2897 }
2898
2899 .pr-hovercard-header {
2900 clear: both;
2901 display: block;
2902 line-height: 20px;
2903 }
2904
2905 .pr-hovercard-user {
2906 display: flex;
2907 align-items: center;
2908 padding-left: 5px;
2909 }
2910
2911 .pr-hovercard-title {
2912 padding-top: 5px;
2913 } No newline at end of file
@@ -32,6 +32,7 b' function registerRCRoutes() {'
32 pyroutes.register('repo_integrations_edit', '/%(repo_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_name', 'integration', 'integration_id']);
32 pyroutes.register('repo_integrations_edit', '/%(repo_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_name', 'integration', 'integration_id']);
33 pyroutes.register('hovercard_user', '/_hovercard/user/%(user_id)s', ['user_id']);
33 pyroutes.register('hovercard_user', '/_hovercard/user/%(user_id)s', ['user_id']);
34 pyroutes.register('hovercard_user_group', '/_hovercard/user_group/%(user_group_id)s', ['user_group_id']);
34 pyroutes.register('hovercard_user_group', '/_hovercard/user_group/%(user_group_id)s', ['user_group_id']);
35 pyroutes.register('hovercard_pull_request', '/_hovercard/pull_request/%(pull_request_id)s', ['pull_request_id']);
35 pyroutes.register('hovercard_repo_commit', '/_hovercard/commit/%(repo_name)s/%(commit_id)s', ['repo_name', 'commit_id']);
36 pyroutes.register('hovercard_repo_commit', '/_hovercard/commit/%(repo_name)s/%(commit_id)s', ['repo_name', 'commit_id']);
36 pyroutes.register('ops_ping', '/_admin/ops/ping', []);
37 pyroutes.register('ops_ping', '/_admin/ops/ping', []);
37 pyroutes.register('ops_error_test', '/_admin/ops/error', []);
38 pyroutes.register('ops_error_test', '/_admin/ops/error', []);
General Comments 0
You need to be logged in to leave comments. Login now