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 | 30 | pattern='/_hovercard/user_group/{user_group_id}') |
|
31 | 31 | |
|
32 | 32 | config.add_route( |
|
33 | name='hovercard_pull_request', | |
|
34 | pattern='/_hovercard/pull_request/{pull_request_id}') | |
|
35 | ||
|
36 | config.add_route( | |
|
33 | 37 | name='hovercard_repo_commit', |
|
34 | 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 | 35 | from rhodecode.lib.ext_json import json |
|
36 | 36 | from rhodecode.lib.vcs.nodes import FileNode |
|
37 | 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 | 39 | from rhodecode.model.repo import RepoModel |
|
40 | 40 | from rhodecode.model.repo_group import RepoGroupModel |
|
41 | 41 | from rhodecode.model.scm import RepoGroupList, RepoList |
@@ -71,6 +71,19 b' class HoverCardsView(BaseAppView):' | |||
|
71 | 71 | c.user_group = UserGroup.get_or_404(user_group_id) |
|
72 | 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 | 88 | class HoverCardsRepoView(RepoAppView): |
|
76 | 89 | def load_default_context(self): |
@@ -74,9 +74,10 b' from webhelpers2.number import format_by' | |||
|
74 | 74 | from rhodecode.lib.action_parser import action_parser |
|
75 | 75 | from rhodecode.lib.ext_json import json |
|
76 | 76 | from rhodecode.lib.utils import repo_name_slug, get_custom_lexer |
|
77 |
from rhodecode.lib.utils2 import |
|
|
78 | get_commit_safe, datetime_to_time, time_to_datetime, time_to_utcdatetime, \ | |
|
79 | AttributeDict, safe_int, md5, md5_safe | |
|
77 | from rhodecode.lib.utils2 import ( | |
|
78 | str2bool, safe_unicode, safe_str, | |
|
79 | get_commit_safe, datetime_to_time, time_to_datetime, time_to_utcdatetime, | |
|
80 | AttributeDict, safe_int, md5, md5_safe, get_host_info) | |
|
80 | 81 | from rhodecode.lib.markup_renderer import MarkupRenderer, relative_links |
|
81 | 82 | from rhodecode.lib.vcs.exceptions import CommitDoesNotExistError |
|
82 | 83 | from rhodecode.lib.vcs.backends.base import BaseChangeset, EmptyCommit |
@@ -1632,10 +1633,10 b' def _process_url_func(match_obj, repo_na' | |||
|
1632 | 1633 | '%(pref)s<a class="tooltip-hovercard %(cls)s" href="%(url)s" data-hovercard-url="%(hovercard_url)s">' |
|
1633 | 1634 | '%(issue-prefix)s%(id-repr)s' |
|
1634 | 1635 | '</a>') |
|
1635 |
elif link_format |
|
|
1636 | elif link_format in ['rst', 'rst+hovercard']: | |
|
1636 | 1637 | tmpl = '`%(issue-prefix)s%(id-repr)s <%(url)s>`_' |
|
1637 |
elif link_format |
|
|
1638 | tmpl = '[%(issue-prefix)s%(id-repr)s](%(url)s)' | |
|
1638 | elif link_format in ['markdown', 'markdown+hovercard']: | |
|
1639 | tmpl = '[%(pref)s%(issue-prefix)s%(id-repr)s](%(url)s)' | |
|
1639 | 1640 | else: |
|
1640 | 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 | 1649 | 'repo': repo_name, |
|
1649 | 1650 | 'repo_name': repo_name_cleaned, |
|
1650 | 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 | 1664 | # named regex variables |
|
1653 | 1665 | named_vars.update(match_obj.groupdict()) |
|
1654 | 1666 | _url = string.Template(entry['url']).safe_substitute(**named_vars) |
|
1655 | 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 | 1670 | def quote_cleaner(input_str): |
|
1658 | 1671 | """Remove quotes as it's HTML""" |
@@ -1666,8 +1679,9 b' def _process_url_func(match_obj, repo_na' | |||
|
1666 | 1679 | 'issue-prefix': entry['pref'], |
|
1667 | 1680 | 'serv': entry['url'], |
|
1668 | 1681 | 'title': desc, |
|
1669 |
'hovercard_url': |
|
|
1682 | 'hovercard_url': hovercard_url | |
|
1670 | 1683 | } |
|
1684 | ||
|
1671 | 1685 | if return_raw_data: |
|
1672 | 1686 | return { |
|
1673 | 1687 | 'id': issue_id, |
@@ -1690,7 +1704,8 b' def get_active_pattern_entries(repo_name' | |||
|
1690 | 1704 | |
|
1691 | 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 | 1709 | if link_format not in allowed_formats: |
|
1695 | 1710 | raise ValueError('Link format can be only one of:{} got {}'.format( |
|
1696 | 1711 | allowed_formats, link_format)) |
@@ -1732,13 +1747,16 b' def process_patterns(text_string, repo_n' | |||
|
1732 | 1747 | |
|
1733 | 1748 | # finally use global replace, eg !123 -> pr-link, those will not catch |
|
1734 | 1749 | # if already similar pattern exists |
|
1750 | server_url = '${scheme}://${netloc}' | |
|
1735 | 1751 | pr_entry = { |
|
1736 | 1752 | 'pref': '!', |
|
1737 | 'url': '/_admin/pull-requests/${id}', | |
|
1738 | 'desc': 'Pull Request !${id}' | |
|
1753 | 'url': server_url + '/_admin/pull-requests/${id}', | |
|
1754 | 'desc': 'Pull Request !${id}', | |
|
1755 | 'hovercard_url': server_url + '/_hovercard/pull_request/${id}' | |
|
1739 | 1756 | } |
|
1740 | 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 | 1760 | new_text = re.compile(r'(?:(?:^!)|(?: !))(\d+)').sub(pr_url_func, new_text) |
|
1743 | 1761 | log.debug('processed !pr pattern') |
|
1744 | 1762 | |
@@ -2009,7 +2027,7 b' def get_last_path_part(file_node):' | |||
|
2009 | 2027 | |
|
2010 | 2028 | def route_url(*args, **kwargs): |
|
2011 | 2029 | """ |
|
2012 |
Wrapper around pyramids `route_url` (fully qualified url) function. |
|
|
2030 | Wrapper around pyramids `route_url` (fully qualified url) function. | |
|
2013 | 2031 | """ |
|
2014 | 2032 | req = get_current_request() |
|
2015 | 2033 | return req.route_url(*args, **kwargs) |
@@ -629,9 +629,29 b' def credentials_filter(uri):' | |||
|
629 | 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 | 652 | def get_clone_url(request, uri_tmpl, repo_name, repo_id, **override): |
|
633 | qualifed_home_url = request.route_url('home') | |
|
634 | parsed_url = urlobject.URLObject(qualifed_home_url) | |
|
653 | qualified_home_url = request.route_url('home') | |
|
654 | parsed_url = urlobject.URLObject(qualified_home_url) | |
|
635 | 655 | decoded_path = safe_unicode(urllib.unquote(parsed_url.path.rstrip('/'))) |
|
636 | 656 | |
|
637 | 657 | args = { |
@@ -27,6 +27,10 b' a { cursor: pointer; }' | |||
|
27 | 27 | display: block; |
|
28 | 28 | } |
|
29 | 29 | |
|
30 | .clear-both { | |
|
31 | clear: both; | |
|
32 | } | |
|
33 | ||
|
30 | 34 | .pull-right { |
|
31 | 35 | float: right !important; |
|
32 | 36 | } |
@@ -2895,3 +2895,19 b' form.markup-form {' | |||
|
2895 | 2895 | clear: both; |
|
2896 | 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 | 32 | pyroutes.register('repo_integrations_edit', '/%(repo_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_name', 'integration', 'integration_id']); |
|
33 | 33 | pyroutes.register('hovercard_user', '/_hovercard/user/%(user_id)s', ['user_id']); |
|
34 | 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 | 36 | pyroutes.register('hovercard_repo_commit', '/_hovercard/commit/%(repo_name)s/%(commit_id)s', ['repo_name', 'commit_id']); |
|
36 | 37 | pyroutes.register('ops_ping', '/_admin/ops/ping', []); |
|
37 | 38 | pyroutes.register('ops_error_test', '/_admin/ops/error', []); |
General Comments 0
You need to be logged in to leave comments.
Login now