##// END OF EJS Templates
changelog: ported to pyramid views.
marcink -
r1931:f3f88cc8 default
parent child Browse files
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.controllers.changelog import DEFAULT_CHANGELOG_SIZE
25 from rhodecode.apps.repository.views.repo_changelog import DEFAULT_CHANGELOG_SIZE
26 from rhodecode.tests import url, TestController
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_index(self, backend):
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 repo_name=backend.repo_name))
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_index_filtered_by_branch(self, backend):
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_index_filtered_by_branch_svn(self, autologin_user, backend):
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_index_filtered_by_wrong_branch(self, backend):
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_index_filtered_by_branch_with_merges(
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_index_closed_branches(self, autologin_user, backend):
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_index_pagination(self, backend):
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 = 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(url(
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_index_with_filenode_that_is_dirnode(self, backend):
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_index_with_filenode_not_existing(self, backend):
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_home',
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_home',
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_home', {'repo_name': repoName});
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.url('changelog_home', repo_name=c.repo_name)}"><div class="menulabel">${_('Changelog')}</div></a></li>
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_home', filter);
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_home', filter);
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.url('changelog_home',repo_name=c.repo_name,branch=commit.branch)}"><i class="icon-code-fork"></i>${h.shorter(commit.branch)}</a>
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.url('changelog_home',repo_name=repo_name)}">
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_file_home',
222 var url = pyroutes.url('repo_changelog_file',
223 {'repo_name': templateContext.repo_name,
223 {'repo_name': templateContext.repo_name,
224 'revision': state.rev, 'f_path': path, 'limit': 6});
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.url('changelog_file_home',repo_name=c.repo_name, revision=c.commit.raw_id, f_path=c.f_path)}">
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.url('changelog_file_home',repo_name=c.repo_name, revision=c.commit.raw_id, f_path=c.f_path)}">
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.url('changelog_home', repo_name=c.pull_request.source_repo.repo_name, branch=c.pull_request.source_ref_parts.name)}">${c.pull_request.source_ref_parts.type}: ${c.pull_request.source_ref_parts.name}</a>
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.url('changelog_home', repo_name=c.pull_request.target_repo.repo_name, branch=c.pull_request.target_ref_parts.name)}">${c.pull_request.target_ref_parts.type}: ${c.pull_request.target_ref_parts.name}</a>
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.url('changelog_file_home',repo_name=entry.get('repository',''),revision=entry.get('commit_id', 'tip'),f_path=entry.get('f_path',''))}">
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.url('changelog_home', repo_name=c.repo_name)}">
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.url('changelog_home',repo_name=c.repo_name,branch=cs.branch)}"><i class="icon-code-fork"></i>${h.shorter(cs.branch)}</a>
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 = url(
928 expected_origin_link = route_path(
916 'changelog_home',
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 = url(
932 expected_target_link = route_path(
920 'changelog_home',
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