##// END OF EJS Templates
tooltips: small fixes/tests fixes.
marcink -
r4033:f294c7c5 default
parent child Browse files
Show More
@@ -1,141 +1,127 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2019 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 import pytest
22 import pytest
23
23
24 import rhodecode
24 import rhodecode
25 from rhodecode.model.db import Repository
25 from rhodecode.model.db import Repository
26 from rhodecode.model.meta import Session
26 from rhodecode.model.meta import Session
27 from rhodecode.model.repo import RepoModel
27 from rhodecode.model.repo import RepoModel
28 from rhodecode.model.repo_group import RepoGroupModel
28 from rhodecode.model.repo_group import RepoGroupModel
29 from rhodecode.model.settings import SettingsModel
29 from rhodecode.model.settings import SettingsModel
30 from rhodecode.tests import TestController
30 from rhodecode.tests import TestController
31 from rhodecode.tests.fixture import Fixture
31 from rhodecode.tests.fixture import Fixture
32 from rhodecode.lib import helpers as h
32 from rhodecode.lib import helpers as h
33
33
34 fixture = Fixture()
34 fixture = Fixture()
35
35
36
36
37 def route_path(name, **kwargs):
37 def route_path(name, **kwargs):
38 return {
38 return {
39 'home': '/',
39 'home': '/',
40 'repo_group_home': '/{repo_group_name}'
40 'repo_group_home': '/{repo_group_name}'
41 }[name].format(**kwargs)
41 }[name].format(**kwargs)
42
42
43
43
44 class TestHomeController(TestController):
44 class TestHomeController(TestController):
45
45
46 def test_index(self):
46 def test_index(self):
47 self.log_user()
47 self.log_user()
48 response = self.app.get(route_path('home'))
48 response = self.app.get(route_path('home'))
49 # if global permission is set
49 # if global permission is set
50 response.mustcontain('New Repository')
50 response.mustcontain('New Repository')
51
51
52 # search for objects inside the JavaScript JSON
52 # search for objects inside the JavaScript JSON
53 for repo in Repository.getAll():
53 for repo in Repository.getAll():
54 response.mustcontain('"name_raw": "%s"' % repo.repo_name)
54 response.mustcontain('"name_raw": "%s"' % repo.repo_name)
55
55
56 def test_index_contains_statics_with_ver(self):
56 def test_index_contains_statics_with_ver(self):
57 from rhodecode.lib.base import calculate_version_hash
57 from rhodecode.lib.base import calculate_version_hash
58
58
59 self.log_user()
59 self.log_user()
60 response = self.app.get(route_path('home'))
60 response = self.app.get(route_path('home'))
61
61
62 rhodecode_version_hash = calculate_version_hash(
62 rhodecode_version_hash = calculate_version_hash(
63 {'beaker.session.secret': 'test-rc-uytcxaz'})
63 {'beaker.session.secret': 'test-rc-uytcxaz'})
64 response.mustcontain('style.css?ver={0}'.format(rhodecode_version_hash))
64 response.mustcontain('style.css?ver={0}'.format(rhodecode_version_hash))
65 response.mustcontain('scripts.min.js?ver={0}'.format(rhodecode_version_hash))
65 response.mustcontain('scripts.min.js?ver={0}'.format(rhodecode_version_hash))
66
66
67 def test_index_contains_backend_specific_details(self, backend):
67 def test_index_contains_backend_specific_details(self, backend):
68 self.log_user()
68 self.log_user()
69 response = self.app.get(route_path('home'))
69 response = self.app.get(route_path('home'))
70 tip = backend.repo.get_commit().raw_id
70 tip = backend.repo.get_commit().raw_id
71
71
72 # html in javascript variable:
72 # html in javascript variable:
73 response.mustcontain(r'<i class=\"icon-%s\"' % (backend.alias, ))
73 response.mustcontain(r'<i class=\"icon-%s\"' % (backend.alias, ))
74 response.mustcontain(r'href=\"/%s\"' % (backend.repo_name, ))
74 response.mustcontain(r'href=\"/%s\"' % (backend.repo_name, ))
75
75
76 response.mustcontain("""/%s/changeset/%s""" % (backend.repo_name, tip))
76 response.mustcontain("""/%s/changeset/%s""" % (backend.repo_name, tip))
77 response.mustcontain("""Added a symlink""")
77 response.mustcontain("""Added a symlink""")
78
78
79 def test_index_with_anonymous_access_disabled(self):
79 def test_index_with_anonymous_access_disabled(self):
80 with fixture.anon_access(False):
80 with fixture.anon_access(False):
81 response = self.app.get(route_path('home'), status=302)
81 response = self.app.get(route_path('home'), status=302)
82 assert 'login' in response.location
82 assert 'login' in response.location
83
83
84 def test_index_page_on_groups(self, autologin_user, repo_group):
84 def test_index_page_on_groups(self, autologin_user, repo_group):
85 response = self.app.get(route_path('repo_group_home', repo_group_name='gr1'))
85 response = self.app.get(route_path('repo_group_home', repo_group_name='gr1'))
86 response.mustcontain("gr1/repo_in_group")
86 response.mustcontain("gr1/repo_in_group")
87
87
88 def test_index_page_on_group_with_trailing_slash(
88 def test_index_page_on_group_with_trailing_slash(
89 self, autologin_user, repo_group):
89 self, autologin_user, repo_group):
90 response = self.app.get(route_path('repo_group_home', repo_group_name='gr1') + '/')
90 response = self.app.get(route_path('repo_group_home', repo_group_name='gr1') + '/')
91 response.mustcontain("gr1/repo_in_group")
91 response.mustcontain("gr1/repo_in_group")
92
92
93 @pytest.fixture(scope='class')
93 @pytest.fixture(scope='class')
94 def repo_group(self, request):
94 def repo_group(self, request):
95 gr = fixture.create_repo_group('gr1')
95 gr = fixture.create_repo_group('gr1')
96 fixture.create_repo(name='gr1/repo_in_group', repo_group=gr)
96 fixture.create_repo(name='gr1/repo_in_group', repo_group=gr)
97
97
98 @request.addfinalizer
98 @request.addfinalizer
99 def cleanup():
99 def cleanup():
100 RepoModel().delete('gr1/repo_in_group')
100 RepoModel().delete('gr1/repo_in_group')
101 RepoGroupModel().delete(repo_group='gr1', force_delete=True)
101 RepoGroupModel().delete(repo_group='gr1', force_delete=True)
102 Session().commit()
102 Session().commit()
103
103
104 def test_index_with_name_with_tags(self, user_util, autologin_user):
105 user = user_util.create_user()
106 username = user.username
107 user.name = '<img src="/image1" onload="alert(\'Hello, World!\');">'
108 user.lastname = '#"><img src=x onerror=prompt(document.cookie);>'
109
110 Session().add(user)
111 Session().commit()
112 user_util.create_repo(owner=username)
113
114 response = self.app.get(route_path('home'))
115 response.mustcontain(h.html_escape(user.first_name))
116 response.mustcontain(h.html_escape(user.last_name))
117
118 @pytest.mark.parametrize("name, state", [
104 @pytest.mark.parametrize("name, state", [
119 ('Disabled', False),
105 ('Disabled', False),
120 ('Enabled', True),
106 ('Enabled', True),
121 ])
107 ])
122 def test_index_show_version(self, autologin_user, name, state):
108 def test_index_show_version(self, autologin_user, name, state):
123 version_string = 'RhodeCode Enterprise %s' % rhodecode.__version__
109 version_string = 'RhodeCode Enterprise %s' % rhodecode.__version__
124
110
125 sett = SettingsModel().create_or_update_setting(
111 sett = SettingsModel().create_or_update_setting(
126 'show_version', state, 'bool')
112 'show_version', state, 'bool')
127 Session().add(sett)
113 Session().add(sett)
128 Session().commit()
114 Session().commit()
129 SettingsModel().invalidate_settings_cache()
115 SettingsModel().invalidate_settings_cache()
130
116
131 response = self.app.get(route_path('home'))
117 response = self.app.get(route_path('home'))
132 if state is True:
118 if state is True:
133 response.mustcontain(version_string)
119 response.mustcontain(version_string)
134 if state is False:
120 if state is False:
135 response.mustcontain(no=[version_string])
121 response.mustcontain(no=[version_string])
136
122
137 def test_logout_form_contains_csrf(self, autologin_user, csrf_token):
123 def test_logout_form_contains_csrf(self, autologin_user, csrf_token):
138 response = self.app.get(route_path('home'))
124 response = self.app.get(route_path('home'))
139 assert_response = response.assert_response()
125 assert_response = response.assert_response()
140 element = assert_response.get_element('.logout #csrf_token')
126 element = assert_response.get_element('.logout #csrf_token')
141 assert element.value == csrf_token
127 assert element.value == csrf_token
@@ -1,148 +1,148 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2019 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import pytest
21 import pytest
22
22
23 from rhodecode.lib.utils2 import md5
23 from rhodecode.lib.utils2 import md5
24 from rhodecode.model.db import Repository
24 from rhodecode.model.db import Repository
25 from rhodecode.model.meta import Session
25 from rhodecode.model.meta import Session
26 from rhodecode.model.settings import SettingsModel, IssueTrackerSettingsModel
26 from rhodecode.model.settings import SettingsModel, IssueTrackerSettingsModel
27
27
28
28
29 def route_path(name, params=None, **kwargs):
29 def route_path(name, params=None, **kwargs):
30 import urllib
30 import urllib
31
31
32 base_url = {
32 base_url = {
33 'repo_summary': '/{repo_name}',
33 'repo_summary': '/{repo_name}',
34 'edit_repo_issuetracker': '/{repo_name}/settings/issue_trackers',
34 'edit_repo_issuetracker': '/{repo_name}/settings/issue_trackers',
35 'edit_repo_issuetracker_test': '/{repo_name}/settings/issue_trackers/test',
35 'edit_repo_issuetracker_test': '/{repo_name}/settings/issue_trackers/test',
36 'edit_repo_issuetracker_delete': '/{repo_name}/settings/issue_trackers/delete',
36 'edit_repo_issuetracker_delete': '/{repo_name}/settings/issue_trackers/delete',
37 'edit_repo_issuetracker_update': '/{repo_name}/settings/issue_trackers/update',
37 'edit_repo_issuetracker_update': '/{repo_name}/settings/issue_trackers/update',
38 }[name].format(**kwargs)
38 }[name].format(**kwargs)
39
39
40 if params:
40 if params:
41 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
41 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
42 return base_url
42 return base_url
43
43
44
44
45 @pytest.mark.usefixtures("app")
45 @pytest.mark.usefixtures("app")
46 class TestRepoIssueTracker(object):
46 class TestRepoIssueTracker(object):
47 def test_issuetracker_index(self, autologin_user, backend):
47 def test_issuetracker_index(self, autologin_user, backend):
48 repo = backend.create_repo()
48 repo = backend.create_repo()
49 response = self.app.get(route_path('edit_repo_issuetracker',
49 response = self.app.get(route_path('edit_repo_issuetracker',
50 repo_name=repo.repo_name))
50 repo_name=repo.repo_name))
51 assert response.status_code == 200
51 assert response.status_code == 200
52
52
53 def test_add_and_test_issuetracker_patterns(
53 def test_add_and_test_issuetracker_patterns(
54 self, autologin_user, backend, csrf_token, request, xhr_header):
54 self, autologin_user, backend, csrf_token, request, xhr_header):
55 pattern = 'issuetracker_pat'
55 pattern = 'issuetracker_pat'
56 another_pattern = pattern+'1'
56 another_pattern = pattern+'1'
57 post_url = route_path(
57 post_url = route_path(
58 'edit_repo_issuetracker_update', repo_name=backend.repo.repo_name)
58 'edit_repo_issuetracker_update', repo_name=backend.repo.repo_name)
59 post_data = {
59 post_data = {
60 'new_pattern_pattern_0': pattern,
60 'new_pattern_pattern_0': pattern,
61 'new_pattern_url_0': 'http://url',
61 'new_pattern_url_0': 'http://url',
62 'new_pattern_prefix_0': 'prefix',
62 'new_pattern_prefix_0': 'prefix',
63 'new_pattern_description_0': 'description',
63 'new_pattern_description_0': 'description',
64 'new_pattern_pattern_1': another_pattern,
64 'new_pattern_pattern_1': another_pattern,
65 'new_pattern_url_1': '/url1',
65 'new_pattern_url_1': '/url1',
66 'new_pattern_prefix_1': 'prefix1',
66 'new_pattern_prefix_1': 'prefix1',
67 'new_pattern_description_1': 'description1',
67 'new_pattern_description_1': 'description1',
68 'csrf_token': csrf_token
68 'csrf_token': csrf_token
69 }
69 }
70 self.app.post(post_url, post_data, status=302)
70 self.app.post(post_url, post_data, status=302)
71 self.settings_model = IssueTrackerSettingsModel(repo=backend.repo)
71 self.settings_model = IssueTrackerSettingsModel(repo=backend.repo)
72 settings = self.settings_model.get_repo_settings()
72 settings = self.settings_model.get_repo_settings()
73 self.uid = md5(pattern)
73 self.uid = md5(pattern)
74 assert settings[self.uid]['pat'] == pattern
74 assert settings[self.uid]['pat'] == pattern
75 self.another_uid = md5(another_pattern)
75 self.another_uid = md5(another_pattern)
76 assert settings[self.another_uid]['pat'] == another_pattern
76 assert settings[self.another_uid]['pat'] == another_pattern
77
77
78 # test pattern
78 # test pattern
79 data = {'test_text': 'example of issuetracker_pat replacement',
79 data = {'test_text': 'example of issuetracker_pat replacement',
80 'csrf_token': csrf_token}
80 'csrf_token': csrf_token}
81 response = self.app.post(
81 response = self.app.post(
82 route_path('edit_repo_issuetracker_test',
82 route_path('edit_repo_issuetracker_test',
83 repo_name=backend.repo.repo_name),
83 repo_name=backend.repo.repo_name),
84 extra_environ=xhr_header, params=data)
84 extra_environ=xhr_header, params=data)
85
85
86 assert response.body == \
86 assert response.body == \
87 'example of <a class="issue-tracker-link" href="http://url">prefix</a> replacement'
87 'example of <a class="tooltip issue-tracker-link" href="http://url" title="description">prefix</a> replacement'
88
88
89 @request.addfinalizer
89 @request.addfinalizer
90 def cleanup():
90 def cleanup():
91 self.settings_model.delete_entries(self.uid)
91 self.settings_model.delete_entries(self.uid)
92 self.settings_model.delete_entries(self.another_uid)
92 self.settings_model.delete_entries(self.another_uid)
93
93
94 def test_edit_issuetracker_pattern(
94 def test_edit_issuetracker_pattern(
95 self, autologin_user, backend, csrf_token, request):
95 self, autologin_user, backend, csrf_token, request):
96 entry_key = 'issuetracker_pat_'
96 entry_key = 'issuetracker_pat_'
97 pattern = 'issuetracker_pat2'
97 pattern = 'issuetracker_pat2'
98 old_pattern = 'issuetracker_pat'
98 old_pattern = 'issuetracker_pat'
99 old_uid = md5(old_pattern)
99 old_uid = md5(old_pattern)
100
100
101 sett = SettingsModel(repo=backend.repo).create_or_update_setting(
101 sett = SettingsModel(repo=backend.repo).create_or_update_setting(
102 entry_key+old_uid, old_pattern, 'unicode')
102 entry_key+old_uid, old_pattern, 'unicode')
103 Session().add(sett)
103 Session().add(sett)
104 Session().commit()
104 Session().commit()
105 post_url = route_path(
105 post_url = route_path(
106 'edit_repo_issuetracker_update', repo_name=backend.repo.repo_name)
106 'edit_repo_issuetracker_update', repo_name=backend.repo.repo_name)
107 post_data = {
107 post_data = {
108 'new_pattern_pattern_0': pattern,
108 'new_pattern_pattern_0': pattern,
109 'new_pattern_url_0': '/url',
109 'new_pattern_url_0': '/url',
110 'new_pattern_prefix_0': 'prefix',
110 'new_pattern_prefix_0': 'prefix',
111 'new_pattern_description_0': 'description',
111 'new_pattern_description_0': 'description',
112 'uid': old_uid,
112 'uid': old_uid,
113 'csrf_token': csrf_token
113 'csrf_token': csrf_token
114 }
114 }
115 self.app.post(post_url, post_data, status=302)
115 self.app.post(post_url, post_data, status=302)
116 self.settings_model = IssueTrackerSettingsModel(repo=backend.repo)
116 self.settings_model = IssueTrackerSettingsModel(repo=backend.repo)
117 settings = self.settings_model.get_repo_settings()
117 settings = self.settings_model.get_repo_settings()
118 self.uid = md5(pattern)
118 self.uid = md5(pattern)
119 assert settings[self.uid]['pat'] == pattern
119 assert settings[self.uid]['pat'] == pattern
120 with pytest.raises(KeyError):
120 with pytest.raises(KeyError):
121 key = settings[old_uid]
121 key = settings[old_uid]
122
122
123 @request.addfinalizer
123 @request.addfinalizer
124 def cleanup():
124 def cleanup():
125 self.settings_model.delete_entries(self.uid)
125 self.settings_model.delete_entries(self.uid)
126
126
127 def test_delete_issuetracker_pattern(
127 def test_delete_issuetracker_pattern(
128 self, autologin_user, backend, csrf_token, settings_util):
128 self, autologin_user, backend, csrf_token, settings_util):
129 repo = backend.create_repo()
129 repo = backend.create_repo()
130 repo_name = repo.repo_name
130 repo_name = repo.repo_name
131 entry_key = 'issuetracker_pat_'
131 entry_key = 'issuetracker_pat_'
132 pattern = 'issuetracker_pat3'
132 pattern = 'issuetracker_pat3'
133 uid = md5(pattern)
133 uid = md5(pattern)
134 settings_util.create_repo_rhodecode_setting(
134 settings_util.create_repo_rhodecode_setting(
135 repo=backend.repo, name=entry_key+uid,
135 repo=backend.repo, name=entry_key+uid,
136 value=entry_key, type_='unicode', cleanup=False)
136 value=entry_key, type_='unicode', cleanup=False)
137
137
138 self.app.post(
138 self.app.post(
139 route_path(
139 route_path(
140 'edit_repo_issuetracker_delete',
140 'edit_repo_issuetracker_delete',
141 repo_name=backend.repo.repo_name),
141 repo_name=backend.repo.repo_name),
142 {
142 {
143 'uid': uid,
143 'uid': uid,
144 'csrf_token': csrf_token
144 'csrf_token': csrf_token
145 }, status=302)
145 }, status=302)
146 settings = IssueTrackerSettingsModel(
146 settings = IssueTrackerSettingsModel(
147 repo=Repository.get_by_repo_name(repo_name)).get_repo_settings()
147 repo=Repository.get_by_repo_name(repo_name)).get_repo_settings()
148 assert 'rhodecode_%s%s' % (entry_key, uid) not in settings
148 assert 'rhodecode_%s%s' % (entry_key, uid) not in settings
@@ -1,469 +1,469 b''
1 ## DATA TABLE RE USABLE ELEMENTS
1 ## DATA TABLE RE USABLE ELEMENTS
2 ## usage:
2 ## usage:
3 ## <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
3 ## <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
4 <%namespace name="base" file="/base/base.mako"/>
4 <%namespace name="base" file="/base/base.mako"/>
5
5
6 <%def name="metatags_help()">
6 <%def name="metatags_help()">
7 <table>
7 <table>
8 <%
8 <%
9 example_tags = [
9 example_tags = [
10 ('state','[stable]'),
10 ('state','[stable]'),
11 ('state','[stale]'),
11 ('state','[stale]'),
12 ('state','[featured]'),
12 ('state','[featured]'),
13 ('state','[dev]'),
13 ('state','[dev]'),
14 ('state','[dead]'),
14 ('state','[dead]'),
15 ('state','[deprecated]'),
15 ('state','[deprecated]'),
16
16
17 ('label','[personal]'),
17 ('label','[personal]'),
18 ('generic','[v2.0.0]'),
18 ('generic','[v2.0.0]'),
19
19
20 ('lang','[lang =&gt; JavaScript]'),
20 ('lang','[lang =&gt; JavaScript]'),
21 ('license','[license =&gt; LicenseName]'),
21 ('license','[license =&gt; LicenseName]'),
22
22
23 ('ref','[requires =&gt; RepoName]'),
23 ('ref','[requires =&gt; RepoName]'),
24 ('ref','[recommends =&gt; GroupName]'),
24 ('ref','[recommends =&gt; GroupName]'),
25 ('ref','[conflicts =&gt; SomeName]'),
25 ('ref','[conflicts =&gt; SomeName]'),
26 ('ref','[base =&gt; SomeName]'),
26 ('ref','[base =&gt; SomeName]'),
27 ('url','[url =&gt; [linkName](https://rhodecode.com)]'),
27 ('url','[url =&gt; [linkName](https://rhodecode.com)]'),
28 ('see','[see =&gt; http://rhodecode.com]'),
28 ('see','[see =&gt; http://rhodecode.com]'),
29 ]
29 ]
30 %>
30 %>
31 % for tag_type, tag in example_tags:
31 % for tag_type, tag in example_tags:
32 <tr>
32 <tr>
33 <td>${tag|n}</td>
33 <td>${tag|n}</td>
34 <td>${h.style_metatag(tag_type, tag)|n}</td>
34 <td>${h.style_metatag(tag_type, tag)|n}</td>
35 </tr>
35 </tr>
36 % endfor
36 % endfor
37 </table>
37 </table>
38 </%def>
38 </%def>
39
39
40 <%def name="render_description(description, stylify_metatags)">
40 <%def name="render_description(description, stylify_metatags)">
41 <%
41 <%
42 tags = []
42 tags = []
43 if stylify_metatags:
43 if stylify_metatags:
44 tags, description = h.extract_metatags(description)
44 tags, description = h.extract_metatags(description)
45 %>
45 %>
46 % for tag_type, tag in tags:
46 % for tag_type, tag in tags:
47 ${h.style_metatag(tag_type, tag)|n,trim}
47 ${h.style_metatag(tag_type, tag)|n,trim}
48 % endfor
48 % endfor
49 <code style="white-space: pre-wrap">${description}</code>
49 <code style="white-space: pre-wrap">${description}</code>
50 </%def>
50 </%def>
51
51
52 ## REPOSITORY RENDERERS
52 ## REPOSITORY RENDERERS
53 <%def name="quick_menu(repo_name)">
53 <%def name="quick_menu(repo_name)">
54 <i class="icon-more"></i>
54 <i class="icon-more"></i>
55 <div class="menu_items_container hidden">
55 <div class="menu_items_container hidden">
56 <ul class="menu_items">
56 <ul class="menu_items">
57 <li>
57 <li>
58 <a title="${_('Summary')}" href="${h.route_path('repo_summary',repo_name=repo_name)}">
58 <a title="${_('Summary')}" href="${h.route_path('repo_summary',repo_name=repo_name)}">
59 <span>${_('Summary')}</span>
59 <span>${_('Summary')}</span>
60 </a>
60 </a>
61 </li>
61 </li>
62 <li>
62 <li>
63 <a title="${_('Commits')}" href="${h.route_path('repo_commits',repo_name=repo_name)}">
63 <a title="${_('Commits')}" href="${h.route_path('repo_commits',repo_name=repo_name)}">
64 <span>${_('Commits')}</span>
64 <span>${_('Commits')}</span>
65 </a>
65 </a>
66 </li>
66 </li>
67 <li>
67 <li>
68 <a title="${_('Files')}" href="${h.route_path('repo_files:default_commit',repo_name=repo_name)}">
68 <a title="${_('Files')}" href="${h.route_path('repo_files:default_commit',repo_name=repo_name)}">
69 <span>${_('Files')}</span>
69 <span>${_('Files')}</span>
70 </a>
70 </a>
71 </li>
71 </li>
72 <li>
72 <li>
73 <a title="${_('Fork')}" href="${h.route_path('repo_fork_new',repo_name=repo_name)}">
73 <a title="${_('Fork')}" href="${h.route_path('repo_fork_new',repo_name=repo_name)}">
74 <span>${_('Fork')}</span>
74 <span>${_('Fork')}</span>
75 </a>
75 </a>
76 </li>
76 </li>
77 </ul>
77 </ul>
78 </div>
78 </div>
79 </%def>
79 </%def>
80
80
81 <%def name="repo_name(name,rtype,rstate,private,archived,fork_of,short_name=False,admin=False)">
81 <%def name="repo_name(name,rtype,rstate,private,archived,fork_of,short_name=False,admin=False)">
82 <%
82 <%
83 def get_name(name,short_name=short_name):
83 def get_name(name,short_name=short_name):
84 if short_name:
84 if short_name:
85 return name.split('/')[-1]
85 return name.split('/')[-1]
86 else:
86 else:
87 return name
87 return name
88 %>
88 %>
89 <div class="${'repo_state_pending' if rstate == 'repo_state_pending' else ''} truncate">
89 <div class="${'repo_state_pending' if rstate == 'repo_state_pending' else ''} truncate">
90 ##NAME
90 ##NAME
91 <a href="${h.route_path('edit_repo',repo_name=name) if admin else h.route_path('repo_summary',repo_name=name)}">
91 <a href="${h.route_path('edit_repo',repo_name=name) if admin else h.route_path('repo_summary',repo_name=name)}">
92
92
93 ##TYPE OF REPO
93 ##TYPE OF REPO
94 %if h.is_hg(rtype):
94 %if h.is_hg(rtype):
95 <span title="${_('Mercurial repository')}"><i class="icon-hg" style="font-size: 14px;"></i></span>
95 <span title="${_('Mercurial repository')}"><i class="icon-hg" style="font-size: 14px;"></i></span>
96 %elif h.is_git(rtype):
96 %elif h.is_git(rtype):
97 <span title="${_('Git repository')}"><i class="icon-git" style="font-size: 14px"></i></span>
97 <span title="${_('Git repository')}"><i class="icon-git" style="font-size: 14px"></i></span>
98 %elif h.is_svn(rtype):
98 %elif h.is_svn(rtype):
99 <span title="${_('Subversion repository')}"><i class="icon-svn" style="font-size: 14px"></i></span>
99 <span title="${_('Subversion repository')}"><i class="icon-svn" style="font-size: 14px"></i></span>
100 %endif
100 %endif
101
101
102 ##PRIVATE/PUBLIC
102 ##PRIVATE/PUBLIC
103 %if private is True and c.visual.show_private_icon:
103 %if private is True and c.visual.show_private_icon:
104 <i class="icon-lock" title="${_('Private repository')}"></i>
104 <i class="icon-lock" title="${_('Private repository')}"></i>
105 %elif private is False and c.visual.show_public_icon:
105 %elif private is False and c.visual.show_public_icon:
106 <i class="icon-unlock-alt" title="${_('Public repository')}"></i>
106 <i class="icon-unlock-alt" title="${_('Public repository')}"></i>
107 %else:
107 %else:
108 <span></span>
108 <span></span>
109 %endif
109 %endif
110 ${get_name(name)}
110 ${get_name(name)}
111 </a>
111 </a>
112 %if fork_of:
112 %if fork_of:
113 <a href="${h.route_path('repo_summary',repo_name=fork_of.repo_name)}"><i class="icon-code-fork"></i></a>
113 <a href="${h.route_path('repo_summary',repo_name=fork_of.repo_name)}"><i class="icon-code-fork"></i></a>
114 %endif
114 %endif
115 %if rstate == 'repo_state_pending':
115 %if rstate == 'repo_state_pending':
116 <span class="creation_in_progress tooltip" title="${_('This repository is being created in a background task')}">
116 <span class="creation_in_progress tooltip" title="${_('This repository is being created in a background task')}">
117 (${_('creating...')})
117 (${_('creating...')})
118 </span>
118 </span>
119 %endif
119 %endif
120
120
121 </div>
121 </div>
122 </%def>
122 </%def>
123
123
124 <%def name="repo_desc(description, stylify_metatags)">
124 <%def name="repo_desc(description, stylify_metatags)">
125 <%
125 <%
126 tags, description = h.extract_metatags(description)
126 tags, description = h.extract_metatags(description)
127 %>
127 %>
128
128
129 <div class="truncate-wrap">
129 <div class="truncate-wrap">
130 % if stylify_metatags:
130 % if stylify_metatags:
131 % for tag_type, tag in tags:
131 % for tag_type, tag in tags:
132 ${h.style_metatag(tag_type, tag)|n}
132 ${h.style_metatag(tag_type, tag)|n}
133 % endfor
133 % endfor
134 % endif
134 % endif
135 ${description}
135 ${description}
136 </div>
136 </div>
137
137
138 </%def>
138 </%def>
139
139
140 <%def name="last_change(last_change)">
140 <%def name="last_change(last_change)">
141 ${h.age_component(last_change, time_is_local=True)}
141 ${h.age_component(last_change, time_is_local=True)}
142 </%def>
142 </%def>
143
143
144 <%def name="revision(repo_name, rev, commit_id, author, last_msg, commit_date)">
144 <%def name="revision(repo_name, rev, commit_id, author, last_msg, commit_date)">
145 <div>
145 <div>
146 %if rev >= 0:
146 %if rev >= 0:
147 <code><a class="tooltip-hovercard" data-hovercard-alt="${last_msg}" data-hovercard-url="${h.route_path('hovercard_repo_commit', repo_name=repo_name, commit_id=commit_id)}" href="${h.route_path('repo_commit',repo_name=repo_name,commit_id=commit_id)}">${'r{}:{}'.format(rev,h.short_id(commit_id))}</a></code>
147 <code><a class="tooltip-hovercard" data-hovercard-alt=${h.tooltip(last_msg)} data-hovercard-url="${h.route_path('hovercard_repo_commit', repo_name=repo_name, commit_id=commit_id)}" href="${h.route_path('repo_commit',repo_name=repo_name,commit_id=commit_id)}">${'r{}:{}'.format(rev,h.short_id(commit_id))}</a></code>
148 %else:
148 %else:
149 ${_('No commits yet')}
149 ${_('No commits yet')}
150 %endif
150 %endif
151 </div>
151 </div>
152 </%def>
152 </%def>
153
153
154 <%def name="rss(name)">
154 <%def name="rss(name)">
155 %if c.rhodecode_user.username != h.DEFAULT_USER:
155 %if c.rhodecode_user.username != h.DEFAULT_USER:
156 <a title="${h.tooltip(_('Subscribe to %s rss feed')% name)}" href="${h.route_path('rss_feed_home', repo_name=name, _query=dict(auth_token=c.rhodecode_user.feed_token))}"><i class="icon-rss-sign"></i></a>
156 <a title="${h.tooltip(_('Subscribe to %s rss feed')% name)}" href="${h.route_path('rss_feed_home', repo_name=name, _query=dict(auth_token=c.rhodecode_user.feed_token))}"><i class="icon-rss-sign"></i></a>
157 %else:
157 %else:
158 <a title="${h.tooltip(_('Subscribe to %s rss feed')% name)}" href="${h.route_path('rss_feed_home', repo_name=name)}"><i class="icon-rss-sign"></i></a>
158 <a title="${h.tooltip(_('Subscribe to %s rss feed')% name)}" href="${h.route_path('rss_feed_home', repo_name=name)}"><i class="icon-rss-sign"></i></a>
159 %endif
159 %endif
160 </%def>
160 </%def>
161
161
162 <%def name="atom(name)">
162 <%def name="atom(name)">
163 %if c.rhodecode_user.username != h.DEFAULT_USER:
163 %if c.rhodecode_user.username != h.DEFAULT_USER:
164 <a title="${h.tooltip(_('Subscribe to %s atom feed')% name)}" href="${h.route_path('atom_feed_home', repo_name=name, _query=dict(auth_token=c.rhodecode_user.feed_token))}"><i class="icon-rss-sign"></i></a>
164 <a title="${h.tooltip(_('Subscribe to %s atom feed')% name)}" href="${h.route_path('atom_feed_home', repo_name=name, _query=dict(auth_token=c.rhodecode_user.feed_token))}"><i class="icon-rss-sign"></i></a>
165 %else:
165 %else:
166 <a title="${h.tooltip(_('Subscribe to %s atom feed')% name)}" href="${h.route_path('atom_feed_home', repo_name=name)}"><i class="icon-rss-sign"></i></a>
166 <a title="${h.tooltip(_('Subscribe to %s atom feed')% name)}" href="${h.route_path('atom_feed_home', repo_name=name)}"><i class="icon-rss-sign"></i></a>
167 %endif
167 %endif
168 </%def>
168 </%def>
169
169
170 <%def name="repo_actions(repo_name, super_user=True)">
170 <%def name="repo_actions(repo_name, super_user=True)">
171 <div>
171 <div>
172 <div class="grid_edit">
172 <div class="grid_edit">
173 <a href="${h.route_path('edit_repo',repo_name=repo_name)}" title="${_('Edit')}">
173 <a href="${h.route_path('edit_repo',repo_name=repo_name)}" title="${_('Edit')}">
174 Edit
174 Edit
175 </a>
175 </a>
176 </div>
176 </div>
177 <div class="grid_delete">
177 <div class="grid_delete">
178 ${h.secure_form(h.route_path('edit_repo_advanced_delete', repo_name=repo_name), request=request)}
178 ${h.secure_form(h.route_path('edit_repo_advanced_delete', repo_name=repo_name), request=request)}
179 ${h.submit('remove_%s' % repo_name,_('Delete'),class_="btn btn-link btn-danger",
179 ${h.submit('remove_%s' % repo_name,_('Delete'),class_="btn btn-link btn-danger",
180 onclick="return confirm('"+_('Confirm to delete this repository: %s') % repo_name+"');")}
180 onclick="return confirm('"+_('Confirm to delete this repository: %s') % repo_name+"');")}
181 ${h.end_form()}
181 ${h.end_form()}
182 </div>
182 </div>
183 </div>
183 </div>
184 </%def>
184 </%def>
185
185
186 <%def name="repo_state(repo_state)">
186 <%def name="repo_state(repo_state)">
187 <div>
187 <div>
188 %if repo_state == 'repo_state_pending':
188 %if repo_state == 'repo_state_pending':
189 <div class="tag tag4">${_('Creating')}</div>
189 <div class="tag tag4">${_('Creating')}</div>
190 %elif repo_state == 'repo_state_created':
190 %elif repo_state == 'repo_state_created':
191 <div class="tag tag1">${_('Created')}</div>
191 <div class="tag tag1">${_('Created')}</div>
192 %else:
192 %else:
193 <div class="tag alert2" title="${h.tooltip(repo_state)}">invalid</div>
193 <div class="tag alert2" title="${h.tooltip(repo_state)}">invalid</div>
194 %endif
194 %endif
195 </div>
195 </div>
196 </%def>
196 </%def>
197
197
198
198
199 ## REPO GROUP RENDERERS
199 ## REPO GROUP RENDERERS
200 <%def name="quick_repo_group_menu(repo_group_name)">
200 <%def name="quick_repo_group_menu(repo_group_name)">
201 <i class="icon-more"></i>
201 <i class="icon-more"></i>
202 <div class="menu_items_container hidden">
202 <div class="menu_items_container hidden">
203 <ul class="menu_items">
203 <ul class="menu_items">
204 <li>
204 <li>
205 <a href="${h.route_path('repo_group_home', repo_group_name=repo_group_name)}">${_('Summary')}</a>
205 <a href="${h.route_path('repo_group_home', repo_group_name=repo_group_name)}">${_('Summary')}</a>
206 </li>
206 </li>
207
207
208 </ul>
208 </ul>
209 </div>
209 </div>
210 </%def>
210 </%def>
211
211
212 <%def name="repo_group_name(repo_group_name, children_groups=None)">
212 <%def name="repo_group_name(repo_group_name, children_groups=None)">
213 <div>
213 <div>
214 <a href="${h.route_path('repo_group_home', repo_group_name=repo_group_name)}">
214 <a href="${h.route_path('repo_group_home', repo_group_name=repo_group_name)}">
215 <i class="icon-repo-group" title="${_('Repository group')}" style="font-size: 14px"></i>
215 <i class="icon-repo-group" title="${_('Repository group')}" style="font-size: 14px"></i>
216 %if children_groups:
216 %if children_groups:
217 ${h.literal(' &raquo; '.join(children_groups))}
217 ${h.literal(' &raquo; '.join(children_groups))}
218 %else:
218 %else:
219 ${repo_group_name}
219 ${repo_group_name}
220 %endif
220 %endif
221 </a>
221 </a>
222 </div>
222 </div>
223 </%def>
223 </%def>
224
224
225 <%def name="repo_group_desc(description, personal, stylify_metatags)">
225 <%def name="repo_group_desc(description, personal, stylify_metatags)">
226
226
227 <%
227 <%
228 if stylify_metatags:
228 if stylify_metatags:
229 tags, description = h.extract_metatags(description)
229 tags, description = h.extract_metatags(description)
230 %>
230 %>
231
231
232 <div class="truncate-wrap">
232 <div class="truncate-wrap">
233 % if personal:
233 % if personal:
234 <div class="metatag" tag="personal">${_('personal')}</div>
234 <div class="metatag" tag="personal">${_('personal')}</div>
235 % endif
235 % endif
236
236
237 % if stylify_metatags:
237 % if stylify_metatags:
238 % for tag_type, tag in tags:
238 % for tag_type, tag in tags:
239 ${h.style_metatag(tag_type, tag)|n}
239 ${h.style_metatag(tag_type, tag)|n}
240 % endfor
240 % endfor
241 % endif
241 % endif
242 ${description}
242 ${description}
243 </div>
243 </div>
244
244
245 </%def>
245 </%def>
246
246
247 <%def name="repo_group_actions(repo_group_id, repo_group_name, gr_count)">
247 <%def name="repo_group_actions(repo_group_id, repo_group_name, gr_count)">
248 <div class="grid_edit">
248 <div class="grid_edit">
249 <a href="${h.route_path('edit_repo_group',repo_group_name=repo_group_name)}" title="${_('Edit')}">Edit</a>
249 <a href="${h.route_path('edit_repo_group',repo_group_name=repo_group_name)}" title="${_('Edit')}">Edit</a>
250 </div>
250 </div>
251 <div class="grid_delete">
251 <div class="grid_delete">
252 ${h.secure_form(h.route_path('edit_repo_group_advanced_delete', repo_group_name=repo_group_name), request=request)}
252 ${h.secure_form(h.route_path('edit_repo_group_advanced_delete', repo_group_name=repo_group_name), request=request)}
253 ${h.submit('remove_%s' % repo_group_name,_('Delete'),class_="btn btn-link btn-danger",
253 ${h.submit('remove_%s' % repo_group_name,_('Delete'),class_="btn btn-link btn-danger",
254 onclick="return confirm('"+_ungettext('Confirm to delete this group: %s with %s repository','Confirm to delete this group: %s with %s repositories',gr_count) % (repo_group_name, gr_count)+"');")}
254 onclick="return confirm('"+_ungettext('Confirm to delete this group: %s with %s repository','Confirm to delete this group: %s with %s repositories',gr_count) % (repo_group_name, gr_count)+"');")}
255 ${h.end_form()}
255 ${h.end_form()}
256 </div>
256 </div>
257 </%def>
257 </%def>
258
258
259
259
260 <%def name="user_actions(user_id, username)">
260 <%def name="user_actions(user_id, username)">
261 <div class="grid_edit">
261 <div class="grid_edit">
262 <a href="${h.route_path('user_edit',user_id=user_id)}" title="${_('Edit')}">
262 <a href="${h.route_path('user_edit',user_id=user_id)}" title="${_('Edit')}">
263 ${_('Edit')}
263 ${_('Edit')}
264 </a>
264 </a>
265 </div>
265 </div>
266 <div class="grid_delete">
266 <div class="grid_delete">
267 ${h.secure_form(h.route_path('user_delete', user_id=user_id), request=request)}
267 ${h.secure_form(h.route_path('user_delete', user_id=user_id), request=request)}
268 ${h.submit('remove_',_('Delete'),id="remove_user_%s" % user_id, class_="btn btn-link btn-danger",
268 ${h.submit('remove_',_('Delete'),id="remove_user_%s" % user_id, class_="btn btn-link btn-danger",
269 onclick="return confirm('"+_('Confirm to delete this user: %s') % username+"');")}
269 onclick="return confirm('"+_('Confirm to delete this user: %s') % username+"');")}
270 ${h.end_form()}
270 ${h.end_form()}
271 </div>
271 </div>
272 </%def>
272 </%def>
273
273
274 <%def name="user_group_actions(user_group_id, user_group_name)">
274 <%def name="user_group_actions(user_group_id, user_group_name)">
275 <div class="grid_edit">
275 <div class="grid_edit">
276 <a href="${h.route_path('edit_user_group', user_group_id=user_group_id)}" title="${_('Edit')}">Edit</a>
276 <a href="${h.route_path('edit_user_group', user_group_id=user_group_id)}" title="${_('Edit')}">Edit</a>
277 </div>
277 </div>
278 <div class="grid_delete">
278 <div class="grid_delete">
279 ${h.secure_form(h.route_path('user_groups_delete', user_group_id=user_group_id), request=request)}
279 ${h.secure_form(h.route_path('user_groups_delete', user_group_id=user_group_id), request=request)}
280 ${h.submit('remove_',_('Delete'),id="remove_group_%s" % user_group_id, class_="btn btn-link btn-danger",
280 ${h.submit('remove_',_('Delete'),id="remove_group_%s" % user_group_id, class_="btn btn-link btn-danger",
281 onclick="return confirm('"+_('Confirm to delete this user group: %s') % user_group_name+"');")}
281 onclick="return confirm('"+_('Confirm to delete this user group: %s') % user_group_name+"');")}
282 ${h.end_form()}
282 ${h.end_form()}
283 </div>
283 </div>
284 </%def>
284 </%def>
285
285
286
286
287 <%def name="user_name(user_id, username)">
287 <%def name="user_name(user_id, username)">
288 ${h.link_to(h.person(username, 'username_or_name_or_email'), h.route_path('user_edit', user_id=user_id))}
288 ${h.link_to(h.person(username, 'username_or_name_or_email'), h.route_path('user_edit', user_id=user_id))}
289 </%def>
289 </%def>
290
290
291 <%def name="user_profile(username)">
291 <%def name="user_profile(username)">
292 ${base.gravatar_with_user(username, 16, tooltip=True)}
292 ${base.gravatar_with_user(username, 16, tooltip=True)}
293 </%def>
293 </%def>
294
294
295 <%def name="user_group_name(user_group_name)">
295 <%def name="user_group_name(user_group_name)">
296 <div>
296 <div>
297 <i class="icon-user-group" title="${_('User group')}"></i>
297 <i class="icon-user-group" title="${_('User group')}"></i>
298 ${h.link_to_group(user_group_name)}
298 ${h.link_to_group(user_group_name)}
299 </div>
299 </div>
300 </%def>
300 </%def>
301
301
302
302
303 ## GISTS
303 ## GISTS
304
304
305 <%def name="gist_gravatar(full_contact)">
305 <%def name="gist_gravatar(full_contact)">
306 <div class="gist_gravatar">
306 <div class="gist_gravatar">
307 ${base.gravatar(full_contact, 30)}
307 ${base.gravatar(full_contact, 30)}
308 </div>
308 </div>
309 </%def>
309 </%def>
310
310
311 <%def name="gist_access_id(gist_access_id, full_contact)">
311 <%def name="gist_access_id(gist_access_id, full_contact)">
312 <div>
312 <div>
313 <b>
313 <b>
314 <a href="${h.route_path('gist_show', gist_id=gist_access_id)}">gist: ${gist_access_id}</a>
314 <a href="${h.route_path('gist_show', gist_id=gist_access_id)}">gist: ${gist_access_id}</a>
315 </b>
315 </b>
316 </div>
316 </div>
317 </%def>
317 </%def>
318
318
319 <%def name="gist_author(full_contact, created_on, expires)">
319 <%def name="gist_author(full_contact, created_on, expires)">
320 ${base.gravatar_with_user(full_contact, 16, tooltip=True)}
320 ${base.gravatar_with_user(full_contact, 16, tooltip=True)}
321 </%def>
321 </%def>
322
322
323
323
324 <%def name="gist_created(created_on)">
324 <%def name="gist_created(created_on)">
325 <div class="created">
325 <div class="created">
326 ${h.age_component(created_on, time_is_local=True)}
326 ${h.age_component(created_on, time_is_local=True)}
327 </div>
327 </div>
328 </%def>
328 </%def>
329
329
330 <%def name="gist_expires(expires)">
330 <%def name="gist_expires(expires)">
331 <div class="created">
331 <div class="created">
332 %if expires == -1:
332 %if expires == -1:
333 ${_('never')}
333 ${_('never')}
334 %else:
334 %else:
335 ${h.age_component(h.time_to_utcdatetime(expires))}
335 ${h.age_component(h.time_to_utcdatetime(expires))}
336 %endif
336 %endif
337 </div>
337 </div>
338 </%def>
338 </%def>
339
339
340 <%def name="gist_type(gist_type)">
340 <%def name="gist_type(gist_type)">
341 %if gist_type != 'public':
341 %if gist_type != 'public':
342 <div class="tag">${_('Private')}</div>
342 <div class="tag">${_('Private')}</div>
343 %endif
343 %endif
344 </%def>
344 </%def>
345
345
346 <%def name="gist_description(gist_description)">
346 <%def name="gist_description(gist_description)">
347 ${gist_description}
347 ${gist_description}
348 </%def>
348 </%def>
349
349
350
350
351 ## PULL REQUESTS GRID RENDERERS
351 ## PULL REQUESTS GRID RENDERERS
352
352
353 <%def name="pullrequest_target_repo(repo_name)">
353 <%def name="pullrequest_target_repo(repo_name)">
354 <div class="truncate">
354 <div class="truncate">
355 ${h.link_to(repo_name,h.route_path('repo_summary',repo_name=repo_name))}
355 ${h.link_to(repo_name,h.route_path('repo_summary',repo_name=repo_name))}
356 </div>
356 </div>
357 </%def>
357 </%def>
358
358
359 <%def name="pullrequest_status(status)">
359 <%def name="pullrequest_status(status)">
360 <i class="icon-circle review-status-${status}"></i>
360 <i class="icon-circle review-status-${status}"></i>
361 </%def>
361 </%def>
362
362
363 <%def name="pullrequest_title(title, description)">
363 <%def name="pullrequest_title(title, description)">
364 ${title}
364 ${title}
365 </%def>
365 </%def>
366
366
367 <%def name="pullrequest_comments(comments_nr)">
367 <%def name="pullrequest_comments(comments_nr)">
368 <i class="icon-comment"></i> ${comments_nr}
368 <i class="icon-comment"></i> ${comments_nr}
369 </%def>
369 </%def>
370
370
371 <%def name="pullrequest_name(pull_request_id, target_repo_name, short=False)">
371 <%def name="pullrequest_name(pull_request_id, target_repo_name, short=False)">
372 <a href="${h.route_path('pullrequest_show',repo_name=target_repo_name,pull_request_id=pull_request_id)}">
372 <a href="${h.route_path('pullrequest_show',repo_name=target_repo_name,pull_request_id=pull_request_id)}">
373 % if short:
373 % if short:
374 #${pull_request_id}
374 #${pull_request_id}
375 % else:
375 % else:
376 ${_('Pull request #%(pr_number)s') % {'pr_number': pull_request_id,}}
376 ${_('Pull request #%(pr_number)s') % {'pr_number': pull_request_id,}}
377 % endif
377 % endif
378 </a>
378 </a>
379 </%def>
379 </%def>
380
380
381 <%def name="pullrequest_updated_on(updated_on)">
381 <%def name="pullrequest_updated_on(updated_on)">
382 ${h.age_component(h.time_to_utcdatetime(updated_on))}
382 ${h.age_component(h.time_to_utcdatetime(updated_on))}
383 </%def>
383 </%def>
384
384
385 <%def name="pullrequest_author(full_contact)">
385 <%def name="pullrequest_author(full_contact)">
386 ${base.gravatar_with_user(full_contact, 16, tooltip=True)}
386 ${base.gravatar_with_user(full_contact, 16, tooltip=True)}
387 </%def>
387 </%def>
388
388
389
389
390 ## ARTIFACT RENDERERS
390 ## ARTIFACT RENDERERS
391 <%def name="repo_artifact_name(repo_name, file_uid, artifact_display_name)">
391 <%def name="repo_artifact_name(repo_name, file_uid, artifact_display_name)">
392 <a href="${h.route_path('repo_artifacts_get', repo_name=repo_name, uid=file_uid)}">
392 <a href="${h.route_path('repo_artifacts_get', repo_name=repo_name, uid=file_uid)}">
393 ${artifact_display_name or '_EMPTY_NAME_'}
393 ${artifact_display_name or '_EMPTY_NAME_'}
394 </a>
394 </a>
395 </%def>
395 </%def>
396
396
397 <%def name="repo_artifact_uid(repo_name, file_uid)">
397 <%def name="repo_artifact_uid(repo_name, file_uid)">
398 <code>${h.shorter(file_uid, size=24, prefix=True)}</code>
398 <code>${h.shorter(file_uid, size=24, prefix=True)}</code>
399 <i class="tooltip icon-clipboard clipboard-action" data-clipboard-text="${h.route_url('repo_artifacts_get', repo_name=repo_name, uid=file_uid)}" title="${_('Copy the full url')}"></i>
399 <i class="tooltip icon-clipboard clipboard-action" data-clipboard-text="${h.route_url('repo_artifacts_get', repo_name=repo_name, uid=file_uid)}" title="${_('Copy the full url')}"></i>
400 </%def>
400 </%def>
401
401
402 <%def name="repo_artifact_sha256(artifact_sha256)">
402 <%def name="repo_artifact_sha256(artifact_sha256)">
403 <div class="code">${h.shorter(artifact_sha256, 12)}<i class="tooltip icon-clipboard clipboard-action" data-clipboard-text="${artifact_sha256}" title="${_('Copy the sha256 ({})').format(artifact_sha256)}"></i></div>
403 <div class="code">${h.shorter(artifact_sha256, 12)}<i class="tooltip icon-clipboard clipboard-action" data-clipboard-text="${artifact_sha256}" title="${_('Copy the sha256 ({})').format(artifact_sha256)}"></i></div>
404 </%def>
404 </%def>
405
405
406 <%def name="repo_artifact_actions(repo_name, file_store_id, file_uid)">
406 <%def name="repo_artifact_actions(repo_name, file_store_id, file_uid)">
407 ## <div class="grid_edit">
407 ## <div class="grid_edit">
408 ## <a href="#Edit" title="${_('Edit')}">${_('Edit')}</a>
408 ## <a href="#Edit" title="${_('Edit')}">${_('Edit')}</a>
409 ## </div>
409 ## </div>
410 <div class="grid_edit">
410 <div class="grid_edit">
411 <a href="${h.route_path('repo_artifacts_info', repo_name=repo_name, uid=file_store_id)}" title="${_('Info')}">${_('Info')}</a>
411 <a href="${h.route_path('repo_artifacts_info', repo_name=repo_name, uid=file_store_id)}" title="${_('Info')}">${_('Info')}</a>
412 </div>
412 </div>
413 % if h.HasRepoPermissionAny('repository.admin')(c.repo_name):
413 % if h.HasRepoPermissionAny('repository.admin')(c.repo_name):
414 <div class="grid_delete">
414 <div class="grid_delete">
415 ${h.secure_form(h.route_path('repo_artifacts_delete', repo_name=repo_name, uid=file_store_id), request=request)}
415 ${h.secure_form(h.route_path('repo_artifacts_delete', repo_name=repo_name, uid=file_store_id), request=request)}
416 ${h.submit('remove_',_('Delete'),id="remove_artifact_%s" % file_store_id, class_="btn btn-link btn-danger",
416 ${h.submit('remove_',_('Delete'),id="remove_artifact_%s" % file_store_id, class_="btn btn-link btn-danger",
417 onclick="return confirm('"+_('Confirm to delete this artifact: %s') % file_uid+"');")}
417 onclick="return confirm('"+_('Confirm to delete this artifact: %s') % file_uid+"');")}
418 ${h.end_form()}
418 ${h.end_form()}
419 </div>
419 </div>
420 % endif
420 % endif
421 </%def>
421 </%def>
422
422
423 <%def name="markup_form(form_id, form_text='', help_text=None)">
423 <%def name="markup_form(form_id, form_text='', help_text=None)">
424
424
425 <div class="markup-form">
425 <div class="markup-form">
426 <div class="markup-form-area">
426 <div class="markup-form-area">
427 <div class="markup-form-area-header">
427 <div class="markup-form-area-header">
428 <ul class="nav-links clearfix">
428 <ul class="nav-links clearfix">
429 <li class="active">
429 <li class="active">
430 <a href="#edit-text" tabindex="-1" id="edit-btn_${form_id}">${_('Write')}</a>
430 <a href="#edit-text" tabindex="-1" id="edit-btn_${form_id}">${_('Write')}</a>
431 </li>
431 </li>
432 <li class="">
432 <li class="">
433 <a href="#preview-text" tabindex="-1" id="preview-btn_${form_id}">${_('Preview')}</a>
433 <a href="#preview-text" tabindex="-1" id="preview-btn_${form_id}">${_('Preview')}</a>
434 </li>
434 </li>
435 </ul>
435 </ul>
436 </div>
436 </div>
437
437
438 <div class="markup-form-area-write" style="display: block;">
438 <div class="markup-form-area-write" style="display: block;">
439 <div id="edit-container_${form_id}">
439 <div id="edit-container_${form_id}">
440 <textarea id="${form_id}" name="${form_id}" class="comment-block-ta ac-input">${form_text if form_text else ''}</textarea>
440 <textarea id="${form_id}" name="${form_id}" class="comment-block-ta ac-input">${form_text if form_text else ''}</textarea>
441 </div>
441 </div>
442 <div id="preview-container_${form_id}" class="clearfix" style="display: none;">
442 <div id="preview-container_${form_id}" class="clearfix" style="display: none;">
443 <div id="preview-box_${form_id}" class="preview-box"></div>
443 <div id="preview-box_${form_id}" class="preview-box"></div>
444 </div>
444 </div>
445 </div>
445 </div>
446
446
447 <div class="markup-form-area-footer">
447 <div class="markup-form-area-footer">
448 <div class="toolbar">
448 <div class="toolbar">
449 <div class="toolbar-text">
449 <div class="toolbar-text">
450 ${(_('Parsed using %s syntax') % (
450 ${(_('Parsed using %s syntax') % (
451 ('<a href="%s">%s</a>' % (h.route_url('%s_help' % c.visual.default_renderer), c.visual.default_renderer.upper())),
451 ('<a href="%s">%s</a>' % (h.route_url('%s_help' % c.visual.default_renderer), c.visual.default_renderer.upper())),
452 )
452 )
453 )|n}
453 )|n}
454 </div>
454 </div>
455 </div>
455 </div>
456 </div>
456 </div>
457 </div>
457 </div>
458
458
459 <div class="markup-form-footer">
459 <div class="markup-form-footer">
460 % if help_text:
460 % if help_text:
461 <span class="help-block">${help_text}</span>
461 <span class="help-block">${help_text}</span>
462 % endif
462 % endif
463 </div>
463 </div>
464 </div>
464 </div>
465 <script type="text/javascript">
465 <script type="text/javascript">
466 new MarkupForm('${form_id}');
466 new MarkupForm('${form_id}');
467 </script>
467 </script>
468
468
469 </%def>
469 </%def>
@@ -1,235 +1,239 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2019 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import copy
21 import copy
22 import mock
22 import mock
23 import pytest
23 import pytest
24
24
25 from rhodecode.lib import helpers
25 from rhodecode.lib import helpers
26 from rhodecode.lib.utils2 import AttributeDict
26 from rhodecode.lib.utils2 import AttributeDict
27 from rhodecode.model.settings import IssueTrackerSettingsModel
27 from rhodecode.model.settings import IssueTrackerSettingsModel
28 from rhodecode.tests import no_newline_id_generator
28 from rhodecode.tests import no_newline_id_generator
29
29
30
30
31 @pytest.mark.parametrize('url, expected_url', [
31 @pytest.mark.parametrize('url, expected_url', [
32 ('http://rc.rc/test', '<a href="http://rc.rc/test">http://rc.rc/test</a>'),
32 ('http://rc.rc/test', '<a href="http://rc.rc/test">http://rc.rc/test</a>'),
33 ('http://rc.rc/@foo', '<a href="http://rc.rc/@foo">http://rc.rc/@foo</a>'),
33 ('http://rc.rc/@foo', '<a href="http://rc.rc/@foo">http://rc.rc/@foo</a>'),
34 ('http://rc.rc/!foo', '<a href="http://rc.rc/!foo">http://rc.rc/!foo</a>'),
34 ('http://rc.rc/!foo', '<a href="http://rc.rc/!foo">http://rc.rc/!foo</a>'),
35 ('http://rc.rc/&foo', '<a href="http://rc.rc/&foo">http://rc.rc/&foo</a>'),
35 ('http://rc.rc/&foo', '<a href="http://rc.rc/&foo">http://rc.rc/&foo</a>'),
36 ('http://rc.rc/#foo', '<a href="http://rc.rc/#foo">http://rc.rc/#foo</a>'),
36 ('http://rc.rc/#foo', '<a href="http://rc.rc/#foo">http://rc.rc/#foo</a>'),
37 ])
37 ])
38 def test_urlify_text(url, expected_url):
38 def test_urlify_text(url, expected_url):
39 assert helpers.urlify_text(url) == expected_url
39 assert helpers.urlify_text(url) == expected_url
40
40
41
41
42 @pytest.mark.parametrize('repo_name, commit_id, path, expected_result', [
42 @pytest.mark.parametrize('repo_name, commit_id, path, expected_result', [
43 # Simple case 1
43 # Simple case 1
44 ('repo', 'commit', 'a/b',
44 ('repo', 'commit', 'a/b',
45 '<a href="/repo/files/commit/"><i class="icon-home"></i></a>'
45 '<a href="/repo/files/commit/"><i class="icon-home"></i></a>'
46 ' / '
46 ' / '
47 '<a href="/repo/files/commit/a">a</a>'
47 '<a href="/repo/files/commit/a">a</a>'
48 ' / '
48 ' / '
49 'b'),
49 'b'),
50
50
51 # Simple case
51 # Simple case
52 ('rX<X', 'cX<X', 'pX<X/aX<X/bX<X',
52 ('rX<X', 'cX<X', 'pX<X/aX<X/bX<X',
53 '<a href="/rX%3CX/files/cX%3CX/"><i class="icon-home"></i></a>'
53 '<a href="/rX%3CX/files/cX%3CX/"><i class="icon-home"></i></a>'
54 ' / '
54 ' / '
55 '<a href="/rX%3CX/files/cX%3CX/pX%3CX">pX&lt;X</a>'
55 '<a href="/rX%3CX/files/cX%3CX/pX%3CX">pX&lt;X</a>'
56 ' / '
56 ' / '
57 '<a href="/rX%3CX/files/cX%3CX/pX%3CX/aX%3CX">aX&lt;X</a>'
57 '<a href="/rX%3CX/files/cX%3CX/pX%3CX/aX%3CX">aX&lt;X</a>'
58 ' / '
58 ' / '
59 'bX&lt;X'),
59 'bX&lt;X'),
60
60
61 # Path with only one segment
61 # Path with only one segment
62 ('rX<X', 'cX<X', 'pX<X',
62 ('rX<X', 'cX<X', 'pX<X',
63 '<a href="/rX%3CX/files/cX%3CX/"><i class="icon-home"></i></a>'
63 '<a href="/rX%3CX/files/cX%3CX/"><i class="icon-home"></i></a>'
64 ' / '
64 ' / '
65 'pX&lt;X'),
65 'pX&lt;X'),
66
66
67 # Empty path
67 # Empty path
68 ('rX<X', 'cX<X', '',
68 ('rX<X', 'cX<X', '',
69 '<i class="icon-home"></i>'),
69 '<i class="icon-home"></i>'),
70
70
71 # simple quote
71 # simple quote
72 ('rX"X', 'cX"X', 'pX"X/aX"X/bX"X',
72 ('rX"X', 'cX"X', 'pX"X/aX"X/bX"X',
73 '<a href="/rX%22X/files/cX%22X/"><i class="icon-home"></i></a>'
73 '<a href="/rX%22X/files/cX%22X/"><i class="icon-home"></i></a>'
74 ' / '
74 ' / '
75 '<a href="/rX%22X/files/cX%22X/pX%22X">pX&#34;X</a>'
75 '<a href="/rX%22X/files/cX%22X/pX%22X">pX&#34;X</a>'
76 ' / '
76 ' / '
77 '<a href="/rX%22X/files/cX%22X/pX%22X/aX%22X">aX&#34;X</a>'
77 '<a href="/rX%22X/files/cX%22X/pX%22X/aX%22X">aX&#34;X</a>'
78 ' / '
78 ' / '
79 'bX&#34;X'),
79 'bX&#34;X'),
80
80
81 ], ids=['simple1', 'simple2', 'one_segment', 'empty_path', 'simple_quote'])
81 ], ids=['simple1', 'simple2', 'one_segment', 'empty_path', 'simple_quote'])
82 def test_files_breadcrumbs_xss(
82 def test_files_breadcrumbs_xss(
83 repo_name, commit_id, path, app, expected_result):
83 repo_name, commit_id, path, app, expected_result):
84 result = helpers.files_breadcrumbs(repo_name, commit_id, path)
84 result = helpers.files_breadcrumbs(repo_name, commit_id, path)
85 # Expect it to encode all path fragments properly. This is important
85 # Expect it to encode all path fragments properly. This is important
86 # because it returns an instance of `literal`.
86 # because it returns an instance of `literal`.
87 if path != '':
87 if path != '':
88 expected_result = expected_result + helpers.files_icon.format(helpers.escape(path))
88 expected_result = expected_result + helpers.files_icon.format(helpers.escape(path))
89 assert result == expected_result
89 assert result == expected_result
90
90
91
91
92 def test_format_binary():
92 def test_format_binary():
93 assert helpers.format_byte_size_binary(298489462784) == '278.0 GiB'
93 assert helpers.format_byte_size_binary(298489462784) == '278.0 GiB'
94
94
95
95
96 @pytest.mark.parametrize('text_string, pattern, expected', [
96 @pytest.mark.parametrize('text_string, pattern, expected', [
97 ('No issue here', '(?:#)(?P<issue_id>\d+)', []),
97 ('No issue here', '(?:#)(?P<issue_id>\d+)', []),
98 ('Fix #42', '(?:#)(?P<issue_id>\d+)',
98 ('Fix #42', '(?:#)(?P<issue_id>\d+)',
99 [{'url': 'http://r.io/{repo}/i/42', 'id': '42'}]),
99 [{'url': 'http://r.io/{repo}/i/42', 'id': '42'}]),
100 ('Fix #42, #53', '(?:#)(?P<issue_id>\d+)', [
100 ('Fix #42, #53', '(?:#)(?P<issue_id>\d+)', [
101 {'url': 'http://r.io/{repo}/i/42', 'id': '42'},
101 {'url': 'http://r.io/{repo}/i/42', 'id': '42'},
102 {'url': 'http://r.io/{repo}/i/53', 'id': '53'}]),
102 {'url': 'http://r.io/{repo}/i/53', 'id': '53'}]),
103 ('Fix #42', '(?:#)?<issue_id>\d+)', []), # Broken regex
103 ('Fix #42', '(?:#)?<issue_id>\d+)', []), # Broken regex
104 ])
104 ])
105 def test_extract_issues(backend, text_string, pattern, expected):
105 def test_extract_issues(backend, text_string, pattern, expected):
106 repo = backend.create_repo()
106 repo = backend.create_repo()
107 config = {
107 config = {
108 '123': {
108 '123': {
109 'uid': '123',
109 'uid': '123',
110 'pat': pattern,
110 'pat': pattern,
111 'url': 'http://r.io/${repo}/i/${issue_id}',
111 'url': 'http://r.io/${repo}/i/${issue_id}',
112 'pref': '#',
112 'pref': '#',
113 'desc': 'Test Pattern'
113 }
114 }
114 }
115 }
115
116
116 def get_settings_mock(self, cache=True):
117 def get_settings_mock(self, cache=True):
117 return config
118 return config
118
119
119 with mock.patch.object(IssueTrackerSettingsModel,
120 with mock.patch.object(IssueTrackerSettingsModel,
120 'get_settings', get_settings_mock):
121 'get_settings', get_settings_mock):
121 text, issues = helpers.process_patterns(text_string, repo.repo_name)
122 text, issues = helpers.process_patterns(text_string, repo.repo_name)
122
123
123 expected = copy.deepcopy(expected)
124 expected = copy.deepcopy(expected)
124 for item in expected:
125 for item in expected:
125 item['url'] = item['url'].format(repo=repo.repo_name)
126 item['url'] = item['url'].format(repo=repo.repo_name)
126
127
127 assert issues == expected
128 assert issues == expected
128
129
129
130
130 @pytest.mark.parametrize('text_string, pattern, link_format, expected_text', [
131 @pytest.mark.parametrize('text_string, pattern, link_format, expected_text', [
131 ('Fix #42', '(?:#)(?P<issue_id>\d+)', 'html',
132 ('Fix #42', '(?:#)(?P<issue_id>\d+)', 'html',
132 'Fix <a class="issue-tracker-link" href="http://r.io/{repo}/i/42">#42</a>'),
133 'Fix <a class="tooltip issue-tracker-link" href="http://r.io/{repo}/i/42" title="Test Pattern">#42</a>'),
133
134
134 ('Fix #42', '(?:#)(?P<issue_id>\d+)', 'markdown',
135 ('Fix #42', '(?:#)(?P<issue_id>\d+)', 'markdown',
135 'Fix [#42](http://r.io/{repo}/i/42)'),
136 'Fix [#42](http://r.io/{repo}/i/42)'),
136
137
137 ('Fix #42', '(?:#)(?P<issue_id>\d+)', 'rst',
138 ('Fix #42', '(?:#)(?P<issue_id>\d+)', 'rst',
138 'Fix `#42 <http://r.io/{repo}/i/42>`_'),
139 'Fix `#42 <http://r.io/{repo}/i/42>`_'),
139
140
140 ('Fix #42', '(?:#)?<issue_id>\d+)', 'html',
141 ('Fix #42', '(?:#)?<issue_id>\d+)', 'html',
141 'Fix #42'), # Broken regex
142 'Fix #42'), # Broken regex
142 ])
143 ])
143 def test_process_patterns_repo(backend, text_string, pattern, expected_text, link_format):
144 def test_process_patterns_repo(backend, text_string, pattern, expected_text, link_format):
144 repo = backend.create_repo()
145 repo = backend.create_repo()
145
146
146 def get_settings_mock(self, cache=True):
147 def get_settings_mock(self, cache=True):
147 return {
148 return {
148 '123': {
149 '123': {
149 'uid': '123',
150 'uid': '123',
150 'pat': pattern,
151 'pat': pattern,
151 'url': 'http://r.io/${repo}/i/${issue_id}',
152 'url': 'http://r.io/${repo}/i/${issue_id}',
152 'pref': '#',
153 'pref': '#',
154 'desc': 'Test Pattern'
153 }
155 }
154 }
156 }
155
157
156 with mock.patch.object(IssueTrackerSettingsModel,
158 with mock.patch.object(IssueTrackerSettingsModel,
157 'get_settings', get_settings_mock):
159 'get_settings', get_settings_mock):
158 processed_text, issues = helpers.process_patterns(
160 processed_text, issues = helpers.process_patterns(
159 text_string, repo.repo_name, link_format)
161 text_string, repo.repo_name, link_format)
160
162
161 assert processed_text == expected_text.format(repo=repo.repo_name)
163 assert processed_text == expected_text.format(repo=repo.repo_name)
162
164
163
165
164 @pytest.mark.parametrize('text_string, pattern, expected_text', [
166 @pytest.mark.parametrize('text_string, pattern, expected_text', [
165 ('Fix #42', '(?:#)(?P<issue_id>\d+)',
167 ('Fix #42', '(?:#)(?P<issue_id>\d+)',
166 'Fix <a class="issue-tracker-link" href="http://r.io/i/42">#42</a>'),
168 'Fix <a class="tooltip issue-tracker-link" href="http://r.io/i/42" title="Test Pattern">#42</a>'),
167 ('Fix #42', '(?:#)?<issue_id>\d+)',
169 ('Fix #42', '(?:#)?<issue_id>\d+)',
168 'Fix #42'), # Broken regex
170 'Fix #42'), # Broken regex
169 ])
171 ])
170 def test_process_patterns_no_repo(text_string, pattern, expected_text):
172 def test_process_patterns_no_repo(text_string, pattern, expected_text):
171
173
172 def get_settings_mock(self, cache=True):
174 def get_settings_mock(self, cache=True):
173 return {
175 return {
174 '123': {
176 '123': {
175 'uid': '123',
177 'uid': '123',
176 'pat': pattern,
178 'pat': pattern,
177 'url': 'http://r.io/i/${issue_id}',
179 'url': 'http://r.io/i/${issue_id}',
178 'pref': '#',
180 'pref': '#',
181 'desc': 'Test Pattern'
179 }
182 }
180 }
183 }
181
184
182 with mock.patch.object(IssueTrackerSettingsModel,
185 with mock.patch.object(IssueTrackerSettingsModel,
183 'get_global_settings', get_settings_mock):
186 'get_global_settings', get_settings_mock):
184 processed_text, issues = helpers.process_patterns(
187 processed_text, issues = helpers.process_patterns(
185 text_string, '')
188 text_string, '')
186
189
187 assert processed_text == expected_text
190 assert processed_text == expected_text
188
191
189
192
190 def test_process_patterns_non_existent_repo_name(backend):
193 def test_process_patterns_non_existent_repo_name(backend):
191 text_string = 'Fix #42'
194 text_string = 'Fix #42'
192 pattern = '(?:#)(?P<issue_id>\d+)'
195 pattern = '(?:#)(?P<issue_id>\d+)'
193 expected_text = ('Fix <a class="issue-tracker-link" '
196 expected_text = ('Fix <a class="tooltip issue-tracker-link" '
194 'href="http://r.io/do-not-exist/i/42">#42</a>')
197 'href="http://r.io/do-not-exist/i/42" title="Test Pattern">#42</a>')
195
198
196 def get_settings_mock(self, cache=True):
199 def get_settings_mock(self, cache=True):
197 return {
200 return {
198 '123': {
201 '123': {
199 'uid': '123',
202 'uid': '123',
200 'pat': pattern,
203 'pat': pattern,
201 'url': 'http://r.io/${repo}/i/${issue_id}',
204 'url': 'http://r.io/${repo}/i/${issue_id}',
202 'pref': '#',
205 'pref': '#',
206 'desc': 'Test Pattern'
203 }
207 }
204 }
208 }
205
209
206 with mock.patch.object(IssueTrackerSettingsModel,
210 with mock.patch.object(IssueTrackerSettingsModel,
207 'get_global_settings', get_settings_mock):
211 'get_global_settings', get_settings_mock):
208 processed_text, issues = helpers.process_patterns(
212 processed_text, issues = helpers.process_patterns(
209 text_string, 'do-not-exist')
213 text_string, 'do-not-exist')
210
214
211 assert processed_text == expected_text
215 assert processed_text == expected_text
212
216
213
217
214 def test_get_visual_attr(baseapp):
218 def test_get_visual_attr(baseapp):
215 from rhodecode.apps._base import TemplateArgs
219 from rhodecode.apps._base import TemplateArgs
216 c = TemplateArgs()
220 c = TemplateArgs()
217 assert None is helpers.get_visual_attr(c, 'fakse')
221 assert None is helpers.get_visual_attr(c, 'fakse')
218
222
219 # emulate the c.visual behaviour
223 # emulate the c.visual behaviour
220 c.visual = AttributeDict({})
224 c.visual = AttributeDict({})
221 assert None is helpers.get_visual_attr(c, 'some_var')
225 assert None is helpers.get_visual_attr(c, 'some_var')
222
226
223 c.visual.some_var = 'foobar'
227 c.visual.some_var = 'foobar'
224 assert 'foobar' == helpers.get_visual_attr(c, 'some_var')
228 assert 'foobar' == helpers.get_visual_attr(c, 'some_var')
225
229
226
230
227 @pytest.mark.parametrize('test_text, inclusive, expected_text', [
231 @pytest.mark.parametrize('test_text, inclusive, expected_text', [
228 ('just a string', False, 'just a string'),
232 ('just a string', False, 'just a string'),
229 ('just a string\n', False, 'just a string'),
233 ('just a string\n', False, 'just a string'),
230 ('just a string\n next line', False, 'just a string...'),
234 ('just a string\n next line', False, 'just a string...'),
231 ('just a string\n next line', True, 'just a string\n...'),
235 ('just a string\n next line', True, 'just a string\n...'),
232 ], ids=no_newline_id_generator)
236 ], ids=no_newline_id_generator)
233 def test_chop_at(test_text, inclusive, expected_text):
237 def test_chop_at(test_text, inclusive, expected_text):
234 assert helpers.chop_at_smart(
238 assert helpers.chop_at_smart(
235 test_text, '\n', inclusive, '...') == expected_text
239 test_text, '\n', inclusive, '...') == expected_text
General Comments 0
You need to be logged in to leave comments. Login now