diff --git a/rhodecode/apps/search/__init__.py b/rhodecode/apps/search/__init__.py
new file mode 100644
index 00000000..3b67d94c
--- /dev/null
+++ b/rhodecode/apps/search/__init__.py
@@ -0,0 +1,44 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2016-2017 RhodeCode GmbH
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License, version 3
+# (only), as published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+# This program is dual-licensed. If you wish to learn more about the
+# RhodeCode Enterprise Edition, including its added features, Support services,
+# and proprietary license terms, please see https://rhodecode.com/licenses/
+from rhodecode.apps._base import ADMIN_PREFIX
+
+
+def includeme(config):
+
+ config.add_route(
+ name='search',
+ pattern=ADMIN_PREFIX + '/search')
+
+ config.add_route(
+ name='search_repo',
+ pattern='/{repo_name:.*?[^/]}/search', repo_route=True)
+
+ # Scan module for configuration decorators.
+ config.scan()
+
+
+ # # FULL TEXT SEARCH
+ # rmap.connect('search', '%s/search' % (ADMIN_PREFIX,),
+ # controller='search')
+ # rmap.connect('search_repo_home', '/{repo_name}/search',
+ # controller='search',
+ # action='index',
+ # conditions={'function': check_repo},
+ # requirements=URL_NAME_REQUIREMENTS)
\ No newline at end of file
diff --git a/rhodecode/apps/search/tests/__init__.py b/rhodecode/apps/search/tests/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/rhodecode/tests/functional/test_search.py b/rhodecode/apps/search/tests/test_search.py
similarity index 83%
rename from rhodecode/tests/functional/test_search.py
rename to rhodecode/apps/search/tests/test_search.py
index b3748e1c..6c97a335 100644
--- a/rhodecode/tests/functional/test_search.py
+++ b/rhodecode/apps/search/tests/test_search.py
@@ -25,16 +25,27 @@ import pytest
from whoosh import query
from rhodecode.tests import (
- TestController, url, SkipTest, HG_REPO,
+ TestController, SkipTest, HG_REPO,
TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
from rhodecode.tests.utils import AssertResponse
+def route_path(name, **kwargs):
+ from rhodecode.apps._base import ADMIN_PREFIX
+ return {
+ 'search':
+ ADMIN_PREFIX + '/search',
+ 'search_repo':
+ '/{repo_name}/search',
+
+ }[name].format(**kwargs)
+
+
class TestSearchController(TestController):
def test_index(self):
self.log_user()
- response = self.app.get(url(controller='search', action='index'))
+ response = self.app.get(route_path('search'))
assert_response = AssertResponse(response)
assert_response.one_element_exists('input#q')
@@ -43,17 +54,15 @@ class TestSearchController(TestController):
raise SkipTest('skipped due to existing index')
else:
self.log_user()
- response = self.app.get(url(controller='search', action='index'),
+ response = self.app.get(route_path('search'),
{'q': HG_REPO})
response.mustcontain('There is no index to search in. '
'Please run whoosh indexer')
def test_search_validation(self):
self.log_user()
- response = self.app.get(
- url(controller='search', action='index'), {'q': query,
- 'type': 'content',
- 'page_limit': 1000})
+ response = self.app.get(route_path('search'),
+ {'q': query, 'type': 'content', 'page_limit': 1000})
response.mustcontain(
'page_limit - 1000 is greater than maximum value 500')
@@ -88,10 +97,8 @@ class TestSearchController(TestController):
])
def test_search_files(self, query, expected_hits, expected_paths):
self.log_user()
- response = self.app.get(
- url(controller='search', action='index'), {'q': query,
- 'type': 'content',
- 'page_limit': 500})
+ response = self.app.get(route_path('search'),
+ {'q': query, 'type': 'content', 'page_limit': 500})
response.mustcontain('%s results' % expected_hits)
for path in expected_paths:
@@ -137,10 +144,8 @@ class TestSearchController(TestController):
def test_search_commit_messages(
self, query, expected_hits, expected_commits, enabled_backends):
self.log_user()
- response = self.app.get(
- url(controller='search', action='index'), {'q': query,
- 'type': 'commit',
- 'page_limit': 500})
+ response = self.app.get(route_path('search'),
+ {'q': query, 'type': 'commit', 'page_limit': 500})
response.mustcontain('%s results' % expected_hits)
for backend, commit_id in expected_commits:
@@ -156,10 +161,8 @@ class TestSearchController(TestController):
])
def test_search_file_paths(self, query, expected_hits, expected_paths):
self.log_user()
- response = self.app.get(
- url(controller='search', action='index'), {'q': query,
- 'type': 'path',
- 'page_limit': 500})
+ response = self.app.get(route_path('search'),
+ {'q': query, 'type': 'path', 'page_limit': 500})
response.mustcontain('%s results' % expected_hits)
for path in expected_paths:
@@ -168,8 +171,7 @@ class TestSearchController(TestController):
def test_search_commit_message_specific_repo(self, backend):
self.log_user()
response = self.app.get(
- url(controller='search', action='index',
- repo_name=backend.repo_name),
+ route_path('search_repo',repo_name=backend.repo_name),
{'q': 'bother to ask where to fetch repo during tests',
'type': 'commit'})
@@ -178,8 +180,7 @@ class TestSearchController(TestController):
def test_filters_are_not_applied_for_admin_user(self):
self.log_user()
with mock.patch('whoosh.searching.Searcher.search') as search_mock:
- self.app.get(
- url(controller='search', action='index'),
+ self.app.get(route_path('search'),
{'q': 'test query', 'type': 'commit'})
assert search_mock.call_count == 1
_, kwargs = search_mock.call_args
@@ -188,8 +189,7 @@ class TestSearchController(TestController):
def test_filters_are_applied_for_normal_user(self, enabled_backends):
self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
with mock.patch('whoosh.searching.Searcher.search') as search_mock:
- self.app.get(
- url(controller='search', action='index'),
+ self.app.get(route_path('search'),
{'q': 'test query', 'type': 'commit'})
assert search_mock.call_count == 1
_, kwargs = search_mock.call_args
diff --git a/rhodecode/apps/search/views.py b/rhodecode/apps/search/views.py
new file mode 100644
index 00000000..1230bf46
--- /dev/null
+++ b/rhodecode/apps/search/views.py
@@ -0,0 +1,133 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2011-2017 RhodeCode GmbH
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License, version 3
+# (only), as published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+# This program is dual-licensed. If you wish to learn more about the
+# RhodeCode Enterprise Edition, including its added features, Support services,
+# and proprietary license terms, please see https://rhodecode.com/licenses/
+
+import logging
+import urllib
+from pyramid.view import view_config
+from webhelpers.util import update_params
+
+from rhodecode.apps._base import BaseAppView, RepoAppView
+from rhodecode.lib.auth import (LoginRequired, HasRepoPermissionAnyDecorator)
+from rhodecode.lib.helpers import Page
+from rhodecode.lib.utils2 import safe_str, safe_int
+from rhodecode.lib.index import searcher_from_config
+from rhodecode.model import validation_schema
+from rhodecode.model.validation_schema.schemas import search_schema
+
+log = logging.getLogger(__name__)
+
+
+def search(request, tmpl_context, repo_name):
+ searcher = searcher_from_config(request.registry.settings)
+ formatted_results = []
+ execution_time = ''
+
+ schema = search_schema.SearchParamsSchema()
+
+ search_params = {}
+ errors = []
+ try:
+ search_params = schema.deserialize(
+ dict(search_query=request.GET.get('q'),
+ search_type=request.GET.get('type'),
+ search_sort=request.GET.get('sort'),
+ page_limit=request.GET.get('page_limit'),
+ requested_page=request.GET.get('page'))
+ )
+ except validation_schema.Invalid as e:
+ errors = e.children
+
+ def url_generator(**kw):
+ q = urllib.quote(safe_str(search_query))
+ return update_params(
+ "?q=%s&type=%s" % (q, safe_str(search_type)), **kw)
+
+ c = tmpl_context
+ search_query = search_params.get('search_query')
+ search_type = search_params.get('search_type')
+ search_sort = search_params.get('search_sort')
+ if search_params.get('search_query'):
+ page_limit = search_params['page_limit']
+ requested_page = search_params['requested_page']
+
+ try:
+ search_result = searcher.search(
+ search_query, search_type, c.auth_user, repo_name,
+ requested_page, page_limit, search_sort)
+
+ formatted_results = Page(
+ search_result['results'], page=requested_page,
+ item_count=search_result['count'],
+ items_per_page=page_limit, url=url_generator)
+ finally:
+ searcher.cleanup()
+
+ if not search_result['error']:
+ execution_time = '%s results (%.3f seconds)' % (
+ search_result['count'],
+ search_result['runtime'])
+ elif not errors:
+ node = schema['search_query']
+ errors = [
+ validation_schema.Invalid(node, search_result['error'])]
+
+ c.perm_user = c.auth_user
+ c.repo_name = repo_name
+ c.sort = search_sort
+ c.url_generator = url_generator
+ c.errors = errors
+ c.formatted_results = formatted_results
+ c.runtime = execution_time
+ c.cur_query = search_query
+ c.search_type = search_type
+ c.searcher = searcher
+
+
+class SearchView(BaseAppView):
+ def load_default_context(self):
+ c = self._get_local_tmpl_context()
+ self._register_global_c(c)
+ return c
+
+ @LoginRequired()
+ @view_config(
+ route_name='search', request_method='GET',
+ renderer='rhodecode:templates/search/search.mako')
+ def search(self):
+ c = self.load_default_context()
+ search(self.request, c, repo_name=None)
+ return self._get_template_context(c)
+
+
+class SearchRepoView(RepoAppView):
+ def load_default_context(self):
+ c = self._get_local_tmpl_context()
+ self._register_global_c(c)
+ return c
+
+ @LoginRequired()
+ @HasRepoPermissionAnyDecorator('repository.admin')
+ @view_config(
+ route_name='search_repo', request_method='GET',
+ renderer='rhodecode:templates/search/search.mako')
+ def search_repo(self):
+ c = self.load_default_context()
+ search(self.request, c, repo_name=self.db_repo_name)
+ return self._get_template_context(c)
diff --git a/rhodecode/config/middleware.py b/rhodecode/config/middleware.py
index b7ec6a3f..0aa57c43 100644
--- a/rhodecode/config/middleware.py
+++ b/rhodecode/config/middleware.py
@@ -291,6 +291,7 @@ def includeme(config):
config.include('rhodecode.apps.login')
config.include('rhodecode.apps.home')
config.include('rhodecode.apps.repository')
+ config.include('rhodecode.apps.search')
config.include('rhodecode.apps.user_profile')
config.include('rhodecode.apps.my_account')
config.include('rhodecode.apps.svn_support')
diff --git a/rhodecode/config/routing.py b/rhodecode/config/routing.py
index a88054a9..0d698fce 100644
--- a/rhodecode/config/routing.py
+++ b/rhodecode/config/routing.py
@@ -614,15 +614,6 @@ def make_map(config):
controller='journal', action='toggle_following', jsroute=True,
conditions={'method': ['POST']})
- # FULL TEXT SEARCH
- rmap.connect('search', '%s/search' % (ADMIN_PREFIX,),
- controller='search')
- rmap.connect('search_repo_home', '/{repo_name}/search',
- controller='search',
- action='index',
- conditions={'function': check_repo},
- requirements=URL_NAME_REQUIREMENTS)
-
# FEEDS
rmap.connect('rss_feed_home', '/{repo_name}/feed/rss',
controller='feed', action='rss',
diff --git a/rhodecode/controllers/search.py b/rhodecode/controllers/search.py
deleted file mode 100644
index 00750942..00000000
--- a/rhodecode/controllers/search.py
+++ /dev/null
@@ -1,112 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# Copyright (C) 2010-2017 RhodeCode GmbH
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License, version 3
-# (only), as published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-#
-# This program is dual-licensed. If you wish to learn more about the
-# RhodeCode Enterprise Edition, including its added features, Support services,
-# and proprietary license terms, please see https://rhodecode.com/licenses/
-
-"""
-Search controller for RhodeCode
-"""
-
-import logging
-import urllib
-
-from pylons import request, config, tmpl_context as c
-
-from webhelpers.util import update_params
-
-from rhodecode.lib.auth import LoginRequired, AuthUser
-from rhodecode.lib.base import BaseRepoController, render
-from rhodecode.lib.helpers import Page
-from rhodecode.lib.utils2 import safe_str, safe_int
-from rhodecode.lib.index import searcher_from_config
-from rhodecode.model import validation_schema
-from rhodecode.model.validation_schema.schemas import search_schema
-
-log = logging.getLogger(__name__)
-
-
-class SearchController(BaseRepoController):
-
- @LoginRequired()
- def index(self, repo_name=None):
-
- searcher = searcher_from_config(config)
- formatted_results = []
- execution_time = ''
-
- schema = search_schema.SearchParamsSchema()
-
- search_params = {}
- errors = []
- try:
- search_params = schema.deserialize(
- dict(search_query=request.GET.get('q'),
- search_type=request.GET.get('type'),
- search_sort=request.GET.get('sort'),
- page_limit=request.GET.get('page_limit'),
- requested_page=request.GET.get('page'))
- )
- except validation_schema.Invalid as e:
- errors = e.children
-
- def url_generator(**kw):
- q = urllib.quote(safe_str(search_query))
- return update_params(
- "?q=%s&type=%s" % (q, safe_str(search_type)), **kw)
-
- search_query = search_params.get('search_query')
- search_type = search_params.get('search_type')
- search_sort = search_params.get('search_sort')
- if search_params.get('search_query'):
- page_limit = search_params['page_limit']
- requested_page = search_params['requested_page']
-
- c.perm_user = AuthUser(user_id=c.rhodecode_user.user_id,
- ip_addr=self.ip_addr)
-
- try:
- search_result = searcher.search(
- search_query, search_type, c.perm_user, repo_name,
- requested_page, page_limit, search_sort)
-
- formatted_results = Page(
- search_result['results'], page=requested_page,
- item_count=search_result['count'],
- items_per_page=page_limit, url=url_generator)
- finally:
- searcher.cleanup()
-
- if not search_result['error']:
- execution_time = '%s results (%.3f seconds)' % (
- search_result['count'],
- search_result['runtime'])
- elif not errors:
- node = schema['search_query']
- errors = [
- validation_schema.Invalid(node, search_result['error'])]
-
- c.sort = search_sort
- c.url_generator = url_generator
- c.errors = errors
- c.formatted_results = formatted_results
- c.runtime = execution_time
- c.cur_query = search_query
- c.search_type = search_type
- c.searcher = searcher
- # Return a rendered template
- return render('/search/search.mako')
diff --git a/rhodecode/templates/base/base.mako b/rhodecode/templates/base/base.mako
index 128322d8..45de2b6b 100644
--- a/rhodecode/templates/base/base.mako
+++ b/rhodecode/templates/base/base.mako
@@ -253,7 +253,7 @@
${_('Compare fork')}
%endif
-
${_('Search')}
+ ${_('Search')}
%if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking:
%if c.rhodecode_db_repo.locked[0]:
@@ -399,7 +399,7 @@
-
diff --git a/rhodecode/templates/search/search.mako b/rhodecode/templates/search/search.mako
index 76a3bd9d..15d43d76 100644
--- a/rhodecode/templates/search/search.mako
+++ b/rhodecode/templates/search/search.mako
@@ -45,7 +45,7 @@
${self.repo_page_title(c.rhodecode_db_repo)}
- ${h.form(h.url('search_repo_home',repo_name=c.repo_name),method='get')}
+ ${h.form(h.route_path('search_repo',repo_name=c.repo_name),method='get')}
%else:
- ${h.form(h.url('search'),method='get')}
+ ${h.form(h.route_path('search'), method='get')}
%endif