Show More
@@ -125,8 +125,8 b' def make_map(config):' | |||
|
125 | 125 | |
|
126 | 126 | # MAIN PAGE |
|
127 | 127 | rmap.connect('home', '/', controller='home', action='index') |
|
128 |
rmap.connect(' |
|
|
129 |
action=' |
|
|
128 | rmap.connect('goto_switcher_data', '/_goto_data', controller='home', | |
|
129 | action='goto_switcher_data') | |
|
130 | 130 | rmap.connect('repo_list_data', '/_repos', controller='home', |
|
131 | 131 | action='repo_list_data') |
|
132 | 132 |
@@ -24,16 +24,17 b' Home controller for RhodeCode Enterprise' | |||
|
24 | 24 | |
|
25 | 25 | import logging |
|
26 | 26 | import time |
|
27 | ||
|
27 | import re | |
|
28 | 28 | |
|
29 | from pylons import tmpl_context as c, request | |
|
29 | from pylons import tmpl_context as c, request, url, config | |
|
30 | 30 | from pylons.i18n.translation import _ |
|
31 | 31 | from sqlalchemy.sql import func |
|
32 | 32 | |
|
33 | 33 | from rhodecode.lib.auth import ( |
|
34 | LoginRequired, HasPermissionAllDecorator, | |
|
34 | LoginRequired, HasPermissionAllDecorator, AuthUser, | |
|
35 | 35 | HasRepoGroupPermissionAnyDecorator, XHRRequired) |
|
36 | 36 | from rhodecode.lib.base import BaseController, render |
|
37 | from rhodecode.lib.index import searcher_from_config | |
|
37 | 38 | from rhodecode.lib.ext_json import json |
|
38 | 39 | from rhodecode.lib.utils import jsonify |
|
39 | 40 | from rhodecode.lib.utils2 import safe_unicode |
@@ -134,7 +135,8 b' class HomeController(BaseController):' | |||
|
134 | 135 | 'id': obj['name'], |
|
135 | 136 | 'text': obj['name'], |
|
136 | 137 | 'type': 'repo', |
|
137 | 'obj': obj['dbrepo'] | |
|
138 | 'obj': obj['dbrepo'], | |
|
139 | 'url': url('summary_home', repo_name=obj['name']) | |
|
138 | 140 | } |
|
139 | 141 | for obj in repo_iter] |
|
140 | 142 | |
@@ -156,16 +158,45 b' class HomeController(BaseController):' | |||
|
156 | 158 | 'id': obj.group_name, |
|
157 | 159 | 'text': obj.group_name, |
|
158 | 160 | 'type': 'group', |
|
159 | 'obj': {} | |
|
161 | 'obj': {}, | |
|
162 | 'url': url('repo_group_home', group_name=obj.group_name) | |
|
160 | 163 | } |
|
161 | 164 | for obj in repo_groups_iter] |
|
162 | 165 | |
|
166 | def _get_hash_commit_list(self, hash_starts_with=None, limit=20): | |
|
167 | if not hash_starts_with or len(hash_starts_with) < 3: | |
|
168 | return [] | |
|
169 | ||
|
170 | commit_hashes = re.compile('([0-9a-f]{2,40})').findall(hash_starts_with) | |
|
171 | ||
|
172 | if len(commit_hashes) != 1: | |
|
173 | return [] | |
|
174 | ||
|
175 | commit_hash_prefix = commit_hashes[0] | |
|
176 | ||
|
177 | auth_user = AuthUser( | |
|
178 | user_id=c.rhodecode_user.user_id, ip_addr=self.ip_addr) | |
|
179 | searcher = searcher_from_config(config) | |
|
180 | result = searcher.search( | |
|
181 | 'commit_id:%s*' % commit_hash_prefix, 'commit', auth_user) | |
|
182 | ||
|
183 | return [ | |
|
184 | { | |
|
185 | 'id': entry['commit_id'], | |
|
186 | 'text': entry['commit_id'], | |
|
187 | 'type': 'commit', | |
|
188 | 'obj': {'repo': entry['repository']}, | |
|
189 | 'url': url('changeset_home', | |
|
190 | repo_name=entry['repository'], revision=entry['commit_id']) | |
|
191 | } | |
|
192 | for entry in result['results']] | |
|
193 | ||
|
163 | 194 | @LoginRequired() |
|
164 | 195 | @XHRRequired() |
|
165 | 196 | @jsonify |
|
166 |
def |
|
|
197 | def goto_switcher_data(self): | |
|
167 | 198 | query = request.GET.get('query') |
|
168 |
log.debug('generating switcher |
|
|
199 | log.debug('generating goto switcher list, query %s', query) | |
|
169 | 200 | |
|
170 | 201 | res = [] |
|
171 | 202 | repo_groups = self._get_repo_group_list(query) |
@@ -182,6 +213,19 b' class HomeController(BaseController):' | |||
|
182 | 213 | 'children': repos |
|
183 | 214 | }) |
|
184 | 215 | |
|
216 | commits = self._get_hash_commit_list(query) | |
|
217 | if commits: | |
|
218 | unique_repos = {} | |
|
219 | for commit in commits: | |
|
220 | unique_repos.setdefault(commit['obj']['repo'], [] | |
|
221 | ).append(commit) | |
|
222 | ||
|
223 | for repo in unique_repos: | |
|
224 | res.append({ | |
|
225 | 'text': _('Commits in %(repo)s') % {'repo': repo}, | |
|
226 | 'children': unique_repos[repo] | |
|
227 | }) | |
|
228 | ||
|
185 | 229 | data = { |
|
186 | 230 | 'more': False, |
|
187 | 231 | 'results': res |
@@ -203,6 +247,7 b' class HomeController(BaseController):' | |||
|
203 | 247 | 'text': _('Repositories'), |
|
204 | 248 | 'children': repos |
|
205 | 249 | }) |
|
250 | ||
|
206 | 251 | data = { |
|
207 | 252 | 'more': False, |
|
208 | 253 | 'results': res |
@@ -42,7 +42,6 b' class BaseSearch(object):' | |||
|
42 | 42 | def search(self, query, document_type, search_user, repo_name=None): |
|
43 | 43 | raise Exception('NotImplemented') |
|
44 | 44 | |
|
45 | ||
|
46 | 45 | def searcher_from_config(config, prefix='search.'): |
|
47 | 46 | _config = {} |
|
48 | 47 | for key in config.keys(): |
@@ -25,6 +25,7 b' Index schema for RhodeCode' | |||
|
25 | 25 | from __future__ import absolute_import |
|
26 | 26 | import logging |
|
27 | 27 | import os |
|
28 | import re | |
|
28 | 29 | |
|
29 | 30 | from pylons.i18n.translation import _ |
|
30 | 31 | |
@@ -59,6 +60,7 b' FRAGMENTER = ContextFragmenter(200)' | |||
|
59 | 60 | log = logging.getLogger(__name__) |
|
60 | 61 | |
|
61 | 62 | |
|
63 | ||
|
62 | 64 | class Search(BaseSearch): |
|
63 | 65 | |
|
64 | 66 | name = 'whoosh' |
@@ -90,8 +92,19 b' class Search(BaseSearch):' | |||
|
90 | 92 | if self.searcher: |
|
91 | 93 | self.searcher.close() |
|
92 | 94 | |
|
95 | def _extend_query(self, query): | |
|
96 | hashes = re.compile('([0-9a-f]{5,40})').findall(query) | |
|
97 | if hashes: | |
|
98 | hashes_or_query = ' OR '.join('commit_id:%s*' % h for h in hashes) | |
|
99 | query = u'(%s) OR %s' % (query, hashes_or_query) | |
|
100 | return query | |
|
101 | ||
|
93 | 102 | def search(self, query, document_type, search_user, repo_name=None, |
|
94 | 103 | requested_page=1, page_limit=10): |
|
104 | ||
|
105 | original_query = query | |
|
106 | query = self._extend_query(query) | |
|
107 | ||
|
95 | 108 | log.debug(u'QUERY: %s on %s', query, document_type) |
|
96 | 109 | result = { |
|
97 | 110 | 'results': [], |
@@ -455,6 +455,9 b'' | |||
|
455 | 455 | tmpl += '<i class="icon-unlock-alt"></i> '; |
|
456 | 456 | } |
|
457 | 457 | } |
|
458 | if(obj_dict && state.type == 'commit') { | |
|
459 | tmpl += '<i class="icon-tag"></i>'; | |
|
460 | } | |
|
458 | 461 | if(obj_dict && state.type == 'group'){ |
|
459 | 462 |
|
|
460 | 463 | } |
@@ -496,7 +499,7 b'' | |||
|
496 | 499 | query.callback({results: cachedData.results}); |
|
497 | 500 | } else { |
|
498 | 501 | $.ajax({ |
|
499 |
url: "${h.url(' |
|
|
502 | url: "${h.url('goto_switcher_data')}", | |
|
500 | 503 | data: {'query': query.term}, |
|
501 | 504 | dataType: 'json', |
|
502 | 505 | type: 'GET', |
@@ -514,7 +517,7 b'' | |||
|
514 | 517 | |
|
515 | 518 | $("#repo_switcher").on('select2-selecting', function(e){ |
|
516 | 519 | e.preventDefault(); |
|
517 | window.location = pyroutes.url('summary_home', {'repo_name': e.val}); | |
|
520 | window.location = e.choice.url; | |
|
518 | 521 | }); |
|
519 | 522 | |
|
520 | 523 | ## Global mouse bindings ## |
@@ -181,19 +181,25 b' class TestUserAutocompleteData(TestContr' | |||
|
181 | 181 | def assert_and_get_content(result): |
|
182 | 182 | repos = [] |
|
183 | 183 | groups = [] |
|
184 | commits = [] | |
|
184 | 185 | for data in result: |
|
185 | 186 | for data_item in data['children']: |
|
186 | 187 | assert data_item['id'] |
|
187 | 188 | assert data_item['text'] |
|
189 | assert data_item['url'] | |
|
188 | 190 | if data_item['type'] == 'repo': |
|
189 | 191 | repos.append(data_item) |
|
190 | else: | |
|
192 | elif data_item['type'] == 'group': | |
|
191 | 193 | groups.append(data_item) |
|
194 | elif data_item['type'] == 'commit': | |
|
195 | commits.append(data_item) | |
|
196 | else: | |
|
197 | raise Exception('invalid type %s' % data_item['type']) | |
|
192 | 198 | |
|
193 | return repos, groups | |
|
199 | return repos, groups, commits | |
|
194 | 200 | |
|
195 | 201 | |
|
196 |
class Test |
|
|
202 | class TestGotoSwitcherData(TestController): | |
|
197 | 203 | required_repos_with_groups = [ |
|
198 | 204 | 'abc', |
|
199 | 205 | 'abc-fork', |
@@ -253,39 +259,41 b' class TestRepoSwitcherData(TestControlle' | |||
|
253 | 259 | self.log_user() |
|
254 | 260 | |
|
255 | 261 | response = self.app.get( |
|
256 |
url(controller='home', action=' |
|
|
262 | url(controller='home', action='goto_switcher_data'), | |
|
257 | 263 | headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=200) |
|
258 | 264 | result = json.loads(response.body)['results'] |
|
259 | 265 | |
|
260 | repos, groups = assert_and_get_content(result) | |
|
266 | repos, groups, commits = assert_and_get_content(result) | |
|
261 | 267 | |
|
262 | 268 | assert len(repos) == len(Repository.get_all()) |
|
263 | 269 | assert len(groups) == len(RepoGroup.get_all()) |
|
270 | assert len(commits) == 0 | |
|
264 | 271 | |
|
265 | 272 | def test_returns_list_of_repos_and_groups_filtered(self): |
|
266 | 273 | self.log_user() |
|
267 | 274 | |
|
268 | 275 | response = self.app.get( |
|
269 |
url(controller='home', action=' |
|
|
276 | url(controller='home', action='goto_switcher_data'), | |
|
270 | 277 | headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, |
|
271 | 278 | params={'query': 'abc'}, status=200) |
|
272 | 279 | result = json.loads(response.body)['results'] |
|
273 | 280 | |
|
274 | repos, groups = assert_and_get_content(result) | |
|
281 | repos, groups, commits = assert_and_get_content(result) | |
|
275 | 282 | |
|
276 | 283 | assert len(repos) == 13 |
|
277 | 284 | assert len(groups) == 5 |
|
285 | assert len(commits) == 0 | |
|
278 | 286 | |
|
279 | 287 | def test_returns_list_of_properly_sorted_and_filtered(self): |
|
280 | 288 | self.log_user() |
|
281 | 289 | |
|
282 | 290 | response = self.app.get( |
|
283 |
url(controller='home', action=' |
|
|
291 | url(controller='home', action='goto_switcher_data'), | |
|
284 | 292 | headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, |
|
285 | 293 | params={'query': 'abc'}, status=200) |
|
286 | 294 | result = json.loads(response.body)['results'] |
|
287 | 295 | |
|
288 | repos, groups = assert_and_get_content(result) | |
|
296 | repos, groups, commits = assert_and_get_content(result) | |
|
289 | 297 | |
|
290 | 298 | test_repos = [x['text'] for x in repos[:4]] |
|
291 | 299 | assert ['abc', 'abcd', 'a/abc', 'abcde'] == test_repos |
@@ -300,54 +308,58 b' class TestRepoListData(TestController):' | |||
|
300 | 308 | self.log_user() |
|
301 | 309 | |
|
302 | 310 | response = self.app.get( |
|
303 |
url(controller='home', action='repo_ |
|
|
311 | url(controller='home', action='repo_list_data'), | |
|
304 | 312 | headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=200) |
|
305 | 313 | result = json.loads(response.body)['results'] |
|
306 | 314 | |
|
307 | repos, groups = assert_and_get_content(result) | |
|
315 | repos, groups, commits = assert_and_get_content(result) | |
|
308 | 316 | |
|
309 | 317 | assert len(repos) == len(Repository.get_all()) |
|
310 | 318 | assert len(groups) == 0 |
|
319 | assert len(commits) == 0 | |
|
311 | 320 | |
|
312 | 321 | def test_returns_list_of_repos_and_groups_filtered(self): |
|
313 | 322 | self.log_user() |
|
314 | 323 | |
|
315 | 324 | response = self.app.get( |
|
316 |
url(controller='home', action='repo_ |
|
|
325 | url(controller='home', action='repo_list_data'), | |
|
317 | 326 | headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, |
|
318 | 327 | params={'query': 'vcs_test_git'}, status=200) |
|
319 | 328 | result = json.loads(response.body)['results'] |
|
320 | 329 | |
|
321 | repos, groups = assert_and_get_content(result) | |
|
330 | repos, groups, commits = assert_and_get_content(result) | |
|
322 | 331 | |
|
323 | 332 | assert len(repos) == len(Repository.query().filter( |
|
324 | 333 | Repository.repo_name.ilike('%vcs_test_git%')).all()) |
|
325 | 334 | assert len(groups) == 0 |
|
335 | assert len(commits) == 0 | |
|
326 | 336 | |
|
327 | 337 | def test_returns_list_of_repos_and_groups_filtered_with_type(self): |
|
328 | 338 | self.log_user() |
|
329 | 339 | |
|
330 | 340 | response = self.app.get( |
|
331 |
url(controller='home', action='repo_ |
|
|
341 | url(controller='home', action='repo_list_data'), | |
|
332 | 342 | headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, |
|
333 | 343 | params={'query': 'vcs_test_git', 'repo_type': 'git'}, status=200) |
|
334 | 344 | result = json.loads(response.body)['results'] |
|
335 | 345 | |
|
336 | repos, groups = assert_and_get_content(result) | |
|
346 | repos, groups, commits = assert_and_get_content(result) | |
|
337 | 347 | |
|
338 | 348 | assert len(repos) == len(Repository.query().filter( |
|
339 | 349 | Repository.repo_name.ilike('%vcs_test_git%')).all()) |
|
340 | 350 | assert len(groups) == 0 |
|
351 | assert len(commits) == 0 | |
|
341 | 352 | |
|
342 | 353 | def test_returns_list_of_repos_non_ascii_query(self): |
|
343 | 354 | self.log_user() |
|
344 | 355 | response = self.app.get( |
|
345 |
url(controller='home', action='repo_ |
|
|
356 | url(controller='home', action='repo_list_data'), | |
|
346 | 357 | headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, |
|
347 | 358 | params={'query': 'ć_vcs_test_ą', 'repo_type': 'git'}, status=200) |
|
348 | 359 | result = json.loads(response.body)['results'] |
|
349 | 360 | |
|
350 | repos, groups = assert_and_get_content(result) | |
|
361 | repos, groups, commits = assert_and_get_content(result) | |
|
351 | 362 | |
|
352 | 363 | assert len(repos) == 0 |
|
353 | 364 | assert len(groups) == 0 |
|
365 | assert len(commits) == 0 |
@@ -129,6 +129,10 b' class TestSearchController(TestControlle' | |||
|
129 | 129 | ('author:marcin@python-blog.com ' |
|
130 | 130 | 'commit_id:b986218ba1c9b0d6a259fac9b050b1724ed8e545', 1, [ |
|
131 | 131 | ('hg', 'b986218ba1c9b0d6a259fac9b050b1724ed8e545')]), |
|
132 | ('b986218ba1c9b0d6a259fac9b050b1724ed8e545', 1, [ | |
|
133 | ('hg', 'b986218ba1c9b0d6a259fac9b050b1724ed8e545')]), | |
|
134 | ('b986218b', 1, [ | |
|
135 | ('hg', 'b986218ba1c9b0d6a259fac9b050b1724ed8e545')]), | |
|
132 | 136 | ]) |
|
133 | 137 | def test_search_commit_messages( |
|
134 | 138 | self, query, expected_hits, expected_commits, enabled_backends): |
General Comments 0
You need to be logged in to leave comments.
Login now