Show More
@@ -0,0 +1,302 b'' | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | ||||
|
3 | # Copyright (C) 2010-2017 RhodeCode GmbH | |||
|
4 | # | |||
|
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 | |||
|
7 | # (only), as published by the Free Software Foundation. | |||
|
8 | # | |||
|
9 | # This program is distributed in the hope that it will be useful, | |||
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
|
12 | # GNU General Public License for more details. | |||
|
13 | # | |||
|
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/>. | |||
|
16 | # | |||
|
17 | # This program is dual-licensed. If you wish to learn more about the | |||
|
18 | # RhodeCode Enterprise Edition, including its added features, Support services, | |||
|
19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ | |||
|
20 | ||||
|
21 | ||||
|
22 | import logging | |||
|
23 | ||||
|
24 | from pyramid.httpexceptions import HTTPNotFound, HTTPFound | |||
|
25 | from pyramid.view import view_config | |||
|
26 | from pyramid.renderers import render | |||
|
27 | from pyramid.response import Response | |||
|
28 | ||||
|
29 | from rhodecode.apps._base import RepoAppView | |||
|
30 | import rhodecode.lib.helpers as h | |||
|
31 | from rhodecode.lib.auth import ( | |||
|
32 | LoginRequired, HasRepoPermissionAnyDecorator) | |||
|
33 | ||||
|
34 | from rhodecode.lib.ext_json import json | |||
|
35 | from rhodecode.lib.graphmod import _colored, _dagwalker | |||
|
36 | from rhodecode.lib.helpers import RepoPage | |||
|
37 | from rhodecode.lib.utils2 import safe_int, safe_str | |||
|
38 | from rhodecode.lib.vcs.exceptions import ( | |||
|
39 | RepositoryError, CommitDoesNotExistError, | |||
|
40 | CommitError, NodeDoesNotExistError, EmptyRepositoryError) | |||
|
41 | ||||
|
42 | log = logging.getLogger(__name__) | |||
|
43 | ||||
|
44 | DEFAULT_CHANGELOG_SIZE = 20 | |||
|
45 | ||||
|
46 | ||||
|
47 | class RepoChangelogView(RepoAppView): | |||
|
48 | ||||
|
49 | def _get_commit_or_redirect(self, commit_id, redirect_after=True): | |||
|
50 | """ | |||
|
51 | This is a safe way to get commit. If an error occurs it redirects to | |||
|
52 | tip with proper message | |||
|
53 | ||||
|
54 | :param commit_id: id of commit to fetch | |||
|
55 | :param redirect_after: toggle redirection | |||
|
56 | """ | |||
|
57 | _ = self.request.translate | |||
|
58 | ||||
|
59 | try: | |||
|
60 | return self.rhodecode_vcs_repo.get_commit(commit_id) | |||
|
61 | except EmptyRepositoryError: | |||
|
62 | if not redirect_after: | |||
|
63 | return None | |||
|
64 | ||||
|
65 | h.flash(h.literal( | |||
|
66 | _('There are no commits yet')), category='warning') | |||
|
67 | raise HTTPFound( | |||
|
68 | h.route_path('repo_summary', repo_name=self.db_repo_name)) | |||
|
69 | ||||
|
70 | except (CommitDoesNotExistError, LookupError): | |||
|
71 | msg = _('No such commit exists for this repository') | |||
|
72 | h.flash(msg, category='error') | |||
|
73 | raise HTTPNotFound() | |||
|
74 | except RepositoryError as e: | |||
|
75 | h.flash(safe_str(h.escape(e)), category='error') | |||
|
76 | raise HTTPNotFound() | |||
|
77 | ||||
|
78 | def _graph(self, repo, commits, prev_data=None, next_data=None): | |||
|
79 | """ | |||
|
80 | Generates a DAG graph for repo | |||
|
81 | ||||
|
82 | :param repo: repo instance | |||
|
83 | :param commits: list of commits | |||
|
84 | """ | |||
|
85 | if not commits: | |||
|
86 | return json.dumps([]) | |||
|
87 | ||||
|
88 | def serialize(commit, parents=True): | |||
|
89 | data = dict( | |||
|
90 | raw_id=commit.raw_id, | |||
|
91 | idx=commit.idx, | |||
|
92 | branch=commit.branch, | |||
|
93 | ) | |||
|
94 | if parents: | |||
|
95 | data['parents'] = [ | |||
|
96 | serialize(x, parents=False) for x in commit.parents] | |||
|
97 | return data | |||
|
98 | ||||
|
99 | prev_data = prev_data or [] | |||
|
100 | next_data = next_data or [] | |||
|
101 | ||||
|
102 | current = [serialize(x) for x in commits] | |||
|
103 | commits = prev_data + current + next_data | |||
|
104 | ||||
|
105 | dag = _dagwalker(repo, commits) | |||
|
106 | ||||
|
107 | data = [[commit_id, vtx, edges, branch] | |||
|
108 | for commit_id, vtx, edges, branch in _colored(dag)] | |||
|
109 | return json.dumps(data), json.dumps(current) | |||
|
110 | ||||
|
111 | def _check_if_valid_branch(self, branch_name, repo_name, f_path): | |||
|
112 | if branch_name not in self.rhodecode_vcs_repo.branches_all: | |||
|
113 | h.flash('Branch {} is not found.'.format(h.escape(branch_name)), | |||
|
114 | category='warning') | |||
|
115 | redirect_url = h.route_path( | |||
|
116 | 'repo_changelog_file', repo_name=repo_name, | |||
|
117 | commit_id=branch_name, f_path=f_path or '') | |||
|
118 | raise HTTPFound(redirect_url) | |||
|
119 | ||||
|
120 | def _load_changelog_data( | |||
|
121 | self, c, collection, page, chunk_size, branch_name=None, | |||
|
122 | dynamic=False): | |||
|
123 | ||||
|
124 | def url_generator(**kw): | |||
|
125 | query_params = {} | |||
|
126 | query_params.update(kw) | |||
|
127 | return h.route_path( | |||
|
128 | 'repo_changelog', | |||
|
129 | repo_name=c.rhodecode_db_repo.repo_name, _query=query_params) | |||
|
130 | ||||
|
131 | c.total_cs = len(collection) | |||
|
132 | c.showing_commits = min(chunk_size, c.total_cs) | |||
|
133 | c.pagination = RepoPage(collection, page=page, item_count=c.total_cs, | |||
|
134 | items_per_page=chunk_size, branch=branch_name, | |||
|
135 | url=url_generator) | |||
|
136 | ||||
|
137 | c.next_page = c.pagination.next_page | |||
|
138 | c.prev_page = c.pagination.previous_page | |||
|
139 | ||||
|
140 | if dynamic: | |||
|
141 | if self.request.GET.get('chunk') != 'next': | |||
|
142 | c.next_page = None | |||
|
143 | if self.request.GET.get('chunk') != 'prev': | |||
|
144 | c.prev_page = None | |||
|
145 | ||||
|
146 | page_commit_ids = [x.raw_id for x in c.pagination] | |||
|
147 | c.comments = c.rhodecode_db_repo.get_comments(page_commit_ids) | |||
|
148 | c.statuses = c.rhodecode_db_repo.statuses(page_commit_ids) | |||
|
149 | ||||
|
150 | def load_default_context(self): | |||
|
151 | c = self._get_local_tmpl_context(include_app_defaults=True) | |||
|
152 | ||||
|
153 | # TODO(marcink): remove repo_info and use c.rhodecode_db_repo instead | |||
|
154 | c.repo_info = self.db_repo | |||
|
155 | c.rhodecode_repo = self.rhodecode_vcs_repo | |||
|
156 | ||||
|
157 | self._register_global_c(c) | |||
|
158 | return c | |||
|
159 | ||||
|
160 | @LoginRequired() | |||
|
161 | @HasRepoPermissionAnyDecorator( | |||
|
162 | 'repository.read', 'repository.write', 'repository.admin') | |||
|
163 | @view_config( | |||
|
164 | route_name='repo_changelog', request_method='GET', | |||
|
165 | renderer='rhodecode:templates/changelog/changelog.mako') | |||
|
166 | @view_config( | |||
|
167 | route_name='repo_changelog_file', request_method='GET', | |||
|
168 | renderer='rhodecode:templates/changelog/changelog.mako') | |||
|
169 | def repo_changelog(self): | |||
|
170 | c = self.load_default_context() | |||
|
171 | ||||
|
172 | commit_id = self.request.matchdict.get('commit_id') | |||
|
173 | f_path = self._get_f_path(self.request.matchdict) | |||
|
174 | ||||
|
175 | chunk_size = 20 | |||
|
176 | ||||
|
177 | c.branch_name = branch_name = self.request.GET.get('branch') or '' | |||
|
178 | c.book_name = book_name = self.request.GET.get('bookmark') or '' | |||
|
179 | hist_limit = safe_int(self.request.GET.get('limit')) or None | |||
|
180 | ||||
|
181 | p = safe_int(self.request.GET.get('page', 1), 1) | |||
|
182 | ||||
|
183 | c.selected_name = branch_name or book_name | |||
|
184 | if not commit_id and branch_name: | |||
|
185 | self._check_if_valid_branch(branch_name, self.db_repo_name, f_path) | |||
|
186 | ||||
|
187 | c.changelog_for_path = f_path | |||
|
188 | pre_load = ['author', 'branch', 'date', 'message', 'parents'] | |||
|
189 | commit_ids = [] | |||
|
190 | ||||
|
191 | partial_xhr = self.request.environ.get('HTTP_X_PARTIAL_XHR') | |||
|
192 | ||||
|
193 | try: | |||
|
194 | if f_path: | |||
|
195 | log.debug('generating changelog for path %s', f_path) | |||
|
196 | # get the history for the file ! | |||
|
197 | base_commit = self.rhodecode_vcs_repo.get_commit(commit_id) | |||
|
198 | try: | |||
|
199 | collection = base_commit.get_file_history( | |||
|
200 | f_path, limit=hist_limit, pre_load=pre_load) | |||
|
201 | if collection and partial_xhr: | |||
|
202 | # for ajax call we remove first one since we're looking | |||
|
203 | # at it right now in the context of a file commit | |||
|
204 | collection.pop(0) | |||
|
205 | except (NodeDoesNotExistError, CommitError): | |||
|
206 | # this node is not present at tip! | |||
|
207 | try: | |||
|
208 | commit = self._get_commit_or_redirect(commit_id) | |||
|
209 | collection = commit.get_file_history(f_path) | |||
|
210 | except RepositoryError as e: | |||
|
211 | h.flash(safe_str(e), category='warning') | |||
|
212 | redirect_url = h.route_path( | |||
|
213 | 'repo_changelog', repo_name=self.db_repo_name) | |||
|
214 | raise HTTPFound(redirect_url) | |||
|
215 | collection = list(reversed(collection)) | |||
|
216 | else: | |||
|
217 | collection = self.rhodecode_vcs_repo.get_commits( | |||
|
218 | branch_name=branch_name, pre_load=pre_load) | |||
|
219 | ||||
|
220 | self._load_changelog_data( | |||
|
221 | c, collection, p, chunk_size, c.branch_name, dynamic=f_path) | |||
|
222 | ||||
|
223 | except EmptyRepositoryError as e: | |||
|
224 | h.flash(safe_str(h.escape(e)), category='warning') | |||
|
225 | raise HTTPFound( | |||
|
226 | h.route_path('repo_summary', repo_name=self.db_repo_name)) | |||
|
227 | except (RepositoryError, CommitDoesNotExistError, Exception) as e: | |||
|
228 | log.exception(safe_str(e)) | |||
|
229 | h.flash(safe_str(h.escape(e)), category='error') | |||
|
230 | raise HTTPFound( | |||
|
231 | h.route_path('repo_changelog', repo_name=self.db_repo_name)) | |||
|
232 | ||||
|
233 | if partial_xhr or self.request.environ.get('HTTP_X_PJAX'): | |||
|
234 | # loading from ajax, we don't want the first result, it's popped | |||
|
235 | # in the code above | |||
|
236 | html = render( | |||
|
237 | 'rhodecode:templates/changelog/changelog_file_history.mako', | |||
|
238 | self._get_template_context(c), self.request) | |||
|
239 | return Response(html) | |||
|
240 | ||||
|
241 | if not f_path: | |||
|
242 | commit_ids = c.pagination | |||
|
243 | ||||
|
244 | c.graph_data, c.graph_commits = self._graph( | |||
|
245 | self.rhodecode_vcs_repo, commit_ids) | |||
|
246 | ||||
|
247 | return self._get_template_context(c) | |||
|
248 | ||||
|
249 | @LoginRequired() | |||
|
250 | @HasRepoPermissionAnyDecorator( | |||
|
251 | 'repository.read', 'repository.write', 'repository.admin') | |||
|
252 | @view_config( | |||
|
253 | route_name='repo_changelog_elements', request_method=('GET', 'POST'), | |||
|
254 | renderer='rhodecode:templates/changelog/changelog_elements.mako', | |||
|
255 | xhr=True) | |||
|
256 | def repo_changelog_elements(self): | |||
|
257 | c = self.load_default_context() | |||
|
258 | chunk_size = 20 | |||
|
259 | ||||
|
260 | def wrap_for_error(err): | |||
|
261 | html = '<tr>' \ | |||
|
262 | '<td colspan="9" class="alert alert-error">ERROR: {}</td>' \ | |||
|
263 | '</tr>'.format(err) | |||
|
264 | return Response(html) | |||
|
265 | ||||
|
266 | c.branch_name = branch_name = self.request.GET.get('branch') or '' | |||
|
267 | c.book_name = book_name = self.request.GET.get('bookmark') or '' | |||
|
268 | ||||
|
269 | c.selected_name = branch_name or book_name | |||
|
270 | if branch_name and branch_name not in self.rhodecode_vcs_repo.branches_all: | |||
|
271 | return wrap_for_error( | |||
|
272 | safe_str('Branch: {} is not valid'.format(branch_name))) | |||
|
273 | ||||
|
274 | pre_load = ['author', 'branch', 'date', 'message', 'parents'] | |||
|
275 | collection = self.rhodecode_vcs_repo.get_commits( | |||
|
276 | branch_name=branch_name, pre_load=pre_load) | |||
|
277 | ||||
|
278 | p = safe_int(self.request.GET.get('page', 1), 1) | |||
|
279 | try: | |||
|
280 | self._load_changelog_data( | |||
|
281 | c, collection, p, chunk_size, dynamic=True) | |||
|
282 | except EmptyRepositoryError as e: | |||
|
283 | return wrap_for_error(safe_str(e)) | |||
|
284 | except (RepositoryError, CommitDoesNotExistError, Exception) as e: | |||
|
285 | log.exception('Failed to fetch commits') | |||
|
286 | return wrap_for_error(safe_str(e)) | |||
|
287 | ||||
|
288 | prev_data = None | |||
|
289 | next_data = None | |||
|
290 | ||||
|
291 | prev_graph = json.loads(self.request.POST.get('graph', '')) | |||
|
292 | ||||
|
293 | if self.request.GET.get('chunk') == 'prev': | |||
|
294 | next_data = prev_graph | |||
|
295 | elif self.request.GET.get('chunk') == 'next': | |||
|
296 | prev_data = prev_graph | |||
|
297 | ||||
|
298 | c.graph_data, c.graph_commits = self._graph( | |||
|
299 | self.rhodecode_vcs_repo, c.pagination, | |||
|
300 | prev_data=prev_data, next_data=next_data) | |||
|
301 | ||||
|
302 | return self._get_template_context(c) |
@@ -139,6 +139,17 b' def includeme(config):' | |||||
139 | name='repo_stats', |
|
139 | name='repo_stats', | |
140 | pattern='/{repo_name:.*?[^/]}/repo_stats/{commit_id}', repo_route=True) |
|
140 | pattern='/{repo_name:.*?[^/]}/repo_stats/{commit_id}', repo_route=True) | |
141 |
|
141 | |||
|
142 | # Changelog | |||
|
143 | config.add_route( | |||
|
144 | name='repo_changelog', | |||
|
145 | pattern='/{repo_name:.*?[^/]}/changelog', repo_route=True) | |||
|
146 | config.add_route( | |||
|
147 | name='repo_changelog_file', | |||
|
148 | pattern='/{repo_name:.*?[^/]}/changelog/{commit_id}/{f_path:.*}', repo_route=True) | |||
|
149 | config.add_route( | |||
|
150 | name='repo_changelog_elements', | |||
|
151 | pattern='/{repo_name:.*?[^/]}/changelog_elements', repo_route=True) | |||
|
152 | ||||
142 | # Tags |
|
153 | # Tags | |
143 | config.add_route( |
|
154 | config.add_route( | |
144 | name='tags_home', |
|
155 | name='tags_home', |
@@ -22,20 +22,32 b' import re' | |||||
22 |
|
22 | |||
23 | import pytest |
|
23 | import pytest | |
24 |
|
24 | |||
25 |
from rhodecode. |
|
25 | from rhodecode.apps.repository.views.repo_changelog import DEFAULT_CHANGELOG_SIZE | |
26 |
from rhodecode.tests import |
|
26 | from rhodecode.tests import TestController | |
27 | from rhodecode.tests.utils import AssertResponse |
|
|||
28 |
|
||||
29 |
|
27 | |||
30 | MATCH_HASH = re.compile(r'<span class="commit_hash">r(\d+):[\da-f]+</span>') |
|
28 | MATCH_HASH = re.compile(r'<span class="commit_hash">r(\d+):[\da-f]+</span>') | |
31 |
|
29 | |||
32 |
|
30 | |||
|
31 | def route_path(name, params=None, **kwargs): | |||
|
32 | import urllib | |||
|
33 | ||||
|
34 | base_url = { | |||
|
35 | 'repo_changelog':'/{repo_name}/changelog', | |||
|
36 | 'repo_changelog_file':'/{repo_name}/changelog/{commit_id}/{f_path}', | |||
|
37 | 'repo_changelog_elements':'/{repo_name}/changelog_elements', | |||
|
38 | }[name].format(**kwargs) | |||
|
39 | ||||
|
40 | if params: | |||
|
41 | base_url = '{}?{}'.format(base_url, urllib.urlencode(params)) | |||
|
42 | return base_url | |||
|
43 | ||||
|
44 | ||||
33 | class TestChangelogController(TestController): |
|
45 | class TestChangelogController(TestController): | |
34 |
|
46 | |||
35 |
def test_ |
|
47 | def test_changelog(self, backend): | |
36 | self.log_user() |
|
48 | self.log_user() | |
37 | response = self.app.get(url(controller='changelog', action='index', |
|
49 | response = self.app.get( | |
38 |
|
|
50 | route_path('repo_changelog', repo_name=backend.repo_name)) | |
39 |
|
51 | |||
40 | first_idx = -1 |
|
52 | first_idx = -1 | |
41 | last_idx = -DEFAULT_CHANGELOG_SIZE |
|
53 | last_idx = -DEFAULT_CHANGELOG_SIZE | |
@@ -43,39 +55,30 b' class TestChangelogController(TestContro' | |||||
43 | response, first_idx, last_idx, backend) |
|
55 | response, first_idx, last_idx, backend) | |
44 |
|
56 | |||
45 | @pytest.mark.backends("hg", "git") |
|
57 | @pytest.mark.backends("hg", "git") | |
46 |
def test_ |
|
58 | def test_changelog_filtered_by_branch(self, backend): | |
47 | self.log_user() |
|
59 | self.log_user() | |
48 | self.app.get( |
|
60 | self.app.get( | |
49 | url( |
|
61 | route_path('repo_changelog', repo_name=backend.repo_name, | |
50 | controller='changelog', |
|
62 | params=dict(branch=backend.default_branch_name)), | |
51 | action='index', |
|
|||
52 | repo_name=backend.repo_name, |
|
|||
53 | branch=backend.default_branch_name), |
|
|||
54 | status=200) |
|
63 | status=200) | |
55 |
|
64 | |||
56 | @pytest.mark.backends("svn") |
|
65 | @pytest.mark.backends("svn") | |
57 |
def test_ |
|
66 | def test_changelog_filtered_by_branch_svn(self, autologin_user, backend): | |
58 | repo = backend['svn-simple-layout'] |
|
67 | repo = backend['svn-simple-layout'] | |
59 | response = self.app.get( |
|
68 | response = self.app.get( | |
60 | url( |
|
69 | route_path('repo_changelog', repo_name=repo.repo_name, | |
61 | controller='changelog', |
|
70 | params=dict(branch='trunk')), | |
62 | action='index', |
|
|||
63 | repo_name=repo.repo_name, |
|
|||
64 | branch='trunk'), |
|
|||
65 | status=200) |
|
71 | status=200) | |
66 |
|
72 | |||
67 | self.assert_commits_on_page( |
|
73 | self.assert_commits_on_page( | |
68 | response, indexes=[15, 12, 7, 3, 2, 1]) |
|
74 | response, indexes=[15, 12, 7, 3, 2, 1]) | |
69 |
|
75 | |||
70 |
def test_ |
|
76 | def test_changelog_filtered_by_wrong_branch(self, backend): | |
71 | self.log_user() |
|
77 | self.log_user() | |
72 | branch = 'wrong-branch-name' |
|
78 | branch = 'wrong-branch-name' | |
73 | response = self.app.get( |
|
79 | response = self.app.get( | |
74 | url( |
|
80 | route_path('repo_changelog', repo_name=backend.repo_name, | |
75 | controller='changelog', |
|
81 | params=dict(branch=branch)), | |
76 | action='index', |
|
|||
77 | repo_name=backend.repo_name, |
|
|||
78 | branch=branch), |
|
|||
79 | status=302) |
|
82 | status=302) | |
80 | expected_url = '/{repo}/changelog/{branch}'.format( |
|
83 | expected_url = '/{repo}/changelog/{branch}'.format( | |
81 | repo=backend.repo_name, branch=branch) |
|
84 | repo=backend.repo_name, branch=branch) | |
@@ -89,7 +92,7 b' class TestChangelogController(TestContro' | |||||
89 | assert found_indexes == indexes |
|
92 | assert found_indexes == indexes | |
90 |
|
93 | |||
91 | @pytest.mark.xfail_backends("svn", reason="Depends on branch support") |
|
94 | @pytest.mark.xfail_backends("svn", reason="Depends on branch support") | |
92 |
def test_ |
|
95 | def test_changelog_filtered_by_branch_with_merges( | |
93 | self, autologin_user, backend): |
|
96 | self, autologin_user, backend): | |
94 |
|
97 | |||
95 | # Note: The changelog of branch "b" does not contain the commit "a1" |
|
98 | # Note: The changelog of branch "b" does not contain the commit "a1" | |
@@ -104,33 +107,27 b' class TestChangelogController(TestContro' | |||||
104 | backend.create_repo(commits) |
|
107 | backend.create_repo(commits) | |
105 |
|
108 | |||
106 | self.app.get( |
|
109 | self.app.get( | |
107 | url('changelog_home', |
|
110 | route_path('repo_changelog', repo_name=backend.repo_name, | |
108 | controller='changelog', |
|
111 | params=dict(branch='b')), | |
109 | action='index', |
|
|||
110 | repo_name=backend.repo_name, |
|
|||
111 | branch='b'), |
|
|||
112 | status=200) |
|
112 | status=200) | |
113 |
|
113 | |||
114 | @pytest.mark.backends("hg") |
|
114 | @pytest.mark.backends("hg") | |
115 |
def test_ |
|
115 | def test_changelog_closed_branches(self, autologin_user, backend): | |
116 | repo = backend['closed_branch'] |
|
116 | repo = backend['closed_branch'] | |
117 | response = self.app.get( |
|
117 | response = self.app.get( | |
118 | url( |
|
118 | route_path('repo_changelog', repo_name=repo.repo_name, | |
119 | controller='changelog', |
|
119 | params=dict(branch='experimental')), | |
120 | action='index', |
|
|||
121 | repo_name=repo.repo_name, |
|
|||
122 | branch='experimental'), |
|
|||
123 | status=200) |
|
120 | status=200) | |
124 |
|
121 | |||
125 | self.assert_commits_on_page( |
|
122 | self.assert_commits_on_page( | |
126 | response, indexes=[3, 1]) |
|
123 | response, indexes=[3, 1]) | |
127 |
|
124 | |||
128 |
def test_ |
|
125 | def test_changelog_pagination(self, backend): | |
129 | self.log_user() |
|
126 | self.log_user() | |
130 | # pagination, walk up to page 6 |
|
127 | # pagination, walk up to page 6 | |
131 |
changelog_url = |
|
128 | changelog_url = route_path( | |
132 | controller='changelog', action='index', |
|
129 | 'repo_changelog', repo_name=backend.repo_name) | |
133 | repo_name=backend.repo_name) |
|
130 | ||
134 | for page in range(1, 7): |
|
131 | for page in range(1, 7): | |
135 | response = self.app.get(changelog_url, {'page': page}) |
|
132 | response = self.app.get(changelog_url, {'page': page}) | |
136 |
|
133 | |||
@@ -166,27 +163,33 b' class TestChangelogController(TestContro' | |||||
166 | first_commit_of_next_page.idx, first_commit_of_next_page.short_id) |
|
163 | first_commit_of_next_page.idx, first_commit_of_next_page.short_id) | |
167 | assert first_span_of_next_page not in response |
|
164 | assert first_span_of_next_page not in response | |
168 |
|
165 | |||
169 | def test_index_with_filenode(self, backend): |
|
166 | @pytest.mark.parametrize('test_path', [ | |
|
167 | 'vcs/exceptions.py', | |||
|
168 | '/vcs/exceptions.py', | |||
|
169 | '//vcs/exceptions.py' | |||
|
170 | ]) | |||
|
171 | def test_changelog_with_filenode(self, backend, test_path): | |||
170 | self.log_user() |
|
172 | self.log_user() | |
171 |
response = self.app.get( |
|
173 | response = self.app.get( | |
172 | controller='changelog', action='index', revision='tip', |
|
174 | route_path('repo_changelog_file', repo_name=backend.repo_name, | |
173 | f_path='/vcs/exceptions.py', repo_name=backend.repo_name)) |
|
175 | commit_id='tip', f_path=test_path), | |
|
176 | ) | |||
174 |
|
177 | |||
175 | # history commits messages |
|
178 | # history commits messages | |
176 | response.mustcontain('Added exceptions module, this time for real') |
|
179 | response.mustcontain('Added exceptions module, this time for real') | |
177 | response.mustcontain('Added not implemented hg backend test case') |
|
180 | response.mustcontain('Added not implemented hg backend test case') | |
178 | response.mustcontain('Added BaseChangeset class') |
|
181 | response.mustcontain('Added BaseChangeset class') | |
179 |
|
182 | |||
180 |
def test_ |
|
183 | def test_changelog_with_filenode_that_is_dirnode(self, backend): | |
181 | self.log_user() |
|
184 | self.log_user() | |
182 | response = self.app.get(url(controller='changelog', action='index', |
|
185 | self.app.get( | |
183 | revision='tip', f_path='/tests', |
|
186 | route_path('repo_changelog_file', repo_name=backend.repo_name, | |
184 | repo_name=backend.repo_name)) |
|
187 | commit_id='tip', f_path='/tests'), | |
185 | assert response.status == '302 Found' |
|
188 | status=302) | |
186 |
|
189 | |||
187 |
def test_ |
|
190 | def test_changelog_with_filenode_not_existing(self, backend): | |
188 | self.log_user() |
|
191 | self.log_user() | |
189 | response = self.app.get(url(controller='changelog', action='index', |
|
192 | self.app.get( | |
190 | revision='tip', f_path='/wrong_path', |
|
193 | route_path('repo_changelog_file', repo_name=backend.repo_name, | |
191 | repo_name=backend.repo_name)) |
|
194 | commit_id='tip', f_path='wrong_path'), | |
192 | assert response.status == '302 Found' |
|
195 | status=302) |
@@ -274,7 +274,6 b' def make_map(config):' | |||||
274 | m.connect('edit_user_perms_summary', '/users/{user_id}/edit/permissions_summary', |
|
274 | m.connect('edit_user_perms_summary', '/users/{user_id}/edit/permissions_summary', | |
275 | action='edit_perms_summary', conditions={'method': ['GET']}) |
|
275 | action='edit_perms_summary', conditions={'method': ['GET']}) | |
276 |
|
276 | |||
277 |
|
||||
278 | # ADMIN USER GROUPS REST ROUTES |
|
277 | # ADMIN USER GROUPS REST ROUTES | |
279 | with rmap.submapper(path_prefix=ADMIN_PREFIX, |
|
278 | with rmap.submapper(path_prefix=ADMIN_PREFIX, | |
280 | controller='admin/user_groups') as m: |
|
279 | controller='admin/user_groups') as m: | |
@@ -698,21 +697,6 b' def make_map(config):' | |||||
698 | conditions={'function': check_repo, 'method': ['DELETE']}, |
|
697 | conditions={'function': check_repo, 'method': ['DELETE']}, | |
699 | requirements=URL_NAME_REQUIREMENTS, jsroute=True) |
|
698 | requirements=URL_NAME_REQUIREMENTS, jsroute=True) | |
700 |
|
699 | |||
701 | rmap.connect('changelog_home', '/{repo_name}/changelog', jsroute=True, |
|
|||
702 | controller='changelog', conditions={'function': check_repo}, |
|
|||
703 | requirements=URL_NAME_REQUIREMENTS) |
|
|||
704 |
|
||||
705 | rmap.connect('changelog_file_home', |
|
|||
706 | '/{repo_name}/changelog/{revision}/{f_path}', |
|
|||
707 | controller='changelog', f_path=None, |
|
|||
708 | conditions={'function': check_repo}, |
|
|||
709 | requirements=URL_NAME_REQUIREMENTS, jsroute=True) |
|
|||
710 |
|
||||
711 | rmap.connect('changelog_elements', '/{repo_name}/changelog_details', |
|
|||
712 | controller='changelog', action='changelog_elements', |
|
|||
713 | conditions={'function': check_repo}, |
|
|||
714 | requirements=URL_NAME_REQUIREMENTS, jsroute=True) |
|
|||
715 |
|
||||
716 | rmap.connect('repo_fork_create_home', '/{repo_name}/fork', |
|
700 | rmap.connect('repo_fork_create_home', '/{repo_name}/fork', | |
717 | controller='forks', action='fork_create', |
|
701 | controller='forks', action='fork_create', | |
718 | conditions={'function': check_repo, 'method': ['POST']}, |
|
702 | conditions={'function': check_repo, 'method': ['POST']}, |
@@ -149,7 +149,6 b' class ChangesetController(BaseRepoContro' | |||||
149 |
|
149 | |||
150 | def __before__(self): |
|
150 | def __before__(self): | |
151 | super(ChangesetController, self).__before__() |
|
151 | super(ChangesetController, self).__before__() | |
152 | c.affected_files_cut_off = 60 |
|
|||
153 |
|
152 | |||
154 | def _index(self, commit_id_range, method): |
|
153 | def _index(self, commit_id_range, method): | |
155 | c.ignorews_url = _ignorews_url |
|
154 | c.ignorews_url = _ignorews_url |
@@ -129,9 +129,8 b' class JournalController(BaseController):' | |||||
129 | desc = action_extra() |
|
129 | desc = action_extra() | |
130 | _url = None |
|
130 | _url = None | |
131 | if entry.repository is not None: |
|
131 | if entry.repository is not None: | |
132 |
_url = url('changelog |
|
132 | _url = h.route_url('repo_changelog', | |
133 |
repo_name=entry.repository.repo_name |
|
133 | repo_name=entry.repository.repo_name) | |
134 | qualified=True) |
|
|||
135 |
|
134 | |||
136 | feed.add_item(title=title, |
|
135 | feed.add_item(title=title, | |
137 | pubdate=entry.action_date, |
|
136 | pubdate=entry.action_date, | |
@@ -172,9 +171,8 b' class JournalController(BaseController):' | |||||
172 | desc = action_extra() |
|
171 | desc = action_extra() | |
173 | _url = None |
|
172 | _url = None | |
174 | if entry.repository is not None: |
|
173 | if entry.repository is not None: | |
175 |
_url = url('changelog |
|
174 | _url = h.route_url('repo_changelog', | |
176 |
repo_name=entry.repository.repo_name |
|
175 | repo_name=entry.repository.repo_name) | |
177 | qualified=True) |
|
|||
178 |
|
176 | |||
179 | feed.add_item(title=title, |
|
177 | feed.add_item(title=title, | |
180 | pubdate=entry.action_date, |
|
178 | pubdate=entry.action_date, |
@@ -324,6 +324,8 b' def attach_context_attributes(context, r' | |||||
324 | context.visual.rhodecode_support_url = \ |
|
324 | context.visual.rhodecode_support_url = \ | |
325 | rc_config.get('rhodecode_support_url') or h.route_url('rhodecode_support') |
|
325 | rc_config.get('rhodecode_support_url') or h.route_url('rhodecode_support') | |
326 |
|
326 | |||
|
327 | context.visual.affected_files_cut_off = 60 | |||
|
328 | ||||
327 | context.pre_code = rc_config.get('rhodecode_pre_code') |
|
329 | context.pre_code = rc_config.get('rhodecode_pre_code') | |
328 | context.post_code = rc_config.get('rhodecode_post_code') |
|
330 | context.post_code = rc_config.get('rhodecode_post_code') | |
329 | context.rhodecode_name = rc_config.get('rhodecode_title') |
|
331 | context.rhodecode_name = rc_config.get('rhodecode_title') |
@@ -66,7 +66,7 b' function setRCMouseBindings(repoName, re' | |||||
66 | }); |
|
66 | }); | |
67 | Mousetrap.bind(['g c'], function(e) { |
|
67 | Mousetrap.bind(['g c'], function(e) { | |
68 | window.location = pyroutes.url( |
|
68 | window.location = pyroutes.url( | |
69 |
'changelog |
|
69 | 'repo_changelog', {'repo_name': repoName}); | |
70 | }); |
|
70 | }); | |
71 | Mousetrap.bind(['g F'], function(e) { |
|
71 | Mousetrap.bind(['g F'], function(e) { | |
72 | window.location = pyroutes.url( |
|
72 | window.location = pyroutes.url( |
@@ -30,9 +30,6 b' function registerRCRoutes() {' | |||||
30 | pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']); |
|
30 | pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']); | |
31 | pyroutes.register('pullrequest_comment', '/%(repo_name)s/pull-request-comment/%(pull_request_id)s', ['repo_name', 'pull_request_id']); |
|
31 | pyroutes.register('pullrequest_comment', '/%(repo_name)s/pull-request-comment/%(pull_request_id)s', ['repo_name', 'pull_request_id']); | |
32 | pyroutes.register('pullrequest_comment_delete', '/%(repo_name)s/pull-request-comment/%(comment_id)s/delete', ['repo_name', 'comment_id']); |
|
32 | pyroutes.register('pullrequest_comment_delete', '/%(repo_name)s/pull-request-comment/%(comment_id)s/delete', ['repo_name', 'comment_id']); | |
33 | pyroutes.register('changelog_home', '/%(repo_name)s/changelog', ['repo_name']); |
|
|||
34 | pyroutes.register('changelog_file_home', '/%(repo_name)s/changelog/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']); |
|
|||
35 | pyroutes.register('changelog_elements', '/%(repo_name)s/changelog_details', ['repo_name']); |
|
|||
36 | pyroutes.register('favicon', '/favicon.ico', []); |
|
33 | pyroutes.register('favicon', '/favicon.ico', []); | |
37 | pyroutes.register('robots', '/robots.txt', []); |
|
34 | pyroutes.register('robots', '/robots.txt', []); | |
38 | pyroutes.register('auth_home', '/_admin/auth*traverse', []); |
|
35 | pyroutes.register('auth_home', '/_admin/auth*traverse', []); | |
@@ -124,6 +121,9 b' function registerRCRoutes() {' | |||||
124 | pyroutes.register('repo_refs_data', '/%(repo_name)s/refs-data', ['repo_name']); |
|
121 | pyroutes.register('repo_refs_data', '/%(repo_name)s/refs-data', ['repo_name']); | |
125 | pyroutes.register('repo_refs_changelog_data', '/%(repo_name)s/refs-data-changelog', ['repo_name']); |
|
122 | pyroutes.register('repo_refs_changelog_data', '/%(repo_name)s/refs-data-changelog', ['repo_name']); | |
126 | pyroutes.register('repo_stats', '/%(repo_name)s/repo_stats/%(commit_id)s', ['repo_name', 'commit_id']); |
|
123 | pyroutes.register('repo_stats', '/%(repo_name)s/repo_stats/%(commit_id)s', ['repo_name', 'commit_id']); | |
|
124 | pyroutes.register('repo_changelog', '/%(repo_name)s/changelog', ['repo_name']); | |||
|
125 | pyroutes.register('repo_changelog_file', '/%(repo_name)s/changelog/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']); | |||
|
126 | pyroutes.register('repo_changelog_elements', '/%(repo_name)s/changelog_elements', ['repo_name']); | |||
127 | pyroutes.register('tags_home', '/%(repo_name)s/tags', ['repo_name']); |
|
127 | pyroutes.register('tags_home', '/%(repo_name)s/tags', ['repo_name']); | |
128 | pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']); |
|
128 | pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']); | |
129 | pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']); |
|
129 | pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']); |
@@ -105,7 +105,7 b' var CommitsController = function () {' | |||||
105 | urlData['branch'] = branch; |
|
105 | urlData['branch'] = branch; | |
106 | } |
|
106 | } | |
107 |
|
107 | |||
108 | return pyroutes.url('changelog_elements', urlData); |
|
108 | return pyroutes.url('repo_changelog_elements', urlData); | |
109 | }; |
|
109 | }; | |
110 |
|
110 | |||
111 | this.loadNext = function (node, page, branch) { |
|
111 | this.loadNext = function (node, page, branch) { |
@@ -226,7 +226,7 b'' | |||||
226 | <div class="wrapper"> |
|
226 | <div class="wrapper"> | |
227 | <ul id="context-pages" class="horizontal-list navigation"> |
|
227 | <ul id="context-pages" class="horizontal-list navigation"> | |
228 | <li class="${is_active('summary')}"><a class="menulink" href="${h.route_path('repo_summary', repo_name=c.repo_name)}"><div class="menulabel">${_('Summary')}</div></a></li> |
|
228 | <li class="${is_active('summary')}"><a class="menulink" href="${h.route_path('repo_summary', repo_name=c.repo_name)}"><div class="menulabel">${_('Summary')}</div></a></li> | |
229 |
<li class="${is_active('changelog')}"><a class="menulink" href="${h. |
|
229 | <li class="${is_active('changelog')}"><a class="menulink" href="${h.route_path('repo_changelog', repo_name=c.repo_name)}"><div class="menulabel">${_('Changelog')}</div></a></li> | |
230 | <li class="${is_active('files')}"><a class="menulink" href="${h.route_path('repo_files', repo_name=c.repo_name, commit_id=c.rhodecode_db_repo.landing_rev[1], f_path='')}"><div class="menulabel">${_('Files')}</div></a></li> |
|
230 | <li class="${is_active('files')}"><a class="menulink" href="${h.route_path('repo_files', repo_name=c.repo_name, commit_id=c.rhodecode_db_repo.landing_rev[1], f_path='')}"><div class="menulabel">${_('Files')}</div></a></li> | |
231 | <li class="${is_active('compare')}"> |
|
231 | <li class="${is_active('compare')}"> | |
232 | <a class="menulink" href="${h.url('compare_home',repo_name=c.repo_name)}"><div class="menulabel">${_('Compare')}</div></a> |
|
232 | <a class="menulink" href="${h.url('compare_home',repo_name=c.repo_name)}"><div class="menulabel">${_('Compare')}</div></a> |
@@ -86,7 +86,7 b'' | |||||
86 | </div> |
|
86 | </div> | |
87 | ${self.breadcrumbs('breadcrumbs_light')} |
|
87 | ${self.breadcrumbs('breadcrumbs_light')} | |
88 | <div id="commit-counter" data-total=${c.total_cs} class="pull-right"> |
|
88 | <div id="commit-counter" data-total=${c.total_cs} class="pull-right"> | |
89 | ${ungettext('showing %d out of %d commit', 'showing %d out of %d commits', c.showing_commits) % (c.showing_commits, c.total_cs)} |
|
89 | ${_ungettext('showing %d out of %d commit', 'showing %d out of %d commits', c.showing_commits) % (c.showing_commits, c.total_cs)} | |
90 | </div> |
|
90 | </div> | |
91 | </div> |
|
91 | </div> | |
92 |
|
92 | |||
@@ -230,7 +230,7 b'' | |||||
230 |
|
230 | |||
231 | $("#clear_filter").on("click", function() { |
|
231 | $("#clear_filter").on("click", function() { | |
232 | var filter = {'repo_name': '${c.repo_name}'}; |
|
232 | var filter = {'repo_name': '${c.repo_name}'}; | |
233 |
window.location = pyroutes.url('changelog |
|
233 | window.location = pyroutes.url('repo_changelog', filter); | |
234 | }); |
|
234 | }); | |
235 |
|
235 | |||
236 | $("#branch_filter").select2({ |
|
236 | $("#branch_filter").select2({ | |
@@ -280,7 +280,7 b'' | |||||
280 | else if (data.type == 'book'){ |
|
280 | else if (data.type == 'book'){ | |
281 | filter["bookmark"] = selected; |
|
281 | filter["bookmark"] = selected; | |
282 | } |
|
282 | } | |
283 |
window.location = pyroutes.url('changelog |
|
283 | window.location = pyroutes.url('repo_changelog', filter); | |
284 | }); |
|
284 | }); | |
285 |
|
285 | |||
286 | commitsController = new CommitsController(); |
|
286 | commitsController = new CommitsController(); |
@@ -101,7 +101,7 b'' | |||||
101 | ## branch |
|
101 | ## branch | |
102 | %if commit.branch: |
|
102 | %if commit.branch: | |
103 | <span class="tag branchtag" title="${h.tooltip(_('Branch %s') % commit.branch)}"> |
|
103 | <span class="tag branchtag" title="${h.tooltip(_('Branch %s') % commit.branch)}"> | |
104 |
<a href="${h. |
|
104 | <a href="${h.route_path('repo_changelog',repo_name=c.repo_name,_query=dict(branch=commit.branch))}"><i class="icon-code-fork"></i>${h.shorter(commit.branch)}</a> | |
105 | </span> |
|
105 | </span> | |
106 | %endif |
|
106 | %endif | |
107 |
|
107 |
@@ -14,7 +14,7 b'' | |||||
14 | </a> |
|
14 | </a> | |
15 | </li> |
|
15 | </li> | |
16 | <li> |
|
16 | <li> | |
17 |
<a title="${_('Changelog')}" href="${h. |
|
17 | <a title="${_('Changelog')}" href="${h.route_path('repo_changelog',repo_name=repo_name)}"> | |
18 | <span>${_('Changelog')}</span> |
|
18 | <span>${_('Changelog')}</span> | |
19 | </a> |
|
19 | </a> | |
20 | </li> |
|
20 | </li> |
@@ -219,9 +219,9 b'' | |||||
219 | if (path.indexOf("#") >= 0) { |
|
219 | if (path.indexOf("#") >= 0) { | |
220 | path = path.slice(0, path.indexOf("#")); |
|
220 | path = path.slice(0, path.indexOf("#")); | |
221 | } |
|
221 | } | |
222 |
var url = pyroutes.url('changelog_fil |
|
222 | var url = pyroutes.url('repo_changelog_file', | |
223 | {'repo_name': templateContext.repo_name, |
|
223 | {'repo_name': templateContext.repo_name, | |
224 |
' |
|
224 | 'commit_id': state.rev, 'f_path': path, 'limit': 6}); | |
225 | $('#file_history_container').show(); |
|
225 | $('#file_history_container').show(); | |
226 | $('#file_history_container').html('<div class="file-history-inner">{0}</div>'.format(_gettext('Loading ...'))); |
|
226 | $('#file_history_container').html('<div class="file-history-inner">{0}</div>'.format(_gettext('Loading ...'))); | |
227 |
|
227 |
@@ -51,7 +51,7 b'' | |||||
51 | <span class="item">${h.format_byte_size_binary(c.file.size)}</span> |
|
51 | <span class="item">${h.format_byte_size_binary(c.file.size)}</span> | |
52 | <span class="item last">${c.file.mimetype}</span> |
|
52 | <span class="item last">${c.file.mimetype}</span> | |
53 | <div class="buttons"> |
|
53 | <div class="buttons"> | |
54 |
<a class="btn btn-mini" href="${h. |
|
54 | <a class="btn btn-mini" href="${h.route_path('repo_changelog_file',repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path=c.f_path)}"> | |
55 | <i class="icon-time"></i> ${_('history')} |
|
55 | <i class="icon-time"></i> ${_('history')} | |
56 | </a> |
|
56 | </a> | |
57 |
|
57 |
@@ -16,7 +16,7 b'' | |||||
16 | <a id="file_history_overview" href="#"> |
|
16 | <a id="file_history_overview" href="#"> | |
17 | ${_('History')} |
|
17 | ${_('History')} | |
18 | </a> |
|
18 | </a> | |
19 |
<a id="file_history_overview_full" style="display: none" href="${h. |
|
19 | <a id="file_history_overview_full" style="display: none" href="${h.route_path('repo_changelog_file',repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path=c.f_path)}"> | |
20 | ${_('Show Full History')} |
|
20 | ${_('Show Full History')} | |
21 | </a> | |
|
21 | </a> | | |
22 | %if c.annotate: |
|
22 | %if c.annotate: |
@@ -75,7 +75,7 b'' | |||||
75 | ## branch link is only valid if it is a branch |
|
75 | ## branch link is only valid if it is a branch | |
76 | <span class="tag"> |
|
76 | <span class="tag"> | |
77 | %if c.pull_request.source_ref_parts.type == 'branch': |
|
77 | %if c.pull_request.source_ref_parts.type == 'branch': | |
78 |
<a href="${h. |
|
78 | <a href="${h.route_path('repo_changelog', repo_name=c.pull_request.source_repo.repo_name, _query=dict(branch=c.pull_request.source_ref_parts.name))}">${c.pull_request.source_ref_parts.type}: ${c.pull_request.source_ref_parts.name}</a> | |
79 | %else: |
|
79 | %else: | |
80 | ${c.pull_request.source_ref_parts.type}: ${c.pull_request.source_ref_parts.name} |
|
80 | ${c.pull_request.source_ref_parts.type}: ${c.pull_request.source_ref_parts.name} | |
81 | %endif |
|
81 | %endif | |
@@ -107,7 +107,7 b'' | |||||
107 | ## branch link is only valid if it is a branch |
|
107 | ## branch link is only valid if it is a branch | |
108 | <span class="tag"> |
|
108 | <span class="tag"> | |
109 | %if c.pull_request.target_ref_parts.type == 'branch': |
|
109 | %if c.pull_request.target_ref_parts.type == 'branch': | |
110 |
<a href="${h. |
|
110 | <a href="${h.route_path('repo_changelog', repo_name=c.pull_request.target_repo.repo_name, _query=dict(branch=c.pull_request.target_ref_parts.name))}">${c.pull_request.target_ref_parts.type}: ${c.pull_request.target_ref_parts.name}</a> | |
111 | %else: |
|
111 | %else: | |
112 | ${c.pull_request.target_ref_parts.type}: ${c.pull_request.target_ref_parts.name} |
|
112 | ${c.pull_request.target_ref_parts.type}: ${c.pull_request.target_ref_parts.name} | |
113 | %endif |
|
113 | %endif |
@@ -65,7 +65,7 b' for line_number in matching_lines:' | |||||
65 | %endif |
|
65 | %endif | |
66 | </div> |
|
66 | </div> | |
67 | <div class="buttons"> |
|
67 | <div class="buttons"> | |
68 |
<a id="file_history_overview_full" href="${h. |
|
68 | <a id="file_history_overview_full" href="${h.route_path('repo_changelog_file',repo_name=entry.get('repository',''),commit_id=entry.get('commit_id', 'tip'),f_path=entry.get('f_path',''))}"> | |
69 | ${_('Show Full History')} |
|
69 | ${_('Show Full History')} | |
70 | </a> |
|
70 | </a> | |
71 | | ${h.link_to(_('Annotation'), h.route_path('repo_files:annotated', repo_name=entry.get('repository',''),commit_id=entry.get('commit_id', 'tip'),f_path=entry.get('f_path','')))} |
|
71 | | ${h.link_to(_('Annotation'), h.route_path('repo_files:annotated', repo_name=entry.get('repository',''),commit_id=entry.get('commit_id', 'tip'),f_path=entry.get('f_path','')))} |
@@ -94,7 +94,7 b'' | |||||
94 | % if commit_rev == -1: |
|
94 | % if commit_rev == -1: | |
95 | ${_ungettext('%(num)s Commit', '%(num)s Commits', 0) % {'num': 0}}, |
|
95 | ${_ungettext('%(num)s Commit', '%(num)s Commits', 0) % {'num': 0}}, | |
96 | % else: |
|
96 | % else: | |
97 |
<a href="${h. |
|
97 | <a href="${h.route_path('repo_changelog', repo_name=c.repo_name)}"> | |
98 | ${_ungettext('%(num)s Commit', '%(num)s Commits', commit_rev) % {'num': commit_rev}}</a>, |
|
98 | ${_ungettext('%(num)s Commit', '%(num)s Commits', commit_rev) % {'num': commit_rev}}</a>, | |
99 | % endif |
|
99 | % endif | |
100 |
|
100 |
@@ -74,7 +74,7 b'' | |||||
74 | ## branch |
|
74 | ## branch | |
75 | %if cs.branch: |
|
75 | %if cs.branch: | |
76 | <span class="branchtag tag" title="${h.tooltip(_('Branch %s') % cs.branch)}"> |
|
76 | <span class="branchtag tag" title="${h.tooltip(_('Branch %s') % cs.branch)}"> | |
77 |
<a href="${h. |
|
77 | <a href="${h.route_path('repo_changelog',repo_name=c.repo_name,_query=dict(branch=cs.branch))}"><i class="icon-code-fork"></i>${h.shorter(cs.branch)}</a> | |
78 | </span> |
|
78 | </span> | |
79 | %endif |
|
79 | %endif | |
80 | </div> |
|
80 | </div> |
@@ -36,6 +36,19 b' from rhodecode.tests import (' | |||||
36 | from rhodecode.tests.utils import AssertResponse |
|
36 | from rhodecode.tests.utils import AssertResponse | |
37 |
|
37 | |||
38 |
|
38 | |||
|
39 | def route_path(name, params=None, **kwargs): | |||
|
40 | import urllib | |||
|
41 | ||||
|
42 | base_url = { | |||
|
43 | 'repo_changelog':'/{repo_name}/changelog', | |||
|
44 | 'repo_changelog_file':'/{repo_name}/changelog/{commit_id}/{f_path}', | |||
|
45 | }[name].format(**kwargs) | |||
|
46 | ||||
|
47 | if params: | |||
|
48 | base_url = '{}?{}'.format(base_url, urllib.urlencode(params)) | |||
|
49 | return base_url | |||
|
50 | ||||
|
51 | ||||
39 | @pytest.mark.usefixtures('app', 'autologin_user') |
|
52 | @pytest.mark.usefixtures('app', 'autologin_user') | |
40 | @pytest.mark.backends("git", "hg") |
|
53 | @pytest.mark.backends("git", "hg") | |
41 | class TestPullrequestsController(object): |
|
54 | class TestPullrequestsController(object): | |
@@ -912,14 +925,14 b' class TestPullrequestsController(object)' | |||||
912 | target_children = target.getchildren() |
|
925 | target_children = target.getchildren() | |
913 | assert len(target_children) == 1 |
|
926 | assert len(target_children) == 1 | |
914 |
|
927 | |||
915 |
expected_origin_link = |
|
928 | expected_origin_link = route_path( | |
916 |
'changelog |
|
929 | 'repo_changelog', | |
917 | repo_name=pull_request.source_repo.scm_instance().name, |
|
930 | repo_name=pull_request.source_repo.scm_instance().name, | |
918 | branch='origin') |
|
931 | params=dict(branch='origin')) | |
919 |
expected_target_link = |
|
932 | expected_target_link = route_path( | |
920 |
'changelog |
|
933 | 'repo_changelog', | |
921 | repo_name=pull_request.target_repo.scm_instance().name, |
|
934 | repo_name=pull_request.target_repo.scm_instance().name, | |
922 | branch='target') |
|
935 | params=dict(branch='target')) | |
923 | assert origin_children[0].attrib['href'] == expected_origin_link |
|
936 | assert origin_children[0].attrib['href'] == expected_origin_link | |
924 | assert origin_children[0].text == 'branch: origin' |
|
937 | assert origin_children[0].text == 'branch: origin' | |
925 | assert target_children[0].attrib['href'] == expected_target_link |
|
938 | assert target_children[0].attrib['href'] == expected_target_link |
1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
General Comments 0
You need to be logged in to leave comments.
Login now