##// END OF EJS Templates
search: new UI for search, and repo group context search...
dan -
r3442:3bc8f801 default
parent child Browse files
Show More
@@ -334,6 +334,13 b' class RepoGroupAppView(BaseAppView):'
334 334 self.db_repo_group = request.db_repo_group
335 335 self.db_repo_group_name = self.db_repo_group.group_name
336 336
337 def _get_local_tmpl_context(self, include_app_defaults=True):
338 _ = self.request.translate
339 c = super(RepoGroupAppView, self)._get_local_tmpl_context(
340 include_app_defaults=include_app_defaults)
341 c.repo_group = self.db_repo_group
342 return c
343
337 344 def _revoke_perms_on_yourself(self, form_result):
338 345 _updates = filter(lambda u: self._rhodecode_user.user_id == int(u[0]),
339 346 form_result['perm_updates'])
@@ -334,6 +334,7 b' class HomeView(BaseAppView):'
334 334 def _get_default_search_queries(self, search_context, searcher, query):
335 335 if not searcher:
336 336 return []
337
337 338 is_es_6 = searcher.is_es_6
338 339
339 340 queries = []
@@ -424,12 +425,20 b' class HomeView(BaseAppView):'
424 425 {
425 426 'id': -1,
426 427 'value': query,
427 'value_display': u'Search for: `{}`'.format(query),
428 'value_display': u'Commit search for: `{}`'.format(query),
428 429 'type': 'search',
429 430 'url': h.route_path('search',
430 431 _query={'q': query, 'type': 'content'})
431 }
432 )
432 })
433 queries.append(
434 {
435 'id': -1,
436 'value': query,
437 'value_display': u'File search for: `{}`'.format(query),
438 'type': 'search',
439 'url': h.route_path('search',
440 _query={'q': query, 'type': 'commit'})
441 })
433 442
434 443 return queries
435 444
@@ -41,7 +41,7 b' def perform_search(request, tmpl_context'
41 41 execution_time = ''
42 42
43 43 schema = search_schema.SearchParamsSchema()
44
44 search_tags = []
45 45 search_params = {}
46 46 errors = []
47 47 try:
@@ -85,6 +85,8 b' def perform_search(request, tmpl_context'
85 85 finally:
86 86 searcher.cleanup()
87 87
88 search_tags = searcher.extract_search_tags(search_query)
89
88 90 if not search_result['error']:
89 91 execution_time = '%s results (%.3f seconds)' % (
90 92 search_result['count'],
@@ -105,6 +107,7 b' def perform_search(request, tmpl_context'
105 107 c.cur_query = search_query
106 108 c.search_type = search_type
107 109 c.searcher = searcher
110 c.search_tags = search_tags
108 111
109 112
110 113 class SearchView(BaseAppView):
@@ -1495,6 +1495,28 b' def breadcrumb_repo_link(repo):'
1495 1495 return literal(' » '.join(path))
1496 1496
1497 1497
1498 def breadcrumb_repo_group_link(repo_group):
1499 """
1500 Makes a breadcrumbs path link to repo
1501
1502 ex::
1503 group >> subgroup
1504
1505 :param repo_group: a Repository Group instance
1506 """
1507
1508 path = [
1509 link_to(group.name,
1510 route_path('repo_group_home', repo_group_name=group.group_name))
1511 for group in repo_group.parents
1512 ] + [
1513 link_to(repo_group.name,
1514 route_path('repo_group_home', repo_group_name=repo_group.group_name))
1515 ]
1516
1517 return literal(' » '.join(path))
1518
1519
1498 1520 def format_byte_size_binary(file_size):
1499 1521 """
1500 1522 Formats file/folder sizes to standard.
@@ -76,6 +76,10 b' class BaseSearcher(object):'
76 76 def get_handlers(self):
77 77 return {}
78 78
79 @staticmethod
80 def extract_search_tags(query):
81 return []
82
79 83
80 84 def search_config(config, prefix='search.'):
81 85 _config = {}
@@ -195,63 +195,3 b' def get_matching_line_offsets(lines, ter'
195 195 matching_lines[line_index] = match_offsets
196 196
197 197 return line_index, matching_lines
198
199
200 def lucene_query_parser():
201 # from pyparsing lucene_grammar
202 from pyparsing import (
203 Literal, CaselessKeyword, Forward, Regex, QuotedString, Suppress,
204 Optional, Group, infixNotation, opAssoc, ParserElement, pyparsing_common)
205
206 ParserElement.enablePackrat()
207
208 COLON, LBRACK, RBRACK, LBRACE, RBRACE, TILDE, CARAT = map(Literal, ":[]{}~^")
209 LPAR, RPAR = map(Suppress, "()")
210 and_, or_, not_, to_ = map(CaselessKeyword, "AND OR NOT TO".split())
211 keyword = and_ | or_ | not_ | to_
212
213 expression = Forward()
214
215 valid_word = Regex(r'([a-zA-Z0-9*_+.-]|\\[!(){}\[\]^"~*?\\:])+').setName("word")
216 valid_word.setParseAction(
217 lambda t: t[0]
218 .replace('\\\\', chr(127))
219 .replace('\\', '')
220 .replace(chr(127), '\\')
221 )
222
223 string = QuotedString('"')
224
225 required_modifier = Literal("+")("required")
226 prohibit_modifier = Literal("-")("prohibit")
227 integer = Regex(r"\d+").setParseAction(lambda t: int(t[0]))
228 proximity_modifier = Group(TILDE + integer("proximity"))
229 number = pyparsing_common.fnumber()
230 fuzzy_modifier = TILDE + Optional(number, default=0.5)("fuzzy")
231
232 term = Forward()
233 field_name = valid_word().setName("fieldname")
234 incl_range_search = Group(LBRACK + term("lower") + to_ + term("upper") + RBRACK)
235 excl_range_search = Group(LBRACE + term("lower") + to_ + term("upper") + RBRACE)
236 range_search = incl_range_search("incl_range") | excl_range_search("excl_range")
237 boost = (CARAT + number("boost"))
238
239 string_expr = Group(string + proximity_modifier) | string
240 word_expr = Group(valid_word + fuzzy_modifier) | valid_word
241 term << (Optional(field_name("field") + COLON) +
242 (word_expr | string_expr | range_search | Group(
243 LPAR + expression + RPAR)) +
244 Optional(boost))
245 term.setParseAction(lambda t: [t] if 'field' in t or 'boost' in t else None)
246
247 expression << infixNotation(
248 term,
249 [
250 (required_modifier | prohibit_modifier, 1, opAssoc.RIGHT),
251 ((not_ | '!').setParseAction(lambda: "NOT"), 1, opAssoc.RIGHT),
252 ((and_ | '&&').setParseAction(lambda: "AND"), 2, opAssoc.LEFT),
253 (Optional(or_ | '||').setParseAction(lambda: "OR"), 2, opAssoc.LEFT),
254 ]
255 )
256
257 return expression
@@ -2236,6 +2236,10 b' h3.files_location{'
2236 2236 clear: both;
2237 2237 margin: 0 0 @padding;
2238 2238 }
2239
2240 .search-tags {
2241 padding: 5px 0;
2242 }
2239 2243 }
2240 2244
2241 2245 div.search-feedback-items {
@@ -300,6 +300,10 b' mark,'
300 300 margin-top: @padding;
301 301 }
302 302 }
303
304 .repo-group-desc {
305 padding: 8px 0px 0px 0px;
306 }
303 307 }
304 308
305 309 .title-main {
@@ -281,7 +281,9 b' function registerRCRoutes() {'
281 281 pyroutes.register('edit_user_group_advanced_sync', '/_admin/user_groups/%(user_group_id)s/edit/advanced/sync', ['user_group_id']);
282 282 pyroutes.register('user_groups_delete', '/_admin/user_groups/%(user_group_id)s/delete', ['user_group_id']);
283 283 pyroutes.register('search', '/_admin/search', []);
284 pyroutes.register('search_repo', '/%(repo_name)s/search', ['repo_name']);
284 pyroutes.register('search_repo', '/%(repo_name)s/_search', ['repo_name']);
285 pyroutes.register('search_repo_alt', '/%(repo_name)s/search', ['repo_name']);
286 pyroutes.register('search_repo_group', '/%(repo_group_name)s/_search', ['repo_group_name']);
285 287 pyroutes.register('user_profile', '/_profiles/%(username)s', ['username']);
286 288 pyroutes.register('user_group_profile', '/_profile_user_group/%(user_group_name)s', ['user_group_name']);
287 289 pyroutes.register('my_account_profile', '/_admin/my_account/profile', []);
@@ -4,17 +4,9 b''
4 4
5 5 <%def name="breadcrumbs_links()">
6 6 %if c.repo:
7 ${h.link_to('Settings',h.route_path('edit_repo', repo_name=c.repo.repo_name))}
8 &raquo;
9 ${h.link_to(_('Integrations'),request.route_url(route_name='repo_integrations_home', repo_name=c.repo.repo_name))}
7 ${_('Settings')}
10 8 %elif c.repo_group:
11 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
12 &raquo;
13 ${h.link_to(_('Repository Groups'),h.route_path('repo_groups'))}
14 &raquo;
15 ${h.link_to(c.repo_group.group_name,h.route_path('edit_repo_group', repo_group_name=c.repo_group.group_name))}
16 &raquo;
17 ${h.link_to(_('Integrations'),request.route_url(route_name='repo_group_integrations_home', repo_group_name=c.repo_group.group_name))}
9 ${_('Settings')}
18 10 %else:
19 11 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
20 12 &raquo;
@@ -8,26 +8,12 b''
8 8 %endif
9 9 </%def>
10 10
11 <%def name="breadcrumbs_links()">
12 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
13 &raquo;
14 ${h.link_to(_('Repository Groups'),h.route_path('repo_groups'))}
15 %if c.repo_group.parent_group:
16 &raquo; ${h.link_to(c.repo_group.parent_group.name, h.route_path('repo_group_home', repo_group_name=c.repo_group.parent_group.group_name))}
17 %endif
18 &raquo; ${c.repo_group.name}
11 <%def name="menu_bar_nav()">
12 ${self.menu_items(active='admin')}
19 13 </%def>
20 14
21 <%def name="breadcrumbs_side_links()">
22 <ul class="links">
23 <li>
24 <a href="${h.route_path('repo_group_new', _query=dict(parent_group=c.repo_group.group_id))}" class="btn btn-small btn-success">${_(u'Add Child Group')}</a>
25 </li>
26 </ul>
27 </%def>
28
29 <%def name="menu_bar_nav()">
30 ${self.menu_items(active='admin')}
15 <%def name="menu_bar_subnav()">
16 ${self.repo_group_menu(active='options')}
31 17 </%def>
32 18
33 19 <%def name="main_content()">
@@ -35,10 +21,10 b''
35 21 </%def>
36 22
37 23 <%def name="main()">
24
38 25 <div class="box">
39 26 <div class="title">
40 ${self.breadcrumbs()}
41 ${self.breadcrumbs_side_links()}
27 ${self.repo_group_page_title(c.repo_group)}
42 28 </div>
43 29
44 30 <div class="sidebar-col-wrapper">
@@ -58,4 +44,5 b''
58 44
59 45 </div>
60 46 </div>
47
61 48 </%def>
@@ -36,7 +36,7 b''
36 36 <div class="box">
37 37 <div class="title">
38 38 ${self.repo_page_title(c.rhodecode_db_repo)}
39 ${self.breadcrumbs()}
39
40 40 </div>
41 41
42 42 <div class="sidebar-col-wrapper scw-small">
@@ -180,7 +180,7 b''
180 180 %endif
181 181
182 182 ## repo name with group name
183 ${h.breadcrumb_repo_link(c.rhodecode_db_repo)}
183 ${h.breadcrumb_repo_link(repo_instance)}
184 184
185 185 </div>
186 186
@@ -251,7 +251,7 b''
251 251 </a>
252 252 <ul class="submenu">
253 253 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
254 <li><a href="${h.route_path('edit_repo',repo_name=c.repo_name)}">${_('Settings')}</a></li>
254 <li><a href="${h.route_path('edit_repo',repo_name=c.repo_name)}">${_('Repository Settings')}</a></li>
255 255 %endif
256 256 %if c.rhodecode_db_repo.fork:
257 257 <li>
@@ -297,6 +297,73 b''
297 297
298 298 </%def>
299 299
300 <%def name="repo_group_page_title(repo_group_instance)">
301 <div class="title-content">
302 <div class="title-main">
303 ## Repository Group icon
304 <i class="icon-folder-close"></i>
305
306 ## repo name with group name
307 ${h.breadcrumb_repo_group_link(repo_group_instance)}
308 </div>
309
310 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
311 <div class="repo-group-desc">
312 ${dt.repo_group_desc(repo_group_instance.description_safe, repo_group_instance.personal, c.visual.stylify_metatags)}
313 </div>
314
315 </div>
316 </%def>
317
318 <%def name="repo_group_menu(active=None)">
319 <%
320 def is_active(selected):
321 if selected == active:
322 return "active"
323
324 is_admin = h.HasPermissionAny('hg.admin')('can create repos index page')
325
326 gr_name = c.repo_group.group_name if c.repo_group else None
327 # create repositories with write permission on group is set to true
328 create_on_write = h.HasPermissionAny('hg.create.write_on_repogroup.true')()
329 group_admin = h.HasRepoGroupPermissionAny('group.admin')(gr_name, 'group admin index page')
330 group_write = h.HasRepoGroupPermissionAny('group.write')(gr_name, 'can write into group index page')
331
332 %>
333
334 <!--- CONTEXT BAR -->
335 <div id="context-bar">
336 <div class="wrapper">
337 <ul id="context-pages" class="navigation horizontal-list">
338 <li class="${is_active('home')}"><a class="menulink" href="${h.route_path('repo_group_home', repo_group_name=c.repo_group.group_name)}"><div class="menulabel">${_('Group Home')}</div></a></li>
339 <li class="${is_active('search')}"><a class="menulink" href="${h.route_path('search_repo_group', repo_group_name=c.repo_group.group_name)}"><div class="menulabel">${_('Search')}</div></a></li>
340
341 <li class="${is_active('options')}">
342 <a class="menulink dropdown">
343 <div class="menulabel">${_('Options')} <div class="show_more"></div></div>
344 </a>
345 <ul class="submenu">
346 %if is_admin or group_admin:
347 <li><a href="${h.route_path('edit_repo_group',repo_group_name=c.repo_group.group_name)}" title="${_('You have admin right to this group, and can edit it')}">${_('Group Settings')}</a></li>
348 %endif
349 %if is_admin or group_admin or (group_write and create_on_write):
350 <li><a href="${h.route_path('repo_new',_query=dict(parent_group=c.repo_group.group_id))}">${_('Add Repository')}</a></li>
351 %endif
352 %if is_admin or group_admin:
353 <li><a href="${h.route_path('repo_group_new',_query=dict(parent_group=c.repo_group.group_id))}">${_(u'Add Parent Group')}</a></li>
354 %endif
355 </ul>
356 </li>
357 </ul>
358 </div>
359 <div class="clear"></div>
360 </div>
361
362 <!--- END CONTEXT BAR -->
363
364 </%def>
365
366
300 367 <%def name="usermenu(active=False)">
301 368 ## USER MENU
302 369 <li id="quick_login_li" class="${'active' if active else ''}">
@@ -1,13 +1,27 b''
1 1 <%inherit file="/base/base.mako"/>
2 2
3
4 <%def name="menu_bar_subnav()">
5 % if c.repo_group:
6 ${self.repo_group_menu(active='home')}
7 % endif
8 </%def>
9
10
3 11 <%def name="main()">
4 12 <div class="box">
5 13 <!-- box / title -->
6 14 <div class="title">
7 <div class="block-left breadcrumbs">
8 ${self.breadcrumbs()}
9 <span id="match_container" style="display:none"><span id="match_count">0</span> ${_('matches')}</span>
15 % if c.repo_group:
16 ${self.repo_group_page_title(c.repo_group)}
17 ## context actions
18 <div>
19 <ul class="links icon-only-links block-right">
20 <li></li>
21 </ul>
10 22 </div>
23 % endif
24
11 25 %if c.rhodecode_user.username != h.DEFAULT_USER:
12 26 <div class="block-right">
13 27 <%
@@ -15,12 +29,6 b''
15 29 create_repo = h.HasPermissionAny('hg.create.repository')('can create repository index page')
16 30 create_repo_group = h.HasPermissionAny('hg.repogroup.create.true')('can create repository groups index page')
17 31 create_user_group = h.HasPermissionAny('hg.usergroup.create.true')('can create user groups index page')
18
19 gr_name = c.repo_group.group_name if c.repo_group else None
20 # create repositories with write permission on group is set to true
21 create_on_write = h.HasPermissionAny('hg.create.write_on_repogroup.true')()
22 group_admin = h.HasRepoGroupPermissionAny('group.admin')(gr_name, 'group admin index page')
23 group_write = h.HasRepoGroupPermissionAny('group.write')(gr_name, 'can write into group index page')
24 32 %>
25 33
26 34 %if not c.repo_group:
@@ -32,17 +40,6 b''
32 40 %if is_admin or create_repo_group:
33 41 <a href="${h.route_path('repo_group_new')}" class="btn btn-small btn-default">${_(u'Add Repository Group')}</a>
34 42 %endif
35 %else:
36 ##we're inside other repository group other terms apply
37 %if is_admin or group_admin or (group_write and create_on_write):
38 <a href="${h.route_path('repo_new',_query=dict(parent_group=c.repo_group.group_id))}" class="btn btn-small btn-success btn-primary">${_('Add Repository')}</a>
39 %endif
40 %if is_admin or group_admin:
41 <a href="${h.route_path('repo_group_new',_query=dict(parent_group=c.repo_group.group_id))}" class="btn btn-small btn-default">${_(u'Add Repository Group')}</a>
42 %endif
43 %if is_admin or group_admin:
44 <a href="${h.route_path('edit_repo_group',repo_group_name=c.repo_group.group_name)}" title="${_('You have admin right to this group, and can edit it')}" class="btn btn-small btn-primary">${_('Edit Repository Group')}</a>
45 %endif
46 43 %endif
47 44 </div>
48 45 %endif
@@ -33,7 +33,7 b''
33 33
34 34
35 35 <div class="pr-details-title">
36 ${_('Pull request summary')}
36 ${_('Summary')}
37 37 </div>
38 38
39 39 <div class="form" style="padding-top: 10px">
@@ -3,7 +3,9 b''
3 3
4 4 <%def name="title()">
5 5 %if c.repo_name:
6 ${_('Search inside repository %(repo_name)s') % {'repo_name': c.repo_name}}
6 ${_('Search inside repository {repo_name}').format(repo_name=c.repo_name)}
7 %elif c.repo_group_name:
8 ${_('Search inside repository group {repo_group_name}').format(repo_group_name=c.repo_group_name)}
7 9 %else:
8 10 ${_('Search inside all accessible repositories')}
9 11 %endif
@@ -14,7 +16,9 b''
14 16
15 17 <%def name="breadcrumbs_links()">
16 18 %if c.repo_name:
17 ${_('Search inside repository %(repo_name)s') % {'repo_name': c.repo_name}}
19 ${_('Search inside repository {repo_name}').format(repo_name=c.repo_name)}
20 %elif c.repo_group_name:
21 ${_('Search inside repository group {repo_group_name}').format(repo_group_name=c.repo_group_name)}
18 22 %else:
19 23 ${_('Search inside all accessible repositories')}
20 24 %endif
@@ -23,7 +27,9 b''
23 27
24 28 <%def name="menu_bar_nav()">
25 29 %if c.repo_name:
26 ${self.menu_items(active='repositories')}
30 ${self.menu_items(active='search')}
31 %elif c.repo_group_name:
32 ${self.menu_items(active='search')}
27 33 %else:
28 34 ${self.menu_items(active='search')}
29 35 %endif
@@ -31,7 +37,9 b''
31 37
32 38 <%def name="menu_bar_subnav()">
33 39 %if c.repo_name:
34 ${self.repo_menu(active='search')}
40 ${self.repo_menu(active='search')}
41 %elif c.repo_group_name:
42 ${self.repo_group_menu(active='search')}
35 43 %endif
36 44 </%def>
37 45
@@ -43,6 +51,12 b''
43 51 ${self.repo_page_title(c.rhodecode_db_repo)}
44 52 </div>
45 53 ${h.form(h.route_path('search_repo',repo_name=c.repo_name),method='get')}
54 %elif c.repo_group_name:
55 <!-- box / title -->
56 <div class="title">
57 ${self.repo_group_page_title(c.repo_group)}
58 </div>
59 ${h.form(h.route_path('search_repo_group',repo_group_name=c.repo_group_name),method='get')}
46 60 %else:
47 61 <!-- box / title -->
48 62 <div class="title">
@@ -54,13 +68,43 b''
54 68 %endif
55 69 <div class="form search-form">
56 70 <div class="fields">
71
57 72 ${h.text('q', c.cur_query, placeholder="Enter query...")}
58 73
59 74 ${h.select('type',c.search_type,[('content',_('Files')), ('path',_('File path')),('commit',_('Commits'))],id='id_search_type')}
60 75 ${h.hidden('max_lines', '10')}
76
61 77 <input type="submit" value="${_('Search')}" class="btn"/>
62 78 <br/>
63 79
80 <div class="search-tags">
81 %if c.repo_name:
82 <span class="tag tag-ok disabled">
83 %if h.is_hg(c.rhodecode_db_repo):
84 <i class="icon-hg"></i>
85 %endif
86 %if h.is_git(c.rhodecode_db_repo):
87 <i class="icon-git"></i>
88 %endif
89 %if h.is_svn(c.rhodecode_db_repo):
90 <i class="icon-svn"></i>
91 %endif
92 ${c.repo_name}
93 </span>
94
95 %elif c.repo_group_name:
96 <span class="tag tag-ok disabled">
97 <i class="icon-folder-close"></i>
98 ${c.repo_group_name}
99 </span>
100 %endif
101
102 % for search_tag in c.search_tags:
103 <span class="tag tag-ok disabled">${search_tag}</span>
104 % endfor
105
106 </div>
107
64 108 <div class="search-feedback-items">
65 109 % for error in c.errors:
66 110 <span class="error-message">
@@ -140,13 +184,18 b' search type: ${handler.search_type_label'
140 184 </div>
141 185 <script>
142 186 $(document).ready(function(){
143 $('#q').autoGrowInput();
144 187 $("#id_search_type").select2({
145 188 'containerCssClass': "drop-menu",
146 189 'dropdownCssClass': "drop-menu-dropdown",
147 190 'dropdownAutoWidth': true,
148 191 'minimumResultsForSearch': -1
149 192 });
193
194 $('#q').autoGrowInput({maxWidth: 920});
195
196 setTimeout(function() {
197 $('#q').keyup()
198 }, 1);
150 199 })
151 200 </script>
152 201 </%def>
@@ -11,15 +11,18 b''
11 11
12 12 <div class="title">
13 13 ${self.repo_page_title(c.rhodecode_db_repo)}
14 <ul class="links icon-only-links block-right">
15 <li>
16 %if c.rhodecode_user.username != h.DEFAULT_USER:
17 <a href="${h.route_path('atom_feed_home', repo_name=c.rhodecode_db_repo.repo_name, _query=dict(auth_token=c.rhodecode_user.feed_token))}" title="${_('RSS Feed')}"><i class="icon-rss-sign"></i></a>
18 %else:
19 <a href="${h.route_path('atom_feed_home', repo_name=c.rhodecode_db_repo.repo_name)}" title="${_('RSS Feed')}"><i class="icon-rss-sign"></i></a>
20 %endif
21 </li>
22 </ul>
14 ## Context Action
15 <div>
16 <ul class="links icon-only-links block-right">
17 <li>
18 %if c.rhodecode_user.username != h.DEFAULT_USER:
19 <a href="${h.route_path('atom_feed_home', repo_name=c.rhodecode_db_repo.repo_name, _query=dict(auth_token=c.rhodecode_user.feed_token))}" title="${_('RSS Feed')}" class="btn btn-sm"><i class="icon-rss-sign"></i>RSS</a>
20 %else:
21 <a href="${h.route_path('atom_feed_home', repo_name=c.rhodecode_db_repo.repo_name)}" title="${_('RSS Feed')}" class="btn btn-sm"><i class="icon-rss-sign"></i>RSS</a>
22 %endif
23 </li>
24 </ul>
25 </div>
23 26 </div>
24 27
25 28 <div id="repo-summary" class="summary">
General Comments 0
You need to be logged in to leave comments. Login now