##// END OF EJS Templates
search: moved search into pyramid views.
marcink -
r1685:0f027159 default
parent child Browse files
Show More
@@ -0,0 +1,44 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2016-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 from rhodecode.apps._base import ADMIN_PREFIX
21
22
23 def includeme(config):
24
25 config.add_route(
26 name='search',
27 pattern=ADMIN_PREFIX + '/search')
28
29 config.add_route(
30 name='search_repo',
31 pattern='/{repo_name:.*?[^/]}/search', repo_route=True)
32
33 # Scan module for configuration decorators.
34 config.scan()
35
36
37 # # FULL TEXT SEARCH
38 # rmap.connect('search', '%s/search' % (ADMIN_PREFIX,),
39 # controller='search')
40 # rmap.connect('search_repo_home', '/{repo_name}/search',
41 # controller='search',
42 # action='index',
43 # conditions={'function': check_repo},
44 # requirements=URL_NAME_REQUIREMENTS) No newline at end of file
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
@@ -0,0 +1,202 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 import os
22
23 import mock
24 import pytest
25 from whoosh import query
26
27 from rhodecode.tests import (
28 TestController, SkipTest, HG_REPO,
29 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
30 from rhodecode.tests.utils import AssertResponse
31
32
33 def route_path(name, **kwargs):
34 from rhodecode.apps._base import ADMIN_PREFIX
35 return {
36 'search':
37 ADMIN_PREFIX + '/search',
38 'search_repo':
39 '/{repo_name}/search',
40
41 }[name].format(**kwargs)
42
43
44 class TestSearchController(TestController):
45
46 def test_index(self):
47 self.log_user()
48 response = self.app.get(route_path('search'))
49 assert_response = AssertResponse(response)
50 assert_response.one_element_exists('input#q')
51
52 def test_search_files_empty_search(self):
53 if os.path.isdir(self.index_location):
54 raise SkipTest('skipped due to existing index')
55 else:
56 self.log_user()
57 response = self.app.get(route_path('search'),
58 {'q': HG_REPO})
59 response.mustcontain('There is no index to search in. '
60 'Please run whoosh indexer')
61
62 def test_search_validation(self):
63 self.log_user()
64 response = self.app.get(route_path('search'),
65 {'q': query, 'type': 'content', 'page_limit': 1000})
66
67 response.mustcontain(
68 'page_limit - 1000 is greater than maximum value 500')
69
70 @pytest.mark.parametrize("query, expected_hits, expected_paths", [
71 ('todo', 23, [
72 'vcs/backends/hg/inmemory.py',
73 'vcs/tests/test_git.py']),
74 ('extension:rst installation', 6, [
75 'docs/index.rst',
76 'docs/installation.rst']),
77 ('def repo', 87, [
78 'vcs/tests/test_git.py',
79 'vcs/tests/test_changesets.py']),
80 ('repository:%s def test' % HG_REPO, 18, [
81 'vcs/tests/test_git.py',
82 'vcs/tests/test_changesets.py']),
83 ('"def main"', 9, [
84 'vcs/__init__.py',
85 'vcs/tests/__init__.py',
86 'vcs/utils/progressbar.py']),
87 ('owner:test_admin', 358, [
88 'vcs/tests/base.py',
89 'MANIFEST.in',
90 'vcs/utils/termcolors.py',
91 'docs/theme/ADC/static/documentation.png']),
92 ('owner:test_admin def main', 72, [
93 'vcs/__init__.py',
94 'vcs/tests/test_utils_filesize.py',
95 'vcs/tests/test_cli.py']),
96 ('owner:michał test', 0, []),
97 ])
98 def test_search_files(self, query, expected_hits, expected_paths):
99 self.log_user()
100 response = self.app.get(route_path('search'),
101 {'q': query, 'type': 'content', 'page_limit': 500})
102
103 response.mustcontain('%s results' % expected_hits)
104 for path in expected_paths:
105 response.mustcontain(path)
106
107 @pytest.mark.parametrize("query, expected_hits, expected_commits", [
108 ('bother to ask where to fetch repo during tests', 3, [
109 ('hg', 'a00c1b6f5d7a6ae678fd553a8b81d92367f7ecf1'),
110 ('git', 'c6eb379775c578a95dad8ddab53f963b80894850'),
111 ('svn', '98')]),
112 ('michał', 0, []),
113 ('changed:tests/utils.py', 36, [
114 ('hg', 'a00c1b6f5d7a6ae678fd553a8b81d92367f7ecf1')]),
115 ('changed:vcs/utils/archivers.py', 11, [
116 ('hg', '25213a5fbb048dff8ba65d21e466a835536e5b70'),
117 ('hg', '47aedd538bf616eedcb0e7d630ea476df0e159c7'),
118 ('hg', 'f5d23247fad4856a1dabd5838afade1e0eed24fb'),
119 ('hg', '04ad456aefd6461aea24f90b63954b6b1ce07b3e'),
120 ('git', 'c994f0de03b2a0aa848a04fc2c0d7e737dba31fc'),
121 ('git', 'd1f898326327e20524fe22417c22d71064fe54a1'),
122 ('git', 'fe568b4081755c12abf6ba673ba777fc02a415f3'),
123 ('git', 'bafe786f0d8c2ff7da5c1dcfcfa577de0b5e92f1')]),
124 ('added:README.rst', 3, [
125 ('hg', '3803844fdbd3b711175fc3da9bdacfcd6d29a6fb'),
126 ('git', 'ff7ca51e58c505fec0dd2491de52c622bb7a806b'),
127 ('svn', '8')]),
128 ('changed:lazy.py', 15, [
129 ('hg', 'eaa291c5e6ae6126a203059de9854ccf7b5baa12'),
130 ('git', '17438a11f72b93f56d0e08e7d1fa79a378578a82'),
131 ('svn', '82'),
132 ('svn', '262'),
133 ('hg', 'f5d23247fad4856a1dabd5838afade1e0eed24fb'),
134 ('git', '33fa3223355104431402a888fa77a4e9956feb3e')
135 ]),
136 ('author:marcin@python-blog.com '
137 'commit_id:b986218ba1c9b0d6a259fac9b050b1724ed8e545', 1, [
138 ('hg', 'b986218ba1c9b0d6a259fac9b050b1724ed8e545')]),
139 ('b986218ba1c9b0d6a259fac9b050b1724ed8e545', 1, [
140 ('hg', 'b986218ba1c9b0d6a259fac9b050b1724ed8e545')]),
141 ('b986218b', 1, [
142 ('hg', 'b986218ba1c9b0d6a259fac9b050b1724ed8e545')]),
143 ])
144 def test_search_commit_messages(
145 self, query, expected_hits, expected_commits, enabled_backends):
146 self.log_user()
147 response = self.app.get(route_path('search'),
148 {'q': query, 'type': 'commit', 'page_limit': 500})
149
150 response.mustcontain('%s results' % expected_hits)
151 for backend, commit_id in expected_commits:
152 if backend in enabled_backends:
153 response.mustcontain(commit_id)
154
155 @pytest.mark.parametrize("query, expected_hits, expected_paths", [
156 ('readme.rst', 3, []),
157 ('test*', 75, []),
158 ('*model*', 1, []),
159 ('extension:rst', 48, []),
160 ('extension:rst api', 24, []),
161 ])
162 def test_search_file_paths(self, query, expected_hits, expected_paths):
163 self.log_user()
164 response = self.app.get(route_path('search'),
165 {'q': query, 'type': 'path', 'page_limit': 500})
166
167 response.mustcontain('%s results' % expected_hits)
168 for path in expected_paths:
169 response.mustcontain(path)
170
171 def test_search_commit_message_specific_repo(self, backend):
172 self.log_user()
173 response = self.app.get(
174 route_path('search_repo',repo_name=backend.repo_name),
175 {'q': 'bother to ask where to fetch repo during tests',
176 'type': 'commit'})
177
178 response.mustcontain('1 results')
179
180 def test_filters_are_not_applied_for_admin_user(self):
181 self.log_user()
182 with mock.patch('whoosh.searching.Searcher.search') as search_mock:
183 self.app.get(route_path('search'),
184 {'q': 'test query', 'type': 'commit'})
185 assert search_mock.call_count == 1
186 _, kwargs = search_mock.call_args
187 assert kwargs['filter'] is None
188
189 def test_filters_are_applied_for_normal_user(self, enabled_backends):
190 self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
191 with mock.patch('whoosh.searching.Searcher.search') as search_mock:
192 self.app.get(route_path('search'),
193 {'q': 'test query', 'type': 'commit'})
194 assert search_mock.call_count == 1
195 _, kwargs = search_mock.call_args
196 assert isinstance(kwargs['filter'], query.Or)
197 expected_repositories = [
198 'vcs_test_{}'.format(b) for b in enabled_backends]
199 queried_repositories = [
200 name for type_, name in kwargs['filter'].all_terms()]
201 for repository in expected_repositories:
202 assert repository in queried_repositories
@@ -0,0 +1,133 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2011-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 import logging
22 import urllib
23 from pyramid.view import view_config
24 from webhelpers.util import update_params
25
26 from rhodecode.apps._base import BaseAppView, RepoAppView
27 from rhodecode.lib.auth import (LoginRequired, HasRepoPermissionAnyDecorator)
28 from rhodecode.lib.helpers import Page
29 from rhodecode.lib.utils2 import safe_str, safe_int
30 from rhodecode.lib.index import searcher_from_config
31 from rhodecode.model import validation_schema
32 from rhodecode.model.validation_schema.schemas import search_schema
33
34 log = logging.getLogger(__name__)
35
36
37 def search(request, tmpl_context, repo_name):
38 searcher = searcher_from_config(request.registry.settings)
39 formatted_results = []
40 execution_time = ''
41
42 schema = search_schema.SearchParamsSchema()
43
44 search_params = {}
45 errors = []
46 try:
47 search_params = schema.deserialize(
48 dict(search_query=request.GET.get('q'),
49 search_type=request.GET.get('type'),
50 search_sort=request.GET.get('sort'),
51 page_limit=request.GET.get('page_limit'),
52 requested_page=request.GET.get('page'))
53 )
54 except validation_schema.Invalid as e:
55 errors = e.children
56
57 def url_generator(**kw):
58 q = urllib.quote(safe_str(search_query))
59 return update_params(
60 "?q=%s&type=%s" % (q, safe_str(search_type)), **kw)
61
62 c = tmpl_context
63 search_query = search_params.get('search_query')
64 search_type = search_params.get('search_type')
65 search_sort = search_params.get('search_sort')
66 if search_params.get('search_query'):
67 page_limit = search_params['page_limit']
68 requested_page = search_params['requested_page']
69
70 try:
71 search_result = searcher.search(
72 search_query, search_type, c.auth_user, repo_name,
73 requested_page, page_limit, search_sort)
74
75 formatted_results = Page(
76 search_result['results'], page=requested_page,
77 item_count=search_result['count'],
78 items_per_page=page_limit, url=url_generator)
79 finally:
80 searcher.cleanup()
81
82 if not search_result['error']:
83 execution_time = '%s results (%.3f seconds)' % (
84 search_result['count'],
85 search_result['runtime'])
86 elif not errors:
87 node = schema['search_query']
88 errors = [
89 validation_schema.Invalid(node, search_result['error'])]
90
91 c.perm_user = c.auth_user
92 c.repo_name = repo_name
93 c.sort = search_sort
94 c.url_generator = url_generator
95 c.errors = errors
96 c.formatted_results = formatted_results
97 c.runtime = execution_time
98 c.cur_query = search_query
99 c.search_type = search_type
100 c.searcher = searcher
101
102
103 class SearchView(BaseAppView):
104 def load_default_context(self):
105 c = self._get_local_tmpl_context()
106 self._register_global_c(c)
107 return c
108
109 @LoginRequired()
110 @view_config(
111 route_name='search', request_method='GET',
112 renderer='rhodecode:templates/search/search.mako')
113 def search(self):
114 c = self.load_default_context()
115 search(self.request, c, repo_name=None)
116 return self._get_template_context(c)
117
118
119 class SearchRepoView(RepoAppView):
120 def load_default_context(self):
121 c = self._get_local_tmpl_context()
122 self._register_global_c(c)
123 return c
124
125 @LoginRequired()
126 @HasRepoPermissionAnyDecorator('repository.admin')
127 @view_config(
128 route_name='search_repo', request_method='GET',
129 renderer='rhodecode:templates/search/search.mako')
130 def search_repo(self):
131 c = self.load_default_context()
132 search(self.request, c, repo_name=self.db_repo_name)
133 return self._get_template_context(c)
@@ -1,513 +1,514 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
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
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
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/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 Pylons middleware initialization
22 Pylons middleware initialization
23 """
23 """
24 import logging
24 import logging
25 from collections import OrderedDict
25 from collections import OrderedDict
26
26
27 from paste.registry import RegistryManager
27 from paste.registry import RegistryManager
28 from paste.gzipper import make_gzip_middleware
28 from paste.gzipper import make_gzip_middleware
29 from pylons.wsgiapp import PylonsApp
29 from pylons.wsgiapp import PylonsApp
30 from pyramid.authorization import ACLAuthorizationPolicy
30 from pyramid.authorization import ACLAuthorizationPolicy
31 from pyramid.config import Configurator
31 from pyramid.config import Configurator
32 from pyramid.settings import asbool, aslist
32 from pyramid.settings import asbool, aslist
33 from pyramid.wsgi import wsgiapp
33 from pyramid.wsgi import wsgiapp
34 from pyramid.httpexceptions import (
34 from pyramid.httpexceptions import (
35 HTTPException, HTTPError, HTTPInternalServerError, HTTPFound)
35 HTTPException, HTTPError, HTTPInternalServerError, HTTPFound)
36 from pyramid.events import ApplicationCreated
36 from pyramid.events import ApplicationCreated
37 from pyramid.renderers import render_to_response
37 from pyramid.renderers import render_to_response
38 from routes.middleware import RoutesMiddleware
38 from routes.middleware import RoutesMiddleware
39 import routes.util
39 import routes.util
40
40
41 import rhodecode
41 import rhodecode
42 from rhodecode.model import meta
42 from rhodecode.model import meta
43 from rhodecode.config import patches
43 from rhodecode.config import patches
44 from rhodecode.config.routing import STATIC_FILE_PREFIX
44 from rhodecode.config.routing import STATIC_FILE_PREFIX
45 from rhodecode.config.environment import (
45 from rhodecode.config.environment import (
46 load_environment, load_pyramid_environment)
46 load_environment, load_pyramid_environment)
47 from rhodecode.lib.middleware import csrf
47 from rhodecode.lib.middleware import csrf
48 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
48 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
49 from rhodecode.lib.middleware.error_handling import (
49 from rhodecode.lib.middleware.error_handling import (
50 PylonsErrorHandlingMiddleware)
50 PylonsErrorHandlingMiddleware)
51 from rhodecode.lib.middleware.https_fixup import HttpsFixup
51 from rhodecode.lib.middleware.https_fixup import HttpsFixup
52 from rhodecode.lib.middleware.vcs import VCSMiddleware
52 from rhodecode.lib.middleware.vcs import VCSMiddleware
53 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
53 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
54 from rhodecode.lib.utils2 import aslist as rhodecode_aslist
54 from rhodecode.lib.utils2 import aslist as rhodecode_aslist
55 from rhodecode.subscribers import (
55 from rhodecode.subscribers import (
56 scan_repositories_if_enabled, write_js_routes_if_enabled,
56 scan_repositories_if_enabled, write_js_routes_if_enabled,
57 write_metadata_if_needed)
57 write_metadata_if_needed)
58
58
59
59
60 log = logging.getLogger(__name__)
60 log = logging.getLogger(__name__)
61
61
62
62
63 # this is used to avoid avoid the route lookup overhead in routesmiddleware
63 # this is used to avoid avoid the route lookup overhead in routesmiddleware
64 # for certain routes which won't go to pylons to - eg. static files, debugger
64 # for certain routes which won't go to pylons to - eg. static files, debugger
65 # it is only needed for the pylons migration and can be removed once complete
65 # it is only needed for the pylons migration and can be removed once complete
66 class SkippableRoutesMiddleware(RoutesMiddleware):
66 class SkippableRoutesMiddleware(RoutesMiddleware):
67 """ Routes middleware that allows you to skip prefixes """
67 """ Routes middleware that allows you to skip prefixes """
68
68
69 def __init__(self, *args, **kw):
69 def __init__(self, *args, **kw):
70 self.skip_prefixes = kw.pop('skip_prefixes', [])
70 self.skip_prefixes = kw.pop('skip_prefixes', [])
71 super(SkippableRoutesMiddleware, self).__init__(*args, **kw)
71 super(SkippableRoutesMiddleware, self).__init__(*args, **kw)
72
72
73 def __call__(self, environ, start_response):
73 def __call__(self, environ, start_response):
74 for prefix in self.skip_prefixes:
74 for prefix in self.skip_prefixes:
75 if environ['PATH_INFO'].startswith(prefix):
75 if environ['PATH_INFO'].startswith(prefix):
76 # added to avoid the case when a missing /_static route falls
76 # added to avoid the case when a missing /_static route falls
77 # through to pylons and causes an exception as pylons is
77 # through to pylons and causes an exception as pylons is
78 # expecting wsgiorg.routingargs to be set in the environ
78 # expecting wsgiorg.routingargs to be set in the environ
79 # by RoutesMiddleware.
79 # by RoutesMiddleware.
80 if 'wsgiorg.routing_args' not in environ:
80 if 'wsgiorg.routing_args' not in environ:
81 environ['wsgiorg.routing_args'] = (None, {})
81 environ['wsgiorg.routing_args'] = (None, {})
82 return self.app(environ, start_response)
82 return self.app(environ, start_response)
83
83
84 return super(SkippableRoutesMiddleware, self).__call__(
84 return super(SkippableRoutesMiddleware, self).__call__(
85 environ, start_response)
85 environ, start_response)
86
86
87
87
88 def make_app(global_conf, static_files=True, **app_conf):
88 def make_app(global_conf, static_files=True, **app_conf):
89 """Create a Pylons WSGI application and return it
89 """Create a Pylons WSGI application and return it
90
90
91 ``global_conf``
91 ``global_conf``
92 The inherited configuration for this application. Normally from
92 The inherited configuration for this application. Normally from
93 the [DEFAULT] section of the Paste ini file.
93 the [DEFAULT] section of the Paste ini file.
94
94
95 ``app_conf``
95 ``app_conf``
96 The application's local configuration. Normally specified in
96 The application's local configuration. Normally specified in
97 the [app:<name>] section of the Paste ini file (where <name>
97 the [app:<name>] section of the Paste ini file (where <name>
98 defaults to main).
98 defaults to main).
99
99
100 """
100 """
101 # Apply compatibility patches
101 # Apply compatibility patches
102 patches.kombu_1_5_1_python_2_7_11()
102 patches.kombu_1_5_1_python_2_7_11()
103 patches.inspect_getargspec()
103 patches.inspect_getargspec()
104
104
105 # Configure the Pylons environment
105 # Configure the Pylons environment
106 config = load_environment(global_conf, app_conf)
106 config = load_environment(global_conf, app_conf)
107
107
108 # The Pylons WSGI app
108 # The Pylons WSGI app
109 app = PylonsApp(config=config)
109 app = PylonsApp(config=config)
110 if rhodecode.is_test:
110 if rhodecode.is_test:
111 app = csrf.CSRFDetector(app)
111 app = csrf.CSRFDetector(app)
112
112
113 expected_origin = config.get('expected_origin')
113 expected_origin = config.get('expected_origin')
114 if expected_origin:
114 if expected_origin:
115 # The API can be accessed from other Origins.
115 # The API can be accessed from other Origins.
116 app = csrf.OriginChecker(app, expected_origin,
116 app = csrf.OriginChecker(app, expected_origin,
117 skip_urls=[routes.util.url_for('api')])
117 skip_urls=[routes.util.url_for('api')])
118
118
119 # Establish the Registry for this application
119 # Establish the Registry for this application
120 app = RegistryManager(app)
120 app = RegistryManager(app)
121
121
122 app.config = config
122 app.config = config
123
123
124 return app
124 return app
125
125
126
126
127 def make_pyramid_app(global_config, **settings):
127 def make_pyramid_app(global_config, **settings):
128 """
128 """
129 Constructs the WSGI application based on Pyramid and wraps the Pylons based
129 Constructs the WSGI application based on Pyramid and wraps the Pylons based
130 application.
130 application.
131
131
132 Specials:
132 Specials:
133
133
134 * We migrate from Pylons to Pyramid. While doing this, we keep both
134 * We migrate from Pylons to Pyramid. While doing this, we keep both
135 frameworks functional. This involves moving some WSGI middlewares around
135 frameworks functional. This involves moving some WSGI middlewares around
136 and providing access to some data internals, so that the old code is
136 and providing access to some data internals, so that the old code is
137 still functional.
137 still functional.
138
138
139 * The application can also be integrated like a plugin via the call to
139 * The application can also be integrated like a plugin via the call to
140 `includeme`. This is accompanied with the other utility functions which
140 `includeme`. This is accompanied with the other utility functions which
141 are called. Changing this should be done with great care to not break
141 are called. Changing this should be done with great care to not break
142 cases when these fragments are assembled from another place.
142 cases when these fragments are assembled from another place.
143
143
144 """
144 """
145 # The edition string should be available in pylons too, so we add it here
145 # The edition string should be available in pylons too, so we add it here
146 # before copying the settings.
146 # before copying the settings.
147 settings.setdefault('rhodecode.edition', 'Community Edition')
147 settings.setdefault('rhodecode.edition', 'Community Edition')
148
148
149 # As long as our Pylons application does expect "unprepared" settings, make
149 # As long as our Pylons application does expect "unprepared" settings, make
150 # sure that we keep an unmodified copy. This avoids unintentional change of
150 # sure that we keep an unmodified copy. This avoids unintentional change of
151 # behavior in the old application.
151 # behavior in the old application.
152 settings_pylons = settings.copy()
152 settings_pylons = settings.copy()
153
153
154 sanitize_settings_and_apply_defaults(settings)
154 sanitize_settings_and_apply_defaults(settings)
155 config = Configurator(settings=settings)
155 config = Configurator(settings=settings)
156 add_pylons_compat_data(config.registry, global_config, settings_pylons)
156 add_pylons_compat_data(config.registry, global_config, settings_pylons)
157
157
158 load_pyramid_environment(global_config, settings)
158 load_pyramid_environment(global_config, settings)
159
159
160 includeme_first(config)
160 includeme_first(config)
161 includeme(config)
161 includeme(config)
162 pyramid_app = config.make_wsgi_app()
162 pyramid_app = config.make_wsgi_app()
163 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
163 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
164 pyramid_app.config = config
164 pyramid_app.config = config
165
165
166 # creating the app uses a connection - return it after we are done
166 # creating the app uses a connection - return it after we are done
167 meta.Session.remove()
167 meta.Session.remove()
168
168
169 return pyramid_app
169 return pyramid_app
170
170
171
171
172 def make_not_found_view(config):
172 def make_not_found_view(config):
173 """
173 """
174 This creates the view which should be registered as not-found-view to
174 This creates the view which should be registered as not-found-view to
175 pyramid. Basically it contains of the old pylons app, converted to a view.
175 pyramid. Basically it contains of the old pylons app, converted to a view.
176 Additionally it is wrapped by some other middlewares.
176 Additionally it is wrapped by some other middlewares.
177 """
177 """
178 settings = config.registry.settings
178 settings = config.registry.settings
179 vcs_server_enabled = settings['vcs.server.enable']
179 vcs_server_enabled = settings['vcs.server.enable']
180
180
181 # Make pylons app from unprepared settings.
181 # Make pylons app from unprepared settings.
182 pylons_app = make_app(
182 pylons_app = make_app(
183 config.registry._pylons_compat_global_config,
183 config.registry._pylons_compat_global_config,
184 **config.registry._pylons_compat_settings)
184 **config.registry._pylons_compat_settings)
185 config.registry._pylons_compat_config = pylons_app.config
185 config.registry._pylons_compat_config = pylons_app.config
186
186
187 # Appenlight monitoring.
187 # Appenlight monitoring.
188 pylons_app, appenlight_client = wrap_in_appenlight_if_enabled(
188 pylons_app, appenlight_client = wrap_in_appenlight_if_enabled(
189 pylons_app, settings)
189 pylons_app, settings)
190
190
191 # The pylons app is executed inside of the pyramid 404 exception handler.
191 # The pylons app is executed inside of the pyramid 404 exception handler.
192 # Exceptions which are raised inside of it are not handled by pyramid
192 # Exceptions which are raised inside of it are not handled by pyramid
193 # again. Therefore we add a middleware that invokes the error handler in
193 # again. Therefore we add a middleware that invokes the error handler in
194 # case of an exception or error response. This way we return proper error
194 # case of an exception or error response. This way we return proper error
195 # HTML pages in case of an error.
195 # HTML pages in case of an error.
196 reraise = (settings.get('debugtoolbar.enabled', False) or
196 reraise = (settings.get('debugtoolbar.enabled', False) or
197 rhodecode.disable_error_handler)
197 rhodecode.disable_error_handler)
198 pylons_app = PylonsErrorHandlingMiddleware(
198 pylons_app = PylonsErrorHandlingMiddleware(
199 pylons_app, error_handler, reraise)
199 pylons_app, error_handler, reraise)
200
200
201 # The VCSMiddleware shall operate like a fallback if pyramid doesn't find a
201 # The VCSMiddleware shall operate like a fallback if pyramid doesn't find a
202 # view to handle the request. Therefore it is wrapped around the pylons
202 # view to handle the request. Therefore it is wrapped around the pylons
203 # app. It has to be outside of the error handling otherwise error responses
203 # app. It has to be outside of the error handling otherwise error responses
204 # from the vcsserver are converted to HTML error pages. This confuses the
204 # from the vcsserver are converted to HTML error pages. This confuses the
205 # command line tools and the user won't get a meaningful error message.
205 # command line tools and the user won't get a meaningful error message.
206 if vcs_server_enabled:
206 if vcs_server_enabled:
207 pylons_app = VCSMiddleware(
207 pylons_app = VCSMiddleware(
208 pylons_app, settings, appenlight_client, registry=config.registry)
208 pylons_app, settings, appenlight_client, registry=config.registry)
209
209
210 # Convert WSGI app to pyramid view and return it.
210 # Convert WSGI app to pyramid view and return it.
211 return wsgiapp(pylons_app)
211 return wsgiapp(pylons_app)
212
212
213
213
214 def add_pylons_compat_data(registry, global_config, settings):
214 def add_pylons_compat_data(registry, global_config, settings):
215 """
215 """
216 Attach data to the registry to support the Pylons integration.
216 Attach data to the registry to support the Pylons integration.
217 """
217 """
218 registry._pylons_compat_global_config = global_config
218 registry._pylons_compat_global_config = global_config
219 registry._pylons_compat_settings = settings
219 registry._pylons_compat_settings = settings
220
220
221
221
222 def error_handler(exception, request):
222 def error_handler(exception, request):
223 import rhodecode
223 import rhodecode
224 from rhodecode.lib.utils2 import AttributeDict
224 from rhodecode.lib.utils2 import AttributeDict
225
225
226 rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode'
226 rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode'
227
227
228 base_response = HTTPInternalServerError()
228 base_response = HTTPInternalServerError()
229 # prefer original exception for the response since it may have headers set
229 # prefer original exception for the response since it may have headers set
230 if isinstance(exception, HTTPException):
230 if isinstance(exception, HTTPException):
231 base_response = exception
231 base_response = exception
232
232
233 def is_http_error(response):
233 def is_http_error(response):
234 # error which should have traceback
234 # error which should have traceback
235 return response.status_code > 499
235 return response.status_code > 499
236
236
237 if is_http_error(base_response):
237 if is_http_error(base_response):
238 log.exception(
238 log.exception(
239 'error occurred handling this request for path: %s', request.path)
239 'error occurred handling this request for path: %s', request.path)
240
240
241 c = AttributeDict()
241 c = AttributeDict()
242 c.error_message = base_response.status
242 c.error_message = base_response.status
243 c.error_explanation = base_response.explanation or str(base_response)
243 c.error_explanation = base_response.explanation or str(base_response)
244 c.visual = AttributeDict()
244 c.visual = AttributeDict()
245
245
246 c.visual.rhodecode_support_url = (
246 c.visual.rhodecode_support_url = (
247 request.registry.settings.get('rhodecode_support_url') or
247 request.registry.settings.get('rhodecode_support_url') or
248 request.route_url('rhodecode_support')
248 request.route_url('rhodecode_support')
249 )
249 )
250 c.redirect_time = 0
250 c.redirect_time = 0
251 c.rhodecode_name = rhodecode_title
251 c.rhodecode_name = rhodecode_title
252 if not c.rhodecode_name:
252 if not c.rhodecode_name:
253 c.rhodecode_name = 'Rhodecode'
253 c.rhodecode_name = 'Rhodecode'
254
254
255 c.causes = []
255 c.causes = []
256 if hasattr(base_response, 'causes'):
256 if hasattr(base_response, 'causes'):
257 c.causes = base_response.causes
257 c.causes = base_response.causes
258
258
259 response = render_to_response(
259 response = render_to_response(
260 '/errors/error_document.mako', {'c': c}, request=request,
260 '/errors/error_document.mako', {'c': c}, request=request,
261 response=base_response)
261 response=base_response)
262
262
263 return response
263 return response
264
264
265
265
266 def includeme(config):
266 def includeme(config):
267 settings = config.registry.settings
267 settings = config.registry.settings
268
268
269 # plugin information
269 # plugin information
270 config.registry.rhodecode_plugins = OrderedDict()
270 config.registry.rhodecode_plugins = OrderedDict()
271
271
272 config.add_directive(
272 config.add_directive(
273 'register_rhodecode_plugin', register_rhodecode_plugin)
273 'register_rhodecode_plugin', register_rhodecode_plugin)
274
274
275 if asbool(settings.get('appenlight', 'false')):
275 if asbool(settings.get('appenlight', 'false')):
276 config.include('appenlight_client.ext.pyramid_tween')
276 config.include('appenlight_client.ext.pyramid_tween')
277
277
278 # Includes which are required. The application would fail without them.
278 # Includes which are required. The application would fail without them.
279 config.include('pyramid_mako')
279 config.include('pyramid_mako')
280 config.include('pyramid_beaker')
280 config.include('pyramid_beaker')
281
281
282 config.include('rhodecode.authentication')
282 config.include('rhodecode.authentication')
283 config.include('rhodecode.integrations')
283 config.include('rhodecode.integrations')
284
284
285 # apps
285 # apps
286 config.include('rhodecode.apps._base')
286 config.include('rhodecode.apps._base')
287 config.include('rhodecode.apps.ops')
287 config.include('rhodecode.apps.ops')
288
288
289 config.include('rhodecode.apps.admin')
289 config.include('rhodecode.apps.admin')
290 config.include('rhodecode.apps.channelstream')
290 config.include('rhodecode.apps.channelstream')
291 config.include('rhodecode.apps.login')
291 config.include('rhodecode.apps.login')
292 config.include('rhodecode.apps.home')
292 config.include('rhodecode.apps.home')
293 config.include('rhodecode.apps.repository')
293 config.include('rhodecode.apps.repository')
294 config.include('rhodecode.apps.search')
294 config.include('rhodecode.apps.user_profile')
295 config.include('rhodecode.apps.user_profile')
295 config.include('rhodecode.apps.my_account')
296 config.include('rhodecode.apps.my_account')
296 config.include('rhodecode.apps.svn_support')
297 config.include('rhodecode.apps.svn_support')
297
298
298 config.include('rhodecode.tweens')
299 config.include('rhodecode.tweens')
299 config.include('rhodecode.api')
300 config.include('rhodecode.api')
300
301
301 config.add_route(
302 config.add_route(
302 'rhodecode_support', 'https://rhodecode.com/help/', static=True)
303 'rhodecode_support', 'https://rhodecode.com/help/', static=True)
303
304
304 config.add_translation_dirs('rhodecode:i18n/')
305 config.add_translation_dirs('rhodecode:i18n/')
305 settings['default_locale_name'] = settings.get('lang', 'en')
306 settings['default_locale_name'] = settings.get('lang', 'en')
306
307
307 # Add subscribers.
308 # Add subscribers.
308 config.add_subscriber(scan_repositories_if_enabled, ApplicationCreated)
309 config.add_subscriber(scan_repositories_if_enabled, ApplicationCreated)
309 config.add_subscriber(write_metadata_if_needed, ApplicationCreated)
310 config.add_subscriber(write_metadata_if_needed, ApplicationCreated)
310 config.add_subscriber(write_js_routes_if_enabled, ApplicationCreated)
311 config.add_subscriber(write_js_routes_if_enabled, ApplicationCreated)
311
312
312 # Set the authorization policy.
313 # Set the authorization policy.
313 authz_policy = ACLAuthorizationPolicy()
314 authz_policy = ACLAuthorizationPolicy()
314 config.set_authorization_policy(authz_policy)
315 config.set_authorization_policy(authz_policy)
315
316
316 # Set the default renderer for HTML templates to mako.
317 # Set the default renderer for HTML templates to mako.
317 config.add_mako_renderer('.html')
318 config.add_mako_renderer('.html')
318
319
319 config.add_renderer(
320 config.add_renderer(
320 name='json_ext',
321 name='json_ext',
321 factory='rhodecode.lib.ext_json_renderer.pyramid_ext_json')
322 factory='rhodecode.lib.ext_json_renderer.pyramid_ext_json')
322
323
323 # include RhodeCode plugins
324 # include RhodeCode plugins
324 includes = aslist(settings.get('rhodecode.includes', []))
325 includes = aslist(settings.get('rhodecode.includes', []))
325 for inc in includes:
326 for inc in includes:
326 config.include(inc)
327 config.include(inc)
327
328
328 # This is the glue which allows us to migrate in chunks. By registering the
329 # This is the glue which allows us to migrate in chunks. By registering the
329 # pylons based application as the "Not Found" view in Pyramid, we will
330 # pylons based application as the "Not Found" view in Pyramid, we will
330 # fallback to the old application each time the new one does not yet know
331 # fallback to the old application each time the new one does not yet know
331 # how to handle a request.
332 # how to handle a request.
332 config.add_notfound_view(make_not_found_view(config))
333 config.add_notfound_view(make_not_found_view(config))
333
334
334 if not settings.get('debugtoolbar.enabled', False):
335 if not settings.get('debugtoolbar.enabled', False):
335 # if no toolbar, then any exception gets caught and rendered
336 # if no toolbar, then any exception gets caught and rendered
336 config.add_view(error_handler, context=Exception)
337 config.add_view(error_handler, context=Exception)
337
338
338 config.add_view(error_handler, context=HTTPError)
339 config.add_view(error_handler, context=HTTPError)
339
340
340
341
341 def includeme_first(config):
342 def includeme_first(config):
342 # redirect automatic browser favicon.ico requests to correct place
343 # redirect automatic browser favicon.ico requests to correct place
343 def favicon_redirect(context, request):
344 def favicon_redirect(context, request):
344 return HTTPFound(
345 return HTTPFound(
345 request.static_path('rhodecode:public/images/favicon.ico'))
346 request.static_path('rhodecode:public/images/favicon.ico'))
346
347
347 config.add_view(favicon_redirect, route_name='favicon')
348 config.add_view(favicon_redirect, route_name='favicon')
348 config.add_route('favicon', '/favicon.ico')
349 config.add_route('favicon', '/favicon.ico')
349
350
350 def robots_redirect(context, request):
351 def robots_redirect(context, request):
351 return HTTPFound(
352 return HTTPFound(
352 request.static_path('rhodecode:public/robots.txt'))
353 request.static_path('rhodecode:public/robots.txt'))
353
354
354 config.add_view(robots_redirect, route_name='robots')
355 config.add_view(robots_redirect, route_name='robots')
355 config.add_route('robots', '/robots.txt')
356 config.add_route('robots', '/robots.txt')
356
357
357 config.add_static_view(
358 config.add_static_view(
358 '_static/deform', 'deform:static')
359 '_static/deform', 'deform:static')
359 config.add_static_view(
360 config.add_static_view(
360 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
361 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
361
362
362
363
363 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
364 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
364 """
365 """
365 Apply outer WSGI middlewares around the application.
366 Apply outer WSGI middlewares around the application.
366
367
367 Part of this has been moved up from the Pylons layer, so that the
368 Part of this has been moved up from the Pylons layer, so that the
368 data is also available if old Pylons code is hit through an already ported
369 data is also available if old Pylons code is hit through an already ported
369 view.
370 view.
370 """
371 """
371 settings = config.registry.settings
372 settings = config.registry.settings
372
373
373 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
374 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
374 pyramid_app = HttpsFixup(pyramid_app, settings)
375 pyramid_app = HttpsFixup(pyramid_app, settings)
375
376
376 # Add RoutesMiddleware to support the pylons compatibility tween during
377 # Add RoutesMiddleware to support the pylons compatibility tween during
377 # migration to pyramid.
378 # migration to pyramid.
378 pyramid_app = SkippableRoutesMiddleware(
379 pyramid_app = SkippableRoutesMiddleware(
379 pyramid_app, config.registry._pylons_compat_config['routes.map'],
380 pyramid_app, config.registry._pylons_compat_config['routes.map'],
380 skip_prefixes=(STATIC_FILE_PREFIX, '/_debug_toolbar'))
381 skip_prefixes=(STATIC_FILE_PREFIX, '/_debug_toolbar'))
381
382
382 pyramid_app, _ = wrap_in_appenlight_if_enabled(pyramid_app, settings)
383 pyramid_app, _ = wrap_in_appenlight_if_enabled(pyramid_app, settings)
383
384
384 if settings['gzip_responses']:
385 if settings['gzip_responses']:
385 pyramid_app = make_gzip_middleware(
386 pyramid_app = make_gzip_middleware(
386 pyramid_app, settings, compress_level=1)
387 pyramid_app, settings, compress_level=1)
387
388
388 # this should be the outer most middleware in the wsgi stack since
389 # this should be the outer most middleware in the wsgi stack since
389 # middleware like Routes make database calls
390 # middleware like Routes make database calls
390 def pyramid_app_with_cleanup(environ, start_response):
391 def pyramid_app_with_cleanup(environ, start_response):
391 try:
392 try:
392 return pyramid_app(environ, start_response)
393 return pyramid_app(environ, start_response)
393 finally:
394 finally:
394 # Dispose current database session and rollback uncommitted
395 # Dispose current database session and rollback uncommitted
395 # transactions.
396 # transactions.
396 meta.Session.remove()
397 meta.Session.remove()
397
398
398 # In a single threaded mode server, on non sqlite db we should have
399 # In a single threaded mode server, on non sqlite db we should have
399 # '0 Current Checked out connections' at the end of a request,
400 # '0 Current Checked out connections' at the end of a request,
400 # if not, then something, somewhere is leaving a connection open
401 # if not, then something, somewhere is leaving a connection open
401 pool = meta.Base.metadata.bind.engine.pool
402 pool = meta.Base.metadata.bind.engine.pool
402 log.debug('sa pool status: %s', pool.status())
403 log.debug('sa pool status: %s', pool.status())
403
404
404
405
405 return pyramid_app_with_cleanup
406 return pyramid_app_with_cleanup
406
407
407
408
408 def sanitize_settings_and_apply_defaults(settings):
409 def sanitize_settings_and_apply_defaults(settings):
409 """
410 """
410 Applies settings defaults and does all type conversion.
411 Applies settings defaults and does all type conversion.
411
412
412 We would move all settings parsing and preparation into this place, so that
413 We would move all settings parsing and preparation into this place, so that
413 we have only one place left which deals with this part. The remaining parts
414 we have only one place left which deals with this part. The remaining parts
414 of the application would start to rely fully on well prepared settings.
415 of the application would start to rely fully on well prepared settings.
415
416
416 This piece would later be split up per topic to avoid a big fat monster
417 This piece would later be split up per topic to avoid a big fat monster
417 function.
418 function.
418 """
419 """
419
420
420 # Pyramid's mako renderer has to search in the templates folder so that the
421 # Pyramid's mako renderer has to search in the templates folder so that the
421 # old templates still work. Ported and new templates are expected to use
422 # old templates still work. Ported and new templates are expected to use
422 # real asset specifications for the includes.
423 # real asset specifications for the includes.
423 mako_directories = settings.setdefault('mako.directories', [
424 mako_directories = settings.setdefault('mako.directories', [
424 # Base templates of the original Pylons application
425 # Base templates of the original Pylons application
425 'rhodecode:templates',
426 'rhodecode:templates',
426 ])
427 ])
427 log.debug(
428 log.debug(
428 "Using the following Mako template directories: %s",
429 "Using the following Mako template directories: %s",
429 mako_directories)
430 mako_directories)
430
431
431 # Default includes, possible to change as a user
432 # Default includes, possible to change as a user
432 pyramid_includes = settings.setdefault('pyramid.includes', [
433 pyramid_includes = settings.setdefault('pyramid.includes', [
433 'rhodecode.lib.middleware.request_wrapper',
434 'rhodecode.lib.middleware.request_wrapper',
434 ])
435 ])
435 log.debug(
436 log.debug(
436 "Using the following pyramid.includes: %s",
437 "Using the following pyramid.includes: %s",
437 pyramid_includes)
438 pyramid_includes)
438
439
439 # TODO: johbo: Re-think this, usually the call to config.include
440 # TODO: johbo: Re-think this, usually the call to config.include
440 # should allow to pass in a prefix.
441 # should allow to pass in a prefix.
441 settings.setdefault('rhodecode.api.url', '/_admin/api')
442 settings.setdefault('rhodecode.api.url', '/_admin/api')
442
443
443 # Sanitize generic settings.
444 # Sanitize generic settings.
444 _list_setting(settings, 'default_encoding', 'UTF-8')
445 _list_setting(settings, 'default_encoding', 'UTF-8')
445 _bool_setting(settings, 'is_test', 'false')
446 _bool_setting(settings, 'is_test', 'false')
446 _bool_setting(settings, 'gzip_responses', 'false')
447 _bool_setting(settings, 'gzip_responses', 'false')
447
448
448 # Call split out functions that sanitize settings for each topic.
449 # Call split out functions that sanitize settings for each topic.
449 _sanitize_appenlight_settings(settings)
450 _sanitize_appenlight_settings(settings)
450 _sanitize_vcs_settings(settings)
451 _sanitize_vcs_settings(settings)
451
452
452 return settings
453 return settings
453
454
454
455
455 def _sanitize_appenlight_settings(settings):
456 def _sanitize_appenlight_settings(settings):
456 _bool_setting(settings, 'appenlight', 'false')
457 _bool_setting(settings, 'appenlight', 'false')
457
458
458
459
459 def _sanitize_vcs_settings(settings):
460 def _sanitize_vcs_settings(settings):
460 """
461 """
461 Applies settings defaults and does type conversion for all VCS related
462 Applies settings defaults and does type conversion for all VCS related
462 settings.
463 settings.
463 """
464 """
464 _string_setting(settings, 'vcs.svn.compatible_version', '')
465 _string_setting(settings, 'vcs.svn.compatible_version', '')
465 _string_setting(settings, 'git_rev_filter', '--all')
466 _string_setting(settings, 'git_rev_filter', '--all')
466 _string_setting(settings, 'vcs.hooks.protocol', 'http')
467 _string_setting(settings, 'vcs.hooks.protocol', 'http')
467 _string_setting(settings, 'vcs.scm_app_implementation', 'http')
468 _string_setting(settings, 'vcs.scm_app_implementation', 'http')
468 _string_setting(settings, 'vcs.server', '')
469 _string_setting(settings, 'vcs.server', '')
469 _string_setting(settings, 'vcs.server.log_level', 'debug')
470 _string_setting(settings, 'vcs.server.log_level', 'debug')
470 _string_setting(settings, 'vcs.server.protocol', 'http')
471 _string_setting(settings, 'vcs.server.protocol', 'http')
471 _bool_setting(settings, 'startup.import_repos', 'false')
472 _bool_setting(settings, 'startup.import_repos', 'false')
472 _bool_setting(settings, 'vcs.hooks.direct_calls', 'false')
473 _bool_setting(settings, 'vcs.hooks.direct_calls', 'false')
473 _bool_setting(settings, 'vcs.server.enable', 'true')
474 _bool_setting(settings, 'vcs.server.enable', 'true')
474 _bool_setting(settings, 'vcs.start_server', 'false')
475 _bool_setting(settings, 'vcs.start_server', 'false')
475 _list_setting(settings, 'vcs.backends', 'hg, git, svn')
476 _list_setting(settings, 'vcs.backends', 'hg, git, svn')
476 _int_setting(settings, 'vcs.connection_timeout', 3600)
477 _int_setting(settings, 'vcs.connection_timeout', 3600)
477
478
478 # Support legacy values of vcs.scm_app_implementation. Legacy
479 # Support legacy values of vcs.scm_app_implementation. Legacy
479 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http'
480 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http'
480 # which is now mapped to 'http'.
481 # which is now mapped to 'http'.
481 scm_app_impl = settings['vcs.scm_app_implementation']
482 scm_app_impl = settings['vcs.scm_app_implementation']
482 if scm_app_impl == 'rhodecode.lib.middleware.utils.scm_app_http':
483 if scm_app_impl == 'rhodecode.lib.middleware.utils.scm_app_http':
483 settings['vcs.scm_app_implementation'] = 'http'
484 settings['vcs.scm_app_implementation'] = 'http'
484
485
485
486
486 def _int_setting(settings, name, default):
487 def _int_setting(settings, name, default):
487 settings[name] = int(settings.get(name, default))
488 settings[name] = int(settings.get(name, default))
488
489
489
490
490 def _bool_setting(settings, name, default):
491 def _bool_setting(settings, name, default):
491 input = settings.get(name, default)
492 input = settings.get(name, default)
492 if isinstance(input, unicode):
493 if isinstance(input, unicode):
493 input = input.encode('utf8')
494 input = input.encode('utf8')
494 settings[name] = asbool(input)
495 settings[name] = asbool(input)
495
496
496
497
497 def _list_setting(settings, name, default):
498 def _list_setting(settings, name, default):
498 raw_value = settings.get(name, default)
499 raw_value = settings.get(name, default)
499
500
500 old_separator = ','
501 old_separator = ','
501 if old_separator in raw_value:
502 if old_separator in raw_value:
502 # If we get a comma separated list, pass it to our own function.
503 # If we get a comma separated list, pass it to our own function.
503 settings[name] = rhodecode_aslist(raw_value, sep=old_separator)
504 settings[name] = rhodecode_aslist(raw_value, sep=old_separator)
504 else:
505 else:
505 # Otherwise we assume it uses pyramids space/newline separation.
506 # Otherwise we assume it uses pyramids space/newline separation.
506 settings[name] = aslist(raw_value)
507 settings[name] = aslist(raw_value)
507
508
508
509
509 def _string_setting(settings, name, default, lower=True):
510 def _string_setting(settings, name, default, lower=True):
510 value = settings.get(name, default)
511 value = settings.get(name, default)
511 if lower:
512 if lower:
512 value = value.lower()
513 value = value.lower()
513 settings[name] = value
514 settings[name] = value
@@ -1,1125 +1,1116 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
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
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
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/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 Routes configuration
22 Routes configuration
23
23
24 The more specific and detailed routes should be defined first so they
24 The more specific and detailed routes should be defined first so they
25 may take precedent over the more generic routes. For more information
25 may take precedent over the more generic routes. For more information
26 refer to the routes manual at http://routes.groovie.org/docs/
26 refer to the routes manual at http://routes.groovie.org/docs/
27
27
28 IMPORTANT: if you change any routing here, make sure to take a look at lib/base.py
28 IMPORTANT: if you change any routing here, make sure to take a look at lib/base.py
29 and _route_name variable which uses some of stored naming here to do redirects.
29 and _route_name variable which uses some of stored naming here to do redirects.
30 """
30 """
31 import os
31 import os
32 import re
32 import re
33 from routes import Mapper
33 from routes import Mapper
34
34
35 # prefix for non repository related links needs to be prefixed with `/`
35 # prefix for non repository related links needs to be prefixed with `/`
36 ADMIN_PREFIX = '/_admin'
36 ADMIN_PREFIX = '/_admin'
37 STATIC_FILE_PREFIX = '/_static'
37 STATIC_FILE_PREFIX = '/_static'
38
38
39 # Default requirements for URL parts
39 # Default requirements for URL parts
40 URL_NAME_REQUIREMENTS = {
40 URL_NAME_REQUIREMENTS = {
41 # group name can have a slash in them, but they must not end with a slash
41 # group name can have a slash in them, but they must not end with a slash
42 'group_name': r'.*?[^/]',
42 'group_name': r'.*?[^/]',
43 'repo_group_name': r'.*?[^/]',
43 'repo_group_name': r'.*?[^/]',
44 # repo names can have a slash in them, but they must not end with a slash
44 # repo names can have a slash in them, but they must not end with a slash
45 'repo_name': r'.*?[^/]',
45 'repo_name': r'.*?[^/]',
46 # file path eats up everything at the end
46 # file path eats up everything at the end
47 'f_path': r'.*',
47 'f_path': r'.*',
48 # reference types
48 # reference types
49 'source_ref_type': '(branch|book|tag|rev|\%\(source_ref_type\)s)',
49 'source_ref_type': '(branch|book|tag|rev|\%\(source_ref_type\)s)',
50 'target_ref_type': '(branch|book|tag|rev|\%\(target_ref_type\)s)',
50 'target_ref_type': '(branch|book|tag|rev|\%\(target_ref_type\)s)',
51 }
51 }
52
52
53
53
54 def add_route_requirements(route_path, requirements):
54 def add_route_requirements(route_path, requirements):
55 """
55 """
56 Adds regex requirements to pyramid routes using a mapping dict
56 Adds regex requirements to pyramid routes using a mapping dict
57
57
58 >>> add_route_requirements('/{action}/{id}', {'id': r'\d+'})
58 >>> add_route_requirements('/{action}/{id}', {'id': r'\d+'})
59 '/{action}/{id:\d+}'
59 '/{action}/{id:\d+}'
60
60
61 """
61 """
62 for key, regex in requirements.items():
62 for key, regex in requirements.items():
63 route_path = route_path.replace('{%s}' % key, '{%s:%s}' % (key, regex))
63 route_path = route_path.replace('{%s}' % key, '{%s:%s}' % (key, regex))
64 return route_path
64 return route_path
65
65
66
66
67 class JSRoutesMapper(Mapper):
67 class JSRoutesMapper(Mapper):
68 """
68 """
69 Wrapper for routes.Mapper to make pyroutes compatible url definitions
69 Wrapper for routes.Mapper to make pyroutes compatible url definitions
70 """
70 """
71 _named_route_regex = re.compile(r'^[a-z-_0-9A-Z]+$')
71 _named_route_regex = re.compile(r'^[a-z-_0-9A-Z]+$')
72 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
72 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
73 def __init__(self, *args, **kw):
73 def __init__(self, *args, **kw):
74 super(JSRoutesMapper, self).__init__(*args, **kw)
74 super(JSRoutesMapper, self).__init__(*args, **kw)
75 self._jsroutes = []
75 self._jsroutes = []
76
76
77 def connect(self, *args, **kw):
77 def connect(self, *args, **kw):
78 """
78 """
79 Wrapper for connect to take an extra argument jsroute=True
79 Wrapper for connect to take an extra argument jsroute=True
80
80
81 :param jsroute: boolean, if True will add the route to the pyroutes list
81 :param jsroute: boolean, if True will add the route to the pyroutes list
82 """
82 """
83 if kw.pop('jsroute', False):
83 if kw.pop('jsroute', False):
84 if not self._named_route_regex.match(args[0]):
84 if not self._named_route_regex.match(args[0]):
85 raise Exception('only named routes can be added to pyroutes')
85 raise Exception('only named routes can be added to pyroutes')
86 self._jsroutes.append(args[0])
86 self._jsroutes.append(args[0])
87
87
88 super(JSRoutesMapper, self).connect(*args, **kw)
88 super(JSRoutesMapper, self).connect(*args, **kw)
89
89
90 def _extract_route_information(self, route):
90 def _extract_route_information(self, route):
91 """
91 """
92 Convert a route into tuple(name, path, args), eg:
92 Convert a route into tuple(name, path, args), eg:
93 ('show_user', '/profile/%(username)s', ['username'])
93 ('show_user', '/profile/%(username)s', ['username'])
94 """
94 """
95 routepath = route.routepath
95 routepath = route.routepath
96 def replace(matchobj):
96 def replace(matchobj):
97 if matchobj.group(1):
97 if matchobj.group(1):
98 return "%%(%s)s" % matchobj.group(1).split(':')[0]
98 return "%%(%s)s" % matchobj.group(1).split(':')[0]
99 else:
99 else:
100 return "%%(%s)s" % matchobj.group(2)
100 return "%%(%s)s" % matchobj.group(2)
101
101
102 routepath = self._argument_prog.sub(replace, routepath)
102 routepath = self._argument_prog.sub(replace, routepath)
103 return (
103 return (
104 route.name,
104 route.name,
105 routepath,
105 routepath,
106 [(arg[0].split(':')[0] if arg[0] != '' else arg[1])
106 [(arg[0].split(':')[0] if arg[0] != '' else arg[1])
107 for arg in self._argument_prog.findall(route.routepath)]
107 for arg in self._argument_prog.findall(route.routepath)]
108 )
108 )
109
109
110 def jsroutes(self):
110 def jsroutes(self):
111 """
111 """
112 Return a list of pyroutes.js compatible routes
112 Return a list of pyroutes.js compatible routes
113 """
113 """
114 for route_name in self._jsroutes:
114 for route_name in self._jsroutes:
115 yield self._extract_route_information(self._routenames[route_name])
115 yield self._extract_route_information(self._routenames[route_name])
116
116
117
117
118 def make_map(config):
118 def make_map(config):
119 """Create, configure and return the routes Mapper"""
119 """Create, configure and return the routes Mapper"""
120 rmap = JSRoutesMapper(directory=config['pylons.paths']['controllers'],
120 rmap = JSRoutesMapper(directory=config['pylons.paths']['controllers'],
121 always_scan=config['debug'])
121 always_scan=config['debug'])
122 rmap.minimization = False
122 rmap.minimization = False
123 rmap.explicit = False
123 rmap.explicit = False
124
124
125 from rhodecode.lib.utils2 import str2bool
125 from rhodecode.lib.utils2 import str2bool
126 from rhodecode.model import repo, repo_group
126 from rhodecode.model import repo, repo_group
127
127
128 def check_repo(environ, match_dict):
128 def check_repo(environ, match_dict):
129 """
129 """
130 check for valid repository for proper 404 handling
130 check for valid repository for proper 404 handling
131
131
132 :param environ:
132 :param environ:
133 :param match_dict:
133 :param match_dict:
134 """
134 """
135 repo_name = match_dict.get('repo_name')
135 repo_name = match_dict.get('repo_name')
136
136
137 if match_dict.get('f_path'):
137 if match_dict.get('f_path'):
138 # fix for multiple initial slashes that causes errors
138 # fix for multiple initial slashes that causes errors
139 match_dict['f_path'] = match_dict['f_path'].lstrip('/')
139 match_dict['f_path'] = match_dict['f_path'].lstrip('/')
140 repo_model = repo.RepoModel()
140 repo_model = repo.RepoModel()
141 by_name_match = repo_model.get_by_repo_name(repo_name)
141 by_name_match = repo_model.get_by_repo_name(repo_name)
142 # if we match quickly from database, short circuit the operation,
142 # if we match quickly from database, short circuit the operation,
143 # and validate repo based on the type.
143 # and validate repo based on the type.
144 if by_name_match:
144 if by_name_match:
145 return True
145 return True
146
146
147 by_id_match = repo_model.get_repo_by_id(repo_name)
147 by_id_match = repo_model.get_repo_by_id(repo_name)
148 if by_id_match:
148 if by_id_match:
149 repo_name = by_id_match.repo_name
149 repo_name = by_id_match.repo_name
150 match_dict['repo_name'] = repo_name
150 match_dict['repo_name'] = repo_name
151 return True
151 return True
152
152
153 return False
153 return False
154
154
155 def check_group(environ, match_dict):
155 def check_group(environ, match_dict):
156 """
156 """
157 check for valid repository group path for proper 404 handling
157 check for valid repository group path for proper 404 handling
158
158
159 :param environ:
159 :param environ:
160 :param match_dict:
160 :param match_dict:
161 """
161 """
162 repo_group_name = match_dict.get('group_name')
162 repo_group_name = match_dict.get('group_name')
163 repo_group_model = repo_group.RepoGroupModel()
163 repo_group_model = repo_group.RepoGroupModel()
164 by_name_match = repo_group_model.get_by_group_name(repo_group_name)
164 by_name_match = repo_group_model.get_by_group_name(repo_group_name)
165 if by_name_match:
165 if by_name_match:
166 return True
166 return True
167
167
168 return False
168 return False
169
169
170 def check_user_group(environ, match_dict):
170 def check_user_group(environ, match_dict):
171 """
171 """
172 check for valid user group for proper 404 handling
172 check for valid user group for proper 404 handling
173
173
174 :param environ:
174 :param environ:
175 :param match_dict:
175 :param match_dict:
176 """
176 """
177 return True
177 return True
178
178
179 def check_int(environ, match_dict):
179 def check_int(environ, match_dict):
180 return match_dict.get('id').isdigit()
180 return match_dict.get('id').isdigit()
181
181
182
182
183 #==========================================================================
183 #==========================================================================
184 # CUSTOM ROUTES HERE
184 # CUSTOM ROUTES HERE
185 #==========================================================================
185 #==========================================================================
186
186
187 # MAIN PAGE
187 # MAIN PAGE
188 rmap.connect('home', '/', controller='home', action='index', jsroute=True)
188 rmap.connect('home', '/', controller='home', action='index', jsroute=True)
189
189
190 # ping and pylons error test
190 # ping and pylons error test
191 rmap.connect('ping', '%s/ping' % (ADMIN_PREFIX,), controller='home', action='ping')
191 rmap.connect('ping', '%s/ping' % (ADMIN_PREFIX,), controller='home', action='ping')
192 rmap.connect('error_test', '%s/error_test' % (ADMIN_PREFIX,), controller='home', action='error_test')
192 rmap.connect('error_test', '%s/error_test' % (ADMIN_PREFIX,), controller='home', action='error_test')
193
193
194 # ADMIN REPOSITORY ROUTES
194 # ADMIN REPOSITORY ROUTES
195 with rmap.submapper(path_prefix=ADMIN_PREFIX,
195 with rmap.submapper(path_prefix=ADMIN_PREFIX,
196 controller='admin/repos') as m:
196 controller='admin/repos') as m:
197 m.connect('repos', '/repos',
197 m.connect('repos', '/repos',
198 action='create', conditions={'method': ['POST']})
198 action='create', conditions={'method': ['POST']})
199 m.connect('repos', '/repos',
199 m.connect('repos', '/repos',
200 action='index', conditions={'method': ['GET']})
200 action='index', conditions={'method': ['GET']})
201 m.connect('new_repo', '/create_repository', jsroute=True,
201 m.connect('new_repo', '/create_repository', jsroute=True,
202 action='create_repository', conditions={'method': ['GET']})
202 action='create_repository', conditions={'method': ['GET']})
203 m.connect('/repos/{repo_name}',
203 m.connect('/repos/{repo_name}',
204 action='update', conditions={'method': ['PUT'],
204 action='update', conditions={'method': ['PUT'],
205 'function': check_repo},
205 'function': check_repo},
206 requirements=URL_NAME_REQUIREMENTS)
206 requirements=URL_NAME_REQUIREMENTS)
207 m.connect('delete_repo', '/repos/{repo_name}',
207 m.connect('delete_repo', '/repos/{repo_name}',
208 action='delete', conditions={'method': ['DELETE']},
208 action='delete', conditions={'method': ['DELETE']},
209 requirements=URL_NAME_REQUIREMENTS)
209 requirements=URL_NAME_REQUIREMENTS)
210 m.connect('repo', '/repos/{repo_name}',
210 m.connect('repo', '/repos/{repo_name}',
211 action='show', conditions={'method': ['GET'],
211 action='show', conditions={'method': ['GET'],
212 'function': check_repo},
212 'function': check_repo},
213 requirements=URL_NAME_REQUIREMENTS)
213 requirements=URL_NAME_REQUIREMENTS)
214
214
215 # ADMIN REPOSITORY GROUPS ROUTES
215 # ADMIN REPOSITORY GROUPS ROUTES
216 with rmap.submapper(path_prefix=ADMIN_PREFIX,
216 with rmap.submapper(path_prefix=ADMIN_PREFIX,
217 controller='admin/repo_groups') as m:
217 controller='admin/repo_groups') as m:
218 m.connect('repo_groups', '/repo_groups',
218 m.connect('repo_groups', '/repo_groups',
219 action='create', conditions={'method': ['POST']})
219 action='create', conditions={'method': ['POST']})
220 m.connect('repo_groups', '/repo_groups',
220 m.connect('repo_groups', '/repo_groups',
221 action='index', conditions={'method': ['GET']})
221 action='index', conditions={'method': ['GET']})
222 m.connect('new_repo_group', '/repo_groups/new',
222 m.connect('new_repo_group', '/repo_groups/new',
223 action='new', conditions={'method': ['GET']})
223 action='new', conditions={'method': ['GET']})
224 m.connect('update_repo_group', '/repo_groups/{group_name}',
224 m.connect('update_repo_group', '/repo_groups/{group_name}',
225 action='update', conditions={'method': ['PUT'],
225 action='update', conditions={'method': ['PUT'],
226 'function': check_group},
226 'function': check_group},
227 requirements=URL_NAME_REQUIREMENTS)
227 requirements=URL_NAME_REQUIREMENTS)
228
228
229 # EXTRAS REPO GROUP ROUTES
229 # EXTRAS REPO GROUP ROUTES
230 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
230 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
231 action='edit',
231 action='edit',
232 conditions={'method': ['GET'], 'function': check_group},
232 conditions={'method': ['GET'], 'function': check_group},
233 requirements=URL_NAME_REQUIREMENTS)
233 requirements=URL_NAME_REQUIREMENTS)
234 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
234 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
235 action='edit',
235 action='edit',
236 conditions={'method': ['PUT'], 'function': check_group},
236 conditions={'method': ['PUT'], 'function': check_group},
237 requirements=URL_NAME_REQUIREMENTS)
237 requirements=URL_NAME_REQUIREMENTS)
238
238
239 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
239 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
240 action='edit_repo_group_advanced',
240 action='edit_repo_group_advanced',
241 conditions={'method': ['GET'], 'function': check_group},
241 conditions={'method': ['GET'], 'function': check_group},
242 requirements=URL_NAME_REQUIREMENTS)
242 requirements=URL_NAME_REQUIREMENTS)
243 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
243 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
244 action='edit_repo_group_advanced',
244 action='edit_repo_group_advanced',
245 conditions={'method': ['PUT'], 'function': check_group},
245 conditions={'method': ['PUT'], 'function': check_group},
246 requirements=URL_NAME_REQUIREMENTS)
246 requirements=URL_NAME_REQUIREMENTS)
247
247
248 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
248 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
249 action='edit_repo_group_perms',
249 action='edit_repo_group_perms',
250 conditions={'method': ['GET'], 'function': check_group},
250 conditions={'method': ['GET'], 'function': check_group},
251 requirements=URL_NAME_REQUIREMENTS)
251 requirements=URL_NAME_REQUIREMENTS)
252 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
252 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
253 action='update_perms',
253 action='update_perms',
254 conditions={'method': ['PUT'], 'function': check_group},
254 conditions={'method': ['PUT'], 'function': check_group},
255 requirements=URL_NAME_REQUIREMENTS)
255 requirements=URL_NAME_REQUIREMENTS)
256
256
257 m.connect('delete_repo_group', '/repo_groups/{group_name}',
257 m.connect('delete_repo_group', '/repo_groups/{group_name}',
258 action='delete', conditions={'method': ['DELETE'],
258 action='delete', conditions={'method': ['DELETE'],
259 'function': check_group},
259 'function': check_group},
260 requirements=URL_NAME_REQUIREMENTS)
260 requirements=URL_NAME_REQUIREMENTS)
261
261
262 # ADMIN USER ROUTES
262 # ADMIN USER ROUTES
263 with rmap.submapper(path_prefix=ADMIN_PREFIX,
263 with rmap.submapper(path_prefix=ADMIN_PREFIX,
264 controller='admin/users') as m:
264 controller='admin/users') as m:
265 m.connect('users', '/users',
265 m.connect('users', '/users',
266 action='create', conditions={'method': ['POST']})
266 action='create', conditions={'method': ['POST']})
267 m.connect('new_user', '/users/new',
267 m.connect('new_user', '/users/new',
268 action='new', conditions={'method': ['GET']})
268 action='new', conditions={'method': ['GET']})
269 m.connect('update_user', '/users/{user_id}',
269 m.connect('update_user', '/users/{user_id}',
270 action='update', conditions={'method': ['PUT']})
270 action='update', conditions={'method': ['PUT']})
271 m.connect('delete_user', '/users/{user_id}',
271 m.connect('delete_user', '/users/{user_id}',
272 action='delete', conditions={'method': ['DELETE']})
272 action='delete', conditions={'method': ['DELETE']})
273 m.connect('edit_user', '/users/{user_id}/edit',
273 m.connect('edit_user', '/users/{user_id}/edit',
274 action='edit', conditions={'method': ['GET']}, jsroute=True)
274 action='edit', conditions={'method': ['GET']}, jsroute=True)
275 m.connect('user', '/users/{user_id}',
275 m.connect('user', '/users/{user_id}',
276 action='show', conditions={'method': ['GET']})
276 action='show', conditions={'method': ['GET']})
277 m.connect('force_password_reset_user', '/users/{user_id}/password_reset',
277 m.connect('force_password_reset_user', '/users/{user_id}/password_reset',
278 action='reset_password', conditions={'method': ['POST']})
278 action='reset_password', conditions={'method': ['POST']})
279 m.connect('create_personal_repo_group', '/users/{user_id}/create_repo_group',
279 m.connect('create_personal_repo_group', '/users/{user_id}/create_repo_group',
280 action='create_personal_repo_group', conditions={'method': ['POST']})
280 action='create_personal_repo_group', conditions={'method': ['POST']})
281
281
282 # EXTRAS USER ROUTES
282 # EXTRAS USER ROUTES
283 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
283 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
284 action='edit_advanced', conditions={'method': ['GET']})
284 action='edit_advanced', conditions={'method': ['GET']})
285 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
285 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
286 action='update_advanced', conditions={'method': ['PUT']})
286 action='update_advanced', conditions={'method': ['PUT']})
287
287
288 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
288 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
289 action='edit_global_perms', conditions={'method': ['GET']})
289 action='edit_global_perms', conditions={'method': ['GET']})
290 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
290 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
291 action='update_global_perms', conditions={'method': ['PUT']})
291 action='update_global_perms', conditions={'method': ['PUT']})
292
292
293 m.connect('edit_user_perms_summary', '/users/{user_id}/edit/permissions_summary',
293 m.connect('edit_user_perms_summary', '/users/{user_id}/edit/permissions_summary',
294 action='edit_perms_summary', conditions={'method': ['GET']})
294 action='edit_perms_summary', conditions={'method': ['GET']})
295
295
296 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
296 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
297 action='edit_emails', conditions={'method': ['GET']})
297 action='edit_emails', conditions={'method': ['GET']})
298 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
298 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
299 action='add_email', conditions={'method': ['PUT']})
299 action='add_email', conditions={'method': ['PUT']})
300 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
300 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
301 action='delete_email', conditions={'method': ['DELETE']})
301 action='delete_email', conditions={'method': ['DELETE']})
302
302
303 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
303 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
304 action='edit_ips', conditions={'method': ['GET']})
304 action='edit_ips', conditions={'method': ['GET']})
305 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
305 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
306 action='add_ip', conditions={'method': ['PUT']})
306 action='add_ip', conditions={'method': ['PUT']})
307 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
307 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
308 action='delete_ip', conditions={'method': ['DELETE']})
308 action='delete_ip', conditions={'method': ['DELETE']})
309
309
310 # ADMIN USER GROUPS REST ROUTES
310 # ADMIN USER GROUPS REST ROUTES
311 with rmap.submapper(path_prefix=ADMIN_PREFIX,
311 with rmap.submapper(path_prefix=ADMIN_PREFIX,
312 controller='admin/user_groups') as m:
312 controller='admin/user_groups') as m:
313 m.connect('users_groups', '/user_groups',
313 m.connect('users_groups', '/user_groups',
314 action='create', conditions={'method': ['POST']})
314 action='create', conditions={'method': ['POST']})
315 m.connect('users_groups', '/user_groups',
315 m.connect('users_groups', '/user_groups',
316 action='index', conditions={'method': ['GET']})
316 action='index', conditions={'method': ['GET']})
317 m.connect('new_users_group', '/user_groups/new',
317 m.connect('new_users_group', '/user_groups/new',
318 action='new', conditions={'method': ['GET']})
318 action='new', conditions={'method': ['GET']})
319 m.connect('update_users_group', '/user_groups/{user_group_id}',
319 m.connect('update_users_group', '/user_groups/{user_group_id}',
320 action='update', conditions={'method': ['PUT']})
320 action='update', conditions={'method': ['PUT']})
321 m.connect('delete_users_group', '/user_groups/{user_group_id}',
321 m.connect('delete_users_group', '/user_groups/{user_group_id}',
322 action='delete', conditions={'method': ['DELETE']})
322 action='delete', conditions={'method': ['DELETE']})
323 m.connect('edit_users_group', '/user_groups/{user_group_id}/edit',
323 m.connect('edit_users_group', '/user_groups/{user_group_id}/edit',
324 action='edit', conditions={'method': ['GET']},
324 action='edit', conditions={'method': ['GET']},
325 function=check_user_group)
325 function=check_user_group)
326
326
327 # EXTRAS USER GROUP ROUTES
327 # EXTRAS USER GROUP ROUTES
328 m.connect('edit_user_group_global_perms',
328 m.connect('edit_user_group_global_perms',
329 '/user_groups/{user_group_id}/edit/global_permissions',
329 '/user_groups/{user_group_id}/edit/global_permissions',
330 action='edit_global_perms', conditions={'method': ['GET']})
330 action='edit_global_perms', conditions={'method': ['GET']})
331 m.connect('edit_user_group_global_perms',
331 m.connect('edit_user_group_global_perms',
332 '/user_groups/{user_group_id}/edit/global_permissions',
332 '/user_groups/{user_group_id}/edit/global_permissions',
333 action='update_global_perms', conditions={'method': ['PUT']})
333 action='update_global_perms', conditions={'method': ['PUT']})
334 m.connect('edit_user_group_perms_summary',
334 m.connect('edit_user_group_perms_summary',
335 '/user_groups/{user_group_id}/edit/permissions_summary',
335 '/user_groups/{user_group_id}/edit/permissions_summary',
336 action='edit_perms_summary', conditions={'method': ['GET']})
336 action='edit_perms_summary', conditions={'method': ['GET']})
337
337
338 m.connect('edit_user_group_perms',
338 m.connect('edit_user_group_perms',
339 '/user_groups/{user_group_id}/edit/permissions',
339 '/user_groups/{user_group_id}/edit/permissions',
340 action='edit_perms', conditions={'method': ['GET']})
340 action='edit_perms', conditions={'method': ['GET']})
341 m.connect('edit_user_group_perms',
341 m.connect('edit_user_group_perms',
342 '/user_groups/{user_group_id}/edit/permissions',
342 '/user_groups/{user_group_id}/edit/permissions',
343 action='update_perms', conditions={'method': ['PUT']})
343 action='update_perms', conditions={'method': ['PUT']})
344
344
345 m.connect('edit_user_group_advanced',
345 m.connect('edit_user_group_advanced',
346 '/user_groups/{user_group_id}/edit/advanced',
346 '/user_groups/{user_group_id}/edit/advanced',
347 action='edit_advanced', conditions={'method': ['GET']})
347 action='edit_advanced', conditions={'method': ['GET']})
348
348
349 m.connect('edit_user_group_advanced_sync',
349 m.connect('edit_user_group_advanced_sync',
350 '/user_groups/{user_group_id}/edit/advanced/sync',
350 '/user_groups/{user_group_id}/edit/advanced/sync',
351 action='edit_advanced_set_synchronization', conditions={'method': ['POST']})
351 action='edit_advanced_set_synchronization', conditions={'method': ['POST']})
352
352
353 m.connect('edit_user_group_members',
353 m.connect('edit_user_group_members',
354 '/user_groups/{user_group_id}/edit/members', jsroute=True,
354 '/user_groups/{user_group_id}/edit/members', jsroute=True,
355 action='user_group_members', conditions={'method': ['GET']})
355 action='user_group_members', conditions={'method': ['GET']})
356
356
357 # ADMIN PERMISSIONS ROUTES
357 # ADMIN PERMISSIONS ROUTES
358 with rmap.submapper(path_prefix=ADMIN_PREFIX,
358 with rmap.submapper(path_prefix=ADMIN_PREFIX,
359 controller='admin/permissions') as m:
359 controller='admin/permissions') as m:
360 m.connect('admin_permissions_application', '/permissions/application',
360 m.connect('admin_permissions_application', '/permissions/application',
361 action='permission_application_update', conditions={'method': ['POST']})
361 action='permission_application_update', conditions={'method': ['POST']})
362 m.connect('admin_permissions_application', '/permissions/application',
362 m.connect('admin_permissions_application', '/permissions/application',
363 action='permission_application', conditions={'method': ['GET']})
363 action='permission_application', conditions={'method': ['GET']})
364
364
365 m.connect('admin_permissions_global', '/permissions/global',
365 m.connect('admin_permissions_global', '/permissions/global',
366 action='permission_global_update', conditions={'method': ['POST']})
366 action='permission_global_update', conditions={'method': ['POST']})
367 m.connect('admin_permissions_global', '/permissions/global',
367 m.connect('admin_permissions_global', '/permissions/global',
368 action='permission_global', conditions={'method': ['GET']})
368 action='permission_global', conditions={'method': ['GET']})
369
369
370 m.connect('admin_permissions_object', '/permissions/object',
370 m.connect('admin_permissions_object', '/permissions/object',
371 action='permission_objects_update', conditions={'method': ['POST']})
371 action='permission_objects_update', conditions={'method': ['POST']})
372 m.connect('admin_permissions_object', '/permissions/object',
372 m.connect('admin_permissions_object', '/permissions/object',
373 action='permission_objects', conditions={'method': ['GET']})
373 action='permission_objects', conditions={'method': ['GET']})
374
374
375 m.connect('admin_permissions_ips', '/permissions/ips',
375 m.connect('admin_permissions_ips', '/permissions/ips',
376 action='permission_ips', conditions={'method': ['POST']})
376 action='permission_ips', conditions={'method': ['POST']})
377 m.connect('admin_permissions_ips', '/permissions/ips',
377 m.connect('admin_permissions_ips', '/permissions/ips',
378 action='permission_ips', conditions={'method': ['GET']})
378 action='permission_ips', conditions={'method': ['GET']})
379
379
380 m.connect('admin_permissions_overview', '/permissions/overview',
380 m.connect('admin_permissions_overview', '/permissions/overview',
381 action='permission_perms', conditions={'method': ['GET']})
381 action='permission_perms', conditions={'method': ['GET']})
382
382
383 # ADMIN DEFAULTS REST ROUTES
383 # ADMIN DEFAULTS REST ROUTES
384 with rmap.submapper(path_prefix=ADMIN_PREFIX,
384 with rmap.submapper(path_prefix=ADMIN_PREFIX,
385 controller='admin/defaults') as m:
385 controller='admin/defaults') as m:
386 m.connect('admin_defaults_repositories', '/defaults/repositories',
386 m.connect('admin_defaults_repositories', '/defaults/repositories',
387 action='update_repository_defaults', conditions={'method': ['POST']})
387 action='update_repository_defaults', conditions={'method': ['POST']})
388 m.connect('admin_defaults_repositories', '/defaults/repositories',
388 m.connect('admin_defaults_repositories', '/defaults/repositories',
389 action='index', conditions={'method': ['GET']})
389 action='index', conditions={'method': ['GET']})
390
390
391 # ADMIN DEBUG STYLE ROUTES
391 # ADMIN DEBUG STYLE ROUTES
392 if str2bool(config.get('debug_style')):
392 if str2bool(config.get('debug_style')):
393 with rmap.submapper(path_prefix=ADMIN_PREFIX + '/debug_style',
393 with rmap.submapper(path_prefix=ADMIN_PREFIX + '/debug_style',
394 controller='debug_style') as m:
394 controller='debug_style') as m:
395 m.connect('debug_style_home', '',
395 m.connect('debug_style_home', '',
396 action='index', conditions={'method': ['GET']})
396 action='index', conditions={'method': ['GET']})
397 m.connect('debug_style_template', '/t/{t_path}',
397 m.connect('debug_style_template', '/t/{t_path}',
398 action='template', conditions={'method': ['GET']})
398 action='template', conditions={'method': ['GET']})
399
399
400 # ADMIN SETTINGS ROUTES
400 # ADMIN SETTINGS ROUTES
401 with rmap.submapper(path_prefix=ADMIN_PREFIX,
401 with rmap.submapper(path_prefix=ADMIN_PREFIX,
402 controller='admin/settings') as m:
402 controller='admin/settings') as m:
403
403
404 # default
404 # default
405 m.connect('admin_settings', '/settings',
405 m.connect('admin_settings', '/settings',
406 action='settings_global_update',
406 action='settings_global_update',
407 conditions={'method': ['POST']})
407 conditions={'method': ['POST']})
408 m.connect('admin_settings', '/settings',
408 m.connect('admin_settings', '/settings',
409 action='settings_global', conditions={'method': ['GET']})
409 action='settings_global', conditions={'method': ['GET']})
410
410
411 m.connect('admin_settings_vcs', '/settings/vcs',
411 m.connect('admin_settings_vcs', '/settings/vcs',
412 action='settings_vcs_update',
412 action='settings_vcs_update',
413 conditions={'method': ['POST']})
413 conditions={'method': ['POST']})
414 m.connect('admin_settings_vcs', '/settings/vcs',
414 m.connect('admin_settings_vcs', '/settings/vcs',
415 action='settings_vcs',
415 action='settings_vcs',
416 conditions={'method': ['GET']})
416 conditions={'method': ['GET']})
417 m.connect('admin_settings_vcs', '/settings/vcs',
417 m.connect('admin_settings_vcs', '/settings/vcs',
418 action='delete_svn_pattern',
418 action='delete_svn_pattern',
419 conditions={'method': ['DELETE']})
419 conditions={'method': ['DELETE']})
420
420
421 m.connect('admin_settings_mapping', '/settings/mapping',
421 m.connect('admin_settings_mapping', '/settings/mapping',
422 action='settings_mapping_update',
422 action='settings_mapping_update',
423 conditions={'method': ['POST']})
423 conditions={'method': ['POST']})
424 m.connect('admin_settings_mapping', '/settings/mapping',
424 m.connect('admin_settings_mapping', '/settings/mapping',
425 action='settings_mapping', conditions={'method': ['GET']})
425 action='settings_mapping', conditions={'method': ['GET']})
426
426
427 m.connect('admin_settings_global', '/settings/global',
427 m.connect('admin_settings_global', '/settings/global',
428 action='settings_global_update',
428 action='settings_global_update',
429 conditions={'method': ['POST']})
429 conditions={'method': ['POST']})
430 m.connect('admin_settings_global', '/settings/global',
430 m.connect('admin_settings_global', '/settings/global',
431 action='settings_global', conditions={'method': ['GET']})
431 action='settings_global', conditions={'method': ['GET']})
432
432
433 m.connect('admin_settings_visual', '/settings/visual',
433 m.connect('admin_settings_visual', '/settings/visual',
434 action='settings_visual_update',
434 action='settings_visual_update',
435 conditions={'method': ['POST']})
435 conditions={'method': ['POST']})
436 m.connect('admin_settings_visual', '/settings/visual',
436 m.connect('admin_settings_visual', '/settings/visual',
437 action='settings_visual', conditions={'method': ['GET']})
437 action='settings_visual', conditions={'method': ['GET']})
438
438
439 m.connect('admin_settings_issuetracker',
439 m.connect('admin_settings_issuetracker',
440 '/settings/issue-tracker', action='settings_issuetracker',
440 '/settings/issue-tracker', action='settings_issuetracker',
441 conditions={'method': ['GET']})
441 conditions={'method': ['GET']})
442 m.connect('admin_settings_issuetracker_save',
442 m.connect('admin_settings_issuetracker_save',
443 '/settings/issue-tracker/save',
443 '/settings/issue-tracker/save',
444 action='settings_issuetracker_save',
444 action='settings_issuetracker_save',
445 conditions={'method': ['POST']})
445 conditions={'method': ['POST']})
446 m.connect('admin_issuetracker_test', '/settings/issue-tracker/test',
446 m.connect('admin_issuetracker_test', '/settings/issue-tracker/test',
447 action='settings_issuetracker_test',
447 action='settings_issuetracker_test',
448 conditions={'method': ['POST']})
448 conditions={'method': ['POST']})
449 m.connect('admin_issuetracker_delete',
449 m.connect('admin_issuetracker_delete',
450 '/settings/issue-tracker/delete',
450 '/settings/issue-tracker/delete',
451 action='settings_issuetracker_delete',
451 action='settings_issuetracker_delete',
452 conditions={'method': ['DELETE']})
452 conditions={'method': ['DELETE']})
453
453
454 m.connect('admin_settings_email', '/settings/email',
454 m.connect('admin_settings_email', '/settings/email',
455 action='settings_email_update',
455 action='settings_email_update',
456 conditions={'method': ['POST']})
456 conditions={'method': ['POST']})
457 m.connect('admin_settings_email', '/settings/email',
457 m.connect('admin_settings_email', '/settings/email',
458 action='settings_email', conditions={'method': ['GET']})
458 action='settings_email', conditions={'method': ['GET']})
459
459
460 m.connect('admin_settings_hooks', '/settings/hooks',
460 m.connect('admin_settings_hooks', '/settings/hooks',
461 action='settings_hooks_update',
461 action='settings_hooks_update',
462 conditions={'method': ['POST', 'DELETE']})
462 conditions={'method': ['POST', 'DELETE']})
463 m.connect('admin_settings_hooks', '/settings/hooks',
463 m.connect('admin_settings_hooks', '/settings/hooks',
464 action='settings_hooks', conditions={'method': ['GET']})
464 action='settings_hooks', conditions={'method': ['GET']})
465
465
466 m.connect('admin_settings_search', '/settings/search',
466 m.connect('admin_settings_search', '/settings/search',
467 action='settings_search', conditions={'method': ['GET']})
467 action='settings_search', conditions={'method': ['GET']})
468
468
469 m.connect('admin_settings_supervisor', '/settings/supervisor',
469 m.connect('admin_settings_supervisor', '/settings/supervisor',
470 action='settings_supervisor', conditions={'method': ['GET']})
470 action='settings_supervisor', conditions={'method': ['GET']})
471 m.connect('admin_settings_supervisor_log', '/settings/supervisor/{procid}/log',
471 m.connect('admin_settings_supervisor_log', '/settings/supervisor/{procid}/log',
472 action='settings_supervisor_log', conditions={'method': ['GET']})
472 action='settings_supervisor_log', conditions={'method': ['GET']})
473
473
474 m.connect('admin_settings_labs', '/settings/labs',
474 m.connect('admin_settings_labs', '/settings/labs',
475 action='settings_labs_update',
475 action='settings_labs_update',
476 conditions={'method': ['POST']})
476 conditions={'method': ['POST']})
477 m.connect('admin_settings_labs', '/settings/labs',
477 m.connect('admin_settings_labs', '/settings/labs',
478 action='settings_labs', conditions={'method': ['GET']})
478 action='settings_labs', conditions={'method': ['GET']})
479
479
480 # ADMIN MY ACCOUNT
480 # ADMIN MY ACCOUNT
481 with rmap.submapper(path_prefix=ADMIN_PREFIX,
481 with rmap.submapper(path_prefix=ADMIN_PREFIX,
482 controller='admin/my_account') as m:
482 controller='admin/my_account') as m:
483
483
484 m.connect('my_account_edit', '/my_account/edit',
484 m.connect('my_account_edit', '/my_account/edit',
485 action='my_account_edit', conditions={'method': ['GET']})
485 action='my_account_edit', conditions={'method': ['GET']})
486 m.connect('my_account', '/my_account/update',
486 m.connect('my_account', '/my_account/update',
487 action='my_account_update', conditions={'method': ['POST']})
487 action='my_account_update', conditions={'method': ['POST']})
488
488
489 # NOTE(marcink): this needs to be kept for password force flag to be
489 # NOTE(marcink): this needs to be kept for password force flag to be
490 # handler, remove after migration to pyramid
490 # handler, remove after migration to pyramid
491 m.connect('my_account_password', '/my_account/password',
491 m.connect('my_account_password', '/my_account/password',
492 action='my_account_password', conditions={'method': ['GET']})
492 action='my_account_password', conditions={'method': ['GET']})
493
493
494 m.connect('my_account_repos', '/my_account/repos',
494 m.connect('my_account_repos', '/my_account/repos',
495 action='my_account_repos', conditions={'method': ['GET']})
495 action='my_account_repos', conditions={'method': ['GET']})
496
496
497 m.connect('my_account_watched', '/my_account/watched',
497 m.connect('my_account_watched', '/my_account/watched',
498 action='my_account_watched', conditions={'method': ['GET']})
498 action='my_account_watched', conditions={'method': ['GET']})
499
499
500 m.connect('my_account_pullrequests', '/my_account/pull_requests',
500 m.connect('my_account_pullrequests', '/my_account/pull_requests',
501 action='my_account_pullrequests', conditions={'method': ['GET']})
501 action='my_account_pullrequests', conditions={'method': ['GET']})
502
502
503 m.connect('my_account_perms', '/my_account/perms',
503 m.connect('my_account_perms', '/my_account/perms',
504 action='my_account_perms', conditions={'method': ['GET']})
504 action='my_account_perms', conditions={'method': ['GET']})
505
505
506 m.connect('my_account_emails', '/my_account/emails',
506 m.connect('my_account_emails', '/my_account/emails',
507 action='my_account_emails', conditions={'method': ['GET']})
507 action='my_account_emails', conditions={'method': ['GET']})
508 m.connect('my_account_emails', '/my_account/emails',
508 m.connect('my_account_emails', '/my_account/emails',
509 action='my_account_emails_add', conditions={'method': ['POST']})
509 action='my_account_emails_add', conditions={'method': ['POST']})
510 m.connect('my_account_emails', '/my_account/emails',
510 m.connect('my_account_emails', '/my_account/emails',
511 action='my_account_emails_delete', conditions={'method': ['DELETE']})
511 action='my_account_emails_delete', conditions={'method': ['DELETE']})
512
512
513 m.connect('my_account_notifications', '/my_account/notifications',
513 m.connect('my_account_notifications', '/my_account/notifications',
514 action='my_notifications',
514 action='my_notifications',
515 conditions={'method': ['GET']})
515 conditions={'method': ['GET']})
516 m.connect('my_account_notifications_toggle_visibility',
516 m.connect('my_account_notifications_toggle_visibility',
517 '/my_account/toggle_visibility',
517 '/my_account/toggle_visibility',
518 action='my_notifications_toggle_visibility',
518 action='my_notifications_toggle_visibility',
519 conditions={'method': ['POST']})
519 conditions={'method': ['POST']})
520 m.connect('my_account_notifications_test_channelstream',
520 m.connect('my_account_notifications_test_channelstream',
521 '/my_account/test_channelstream',
521 '/my_account/test_channelstream',
522 action='my_account_notifications_test_channelstream',
522 action='my_account_notifications_test_channelstream',
523 conditions={'method': ['POST']})
523 conditions={'method': ['POST']})
524
524
525 # NOTIFICATION REST ROUTES
525 # NOTIFICATION REST ROUTES
526 with rmap.submapper(path_prefix=ADMIN_PREFIX,
526 with rmap.submapper(path_prefix=ADMIN_PREFIX,
527 controller='admin/notifications') as m:
527 controller='admin/notifications') as m:
528 m.connect('notifications', '/notifications',
528 m.connect('notifications', '/notifications',
529 action='index', conditions={'method': ['GET']})
529 action='index', conditions={'method': ['GET']})
530 m.connect('notifications_mark_all_read', '/notifications/mark_all_read',
530 m.connect('notifications_mark_all_read', '/notifications/mark_all_read',
531 action='mark_all_read', conditions={'method': ['POST']})
531 action='mark_all_read', conditions={'method': ['POST']})
532 m.connect('/notifications/{notification_id}',
532 m.connect('/notifications/{notification_id}',
533 action='update', conditions={'method': ['PUT']})
533 action='update', conditions={'method': ['PUT']})
534 m.connect('/notifications/{notification_id}',
534 m.connect('/notifications/{notification_id}',
535 action='delete', conditions={'method': ['DELETE']})
535 action='delete', conditions={'method': ['DELETE']})
536 m.connect('notification', '/notifications/{notification_id}',
536 m.connect('notification', '/notifications/{notification_id}',
537 action='show', conditions={'method': ['GET']})
537 action='show', conditions={'method': ['GET']})
538
538
539 # ADMIN GIST
539 # ADMIN GIST
540 with rmap.submapper(path_prefix=ADMIN_PREFIX,
540 with rmap.submapper(path_prefix=ADMIN_PREFIX,
541 controller='admin/gists') as m:
541 controller='admin/gists') as m:
542 m.connect('gists', '/gists',
542 m.connect('gists', '/gists',
543 action='create', conditions={'method': ['POST']})
543 action='create', conditions={'method': ['POST']})
544 m.connect('gists', '/gists', jsroute=True,
544 m.connect('gists', '/gists', jsroute=True,
545 action='index', conditions={'method': ['GET']})
545 action='index', conditions={'method': ['GET']})
546 m.connect('new_gist', '/gists/new', jsroute=True,
546 m.connect('new_gist', '/gists/new', jsroute=True,
547 action='new', conditions={'method': ['GET']})
547 action='new', conditions={'method': ['GET']})
548
548
549 m.connect('/gists/{gist_id}',
549 m.connect('/gists/{gist_id}',
550 action='delete', conditions={'method': ['DELETE']})
550 action='delete', conditions={'method': ['DELETE']})
551 m.connect('edit_gist', '/gists/{gist_id}/edit',
551 m.connect('edit_gist', '/gists/{gist_id}/edit',
552 action='edit_form', conditions={'method': ['GET']})
552 action='edit_form', conditions={'method': ['GET']})
553 m.connect('edit_gist', '/gists/{gist_id}/edit',
553 m.connect('edit_gist', '/gists/{gist_id}/edit',
554 action='edit', conditions={'method': ['POST']})
554 action='edit', conditions={'method': ['POST']})
555 m.connect(
555 m.connect(
556 'edit_gist_check_revision', '/gists/{gist_id}/edit/check_revision',
556 'edit_gist_check_revision', '/gists/{gist_id}/edit/check_revision',
557 action='check_revision', conditions={'method': ['GET']})
557 action='check_revision', conditions={'method': ['GET']})
558
558
559 m.connect('gist', '/gists/{gist_id}',
559 m.connect('gist', '/gists/{gist_id}',
560 action='show', conditions={'method': ['GET']})
560 action='show', conditions={'method': ['GET']})
561 m.connect('gist_rev', '/gists/{gist_id}/{revision}',
561 m.connect('gist_rev', '/gists/{gist_id}/{revision}',
562 revision='tip',
562 revision='tip',
563 action='show', conditions={'method': ['GET']})
563 action='show', conditions={'method': ['GET']})
564 m.connect('formatted_gist', '/gists/{gist_id}/{revision}/{format}',
564 m.connect('formatted_gist', '/gists/{gist_id}/{revision}/{format}',
565 revision='tip',
565 revision='tip',
566 action='show', conditions={'method': ['GET']})
566 action='show', conditions={'method': ['GET']})
567 m.connect('formatted_gist_file', '/gists/{gist_id}/{revision}/{format}/{f_path}',
567 m.connect('formatted_gist_file', '/gists/{gist_id}/{revision}/{format}/{f_path}',
568 revision='tip',
568 revision='tip',
569 action='show', conditions={'method': ['GET']},
569 action='show', conditions={'method': ['GET']},
570 requirements=URL_NAME_REQUIREMENTS)
570 requirements=URL_NAME_REQUIREMENTS)
571
571
572 # ADMIN MAIN PAGES
572 # ADMIN MAIN PAGES
573 with rmap.submapper(path_prefix=ADMIN_PREFIX,
573 with rmap.submapper(path_prefix=ADMIN_PREFIX,
574 controller='admin/admin') as m:
574 controller='admin/admin') as m:
575 m.connect('admin_home', '', action='index')
575 m.connect('admin_home', '', action='index')
576 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
576 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
577 action='add_repo')
577 action='add_repo')
578 m.connect(
578 m.connect(
579 'pull_requests_global_0', '/pull_requests/{pull_request_id:[0-9]+}',
579 'pull_requests_global_0', '/pull_requests/{pull_request_id:[0-9]+}',
580 action='pull_requests')
580 action='pull_requests')
581 m.connect(
581 m.connect(
582 'pull_requests_global_1', '/pull-requests/{pull_request_id:[0-9]+}',
582 'pull_requests_global_1', '/pull-requests/{pull_request_id:[0-9]+}',
583 action='pull_requests')
583 action='pull_requests')
584 m.connect(
584 m.connect(
585 'pull_requests_global', '/pull-request/{pull_request_id:[0-9]+}',
585 'pull_requests_global', '/pull-request/{pull_request_id:[0-9]+}',
586 action='pull_requests')
586 action='pull_requests')
587
587
588 # USER JOURNAL
588 # USER JOURNAL
589 rmap.connect('journal', '%s/journal' % (ADMIN_PREFIX,),
589 rmap.connect('journal', '%s/journal' % (ADMIN_PREFIX,),
590 controller='journal', action='index')
590 controller='journal', action='index')
591 rmap.connect('journal_rss', '%s/journal/rss' % (ADMIN_PREFIX,),
591 rmap.connect('journal_rss', '%s/journal/rss' % (ADMIN_PREFIX,),
592 controller='journal', action='journal_rss')
592 controller='journal', action='journal_rss')
593 rmap.connect('journal_atom', '%s/journal/atom' % (ADMIN_PREFIX,),
593 rmap.connect('journal_atom', '%s/journal/atom' % (ADMIN_PREFIX,),
594 controller='journal', action='journal_atom')
594 controller='journal', action='journal_atom')
595
595
596 rmap.connect('public_journal', '%s/public_journal' % (ADMIN_PREFIX,),
596 rmap.connect('public_journal', '%s/public_journal' % (ADMIN_PREFIX,),
597 controller='journal', action='public_journal')
597 controller='journal', action='public_journal')
598
598
599 rmap.connect('public_journal_rss', '%s/public_journal/rss' % (ADMIN_PREFIX,),
599 rmap.connect('public_journal_rss', '%s/public_journal/rss' % (ADMIN_PREFIX,),
600 controller='journal', action='public_journal_rss')
600 controller='journal', action='public_journal_rss')
601
601
602 rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % (ADMIN_PREFIX,),
602 rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % (ADMIN_PREFIX,),
603 controller='journal', action='public_journal_rss')
603 controller='journal', action='public_journal_rss')
604
604
605 rmap.connect('public_journal_atom',
605 rmap.connect('public_journal_atom',
606 '%s/public_journal/atom' % (ADMIN_PREFIX,), controller='journal',
606 '%s/public_journal/atom' % (ADMIN_PREFIX,), controller='journal',
607 action='public_journal_atom')
607 action='public_journal_atom')
608
608
609 rmap.connect('public_journal_atom_old',
609 rmap.connect('public_journal_atom_old',
610 '%s/public_journal_atom' % (ADMIN_PREFIX,), controller='journal',
610 '%s/public_journal_atom' % (ADMIN_PREFIX,), controller='journal',
611 action='public_journal_atom')
611 action='public_journal_atom')
612
612
613 rmap.connect('toggle_following', '%s/toggle_following' % (ADMIN_PREFIX,),
613 rmap.connect('toggle_following', '%s/toggle_following' % (ADMIN_PREFIX,),
614 controller='journal', action='toggle_following', jsroute=True,
614 controller='journal', action='toggle_following', jsroute=True,
615 conditions={'method': ['POST']})
615 conditions={'method': ['POST']})
616
616
617 # FULL TEXT SEARCH
618 rmap.connect('search', '%s/search' % (ADMIN_PREFIX,),
619 controller='search')
620 rmap.connect('search_repo_home', '/{repo_name}/search',
621 controller='search',
622 action='index',
623 conditions={'function': check_repo},
624 requirements=URL_NAME_REQUIREMENTS)
625
626 # FEEDS
617 # FEEDS
627 rmap.connect('rss_feed_home', '/{repo_name}/feed/rss',
618 rmap.connect('rss_feed_home', '/{repo_name}/feed/rss',
628 controller='feed', action='rss',
619 controller='feed', action='rss',
629 conditions={'function': check_repo},
620 conditions={'function': check_repo},
630 requirements=URL_NAME_REQUIREMENTS)
621 requirements=URL_NAME_REQUIREMENTS)
631
622
632 rmap.connect('atom_feed_home', '/{repo_name}/feed/atom',
623 rmap.connect('atom_feed_home', '/{repo_name}/feed/atom',
633 controller='feed', action='atom',
624 controller='feed', action='atom',
634 conditions={'function': check_repo},
625 conditions={'function': check_repo},
635 requirements=URL_NAME_REQUIREMENTS)
626 requirements=URL_NAME_REQUIREMENTS)
636
627
637 #==========================================================================
628 #==========================================================================
638 # REPOSITORY ROUTES
629 # REPOSITORY ROUTES
639 #==========================================================================
630 #==========================================================================
640
631
641 rmap.connect('repo_creating_home', '/{repo_name}/repo_creating',
632 rmap.connect('repo_creating_home', '/{repo_name}/repo_creating',
642 controller='admin/repos', action='repo_creating',
633 controller='admin/repos', action='repo_creating',
643 requirements=URL_NAME_REQUIREMENTS)
634 requirements=URL_NAME_REQUIREMENTS)
644 rmap.connect('repo_check_home', '/{repo_name}/crepo_check',
635 rmap.connect('repo_check_home', '/{repo_name}/crepo_check',
645 controller='admin/repos', action='repo_check',
636 controller='admin/repos', action='repo_check',
646 requirements=URL_NAME_REQUIREMENTS)
637 requirements=URL_NAME_REQUIREMENTS)
647
638
648 rmap.connect('repo_stats', '/{repo_name}/repo_stats/{commit_id}',
639 rmap.connect('repo_stats', '/{repo_name}/repo_stats/{commit_id}',
649 controller='summary', action='repo_stats',
640 controller='summary', action='repo_stats',
650 conditions={'function': check_repo},
641 conditions={'function': check_repo},
651 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
642 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
652
643
653 rmap.connect('repo_refs_data', '/{repo_name}/refs-data',
644 rmap.connect('repo_refs_data', '/{repo_name}/refs-data',
654 controller='summary', action='repo_refs_data',
645 controller='summary', action='repo_refs_data',
655 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
646 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
656 rmap.connect('repo_refs_changelog_data', '/{repo_name}/refs-data-changelog',
647 rmap.connect('repo_refs_changelog_data', '/{repo_name}/refs-data-changelog',
657 controller='summary', action='repo_refs_changelog_data',
648 controller='summary', action='repo_refs_changelog_data',
658 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
649 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
659 rmap.connect('repo_default_reviewers_data', '/{repo_name}/default-reviewers',
650 rmap.connect('repo_default_reviewers_data', '/{repo_name}/default-reviewers',
660 controller='summary', action='repo_default_reviewers_data',
651 controller='summary', action='repo_default_reviewers_data',
661 jsroute=True, requirements=URL_NAME_REQUIREMENTS)
652 jsroute=True, requirements=URL_NAME_REQUIREMENTS)
662
653
663 rmap.connect('changeset_home', '/{repo_name}/changeset/{revision}',
654 rmap.connect('changeset_home', '/{repo_name}/changeset/{revision}',
664 controller='changeset', revision='tip',
655 controller='changeset', revision='tip',
665 conditions={'function': check_repo},
656 conditions={'function': check_repo},
666 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
657 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
667 rmap.connect('changeset_children', '/{repo_name}/changeset_children/{revision}',
658 rmap.connect('changeset_children', '/{repo_name}/changeset_children/{revision}',
668 controller='changeset', revision='tip', action='changeset_children',
659 controller='changeset', revision='tip', action='changeset_children',
669 conditions={'function': check_repo},
660 conditions={'function': check_repo},
670 requirements=URL_NAME_REQUIREMENTS)
661 requirements=URL_NAME_REQUIREMENTS)
671 rmap.connect('changeset_parents', '/{repo_name}/changeset_parents/{revision}',
662 rmap.connect('changeset_parents', '/{repo_name}/changeset_parents/{revision}',
672 controller='changeset', revision='tip', action='changeset_parents',
663 controller='changeset', revision='tip', action='changeset_parents',
673 conditions={'function': check_repo},
664 conditions={'function': check_repo},
674 requirements=URL_NAME_REQUIREMENTS)
665 requirements=URL_NAME_REQUIREMENTS)
675
666
676 # repo edit options
667 # repo edit options
677 rmap.connect('edit_repo', '/{repo_name}/settings', jsroute=True,
668 rmap.connect('edit_repo', '/{repo_name}/settings', jsroute=True,
678 controller='admin/repos', action='edit',
669 controller='admin/repos', action='edit',
679 conditions={'method': ['GET'], 'function': check_repo},
670 conditions={'method': ['GET'], 'function': check_repo},
680 requirements=URL_NAME_REQUIREMENTS)
671 requirements=URL_NAME_REQUIREMENTS)
681
672
682 rmap.connect('edit_repo_perms', '/{repo_name}/settings/permissions',
673 rmap.connect('edit_repo_perms', '/{repo_name}/settings/permissions',
683 jsroute=True,
674 jsroute=True,
684 controller='admin/repos', action='edit_permissions',
675 controller='admin/repos', action='edit_permissions',
685 conditions={'method': ['GET'], 'function': check_repo},
676 conditions={'method': ['GET'], 'function': check_repo},
686 requirements=URL_NAME_REQUIREMENTS)
677 requirements=URL_NAME_REQUIREMENTS)
687 rmap.connect('edit_repo_perms_update', '/{repo_name}/settings/permissions',
678 rmap.connect('edit_repo_perms_update', '/{repo_name}/settings/permissions',
688 controller='admin/repos', action='edit_permissions_update',
679 controller='admin/repos', action='edit_permissions_update',
689 conditions={'method': ['PUT'], 'function': check_repo},
680 conditions={'method': ['PUT'], 'function': check_repo},
690 requirements=URL_NAME_REQUIREMENTS)
681 requirements=URL_NAME_REQUIREMENTS)
691
682
692 rmap.connect('edit_repo_fields', '/{repo_name}/settings/fields',
683 rmap.connect('edit_repo_fields', '/{repo_name}/settings/fields',
693 controller='admin/repos', action='edit_fields',
684 controller='admin/repos', action='edit_fields',
694 conditions={'method': ['GET'], 'function': check_repo},
685 conditions={'method': ['GET'], 'function': check_repo},
695 requirements=URL_NAME_REQUIREMENTS)
686 requirements=URL_NAME_REQUIREMENTS)
696 rmap.connect('create_repo_fields', '/{repo_name}/settings/fields/new',
687 rmap.connect('create_repo_fields', '/{repo_name}/settings/fields/new',
697 controller='admin/repos', action='create_repo_field',
688 controller='admin/repos', action='create_repo_field',
698 conditions={'method': ['PUT'], 'function': check_repo},
689 conditions={'method': ['PUT'], 'function': check_repo},
699 requirements=URL_NAME_REQUIREMENTS)
690 requirements=URL_NAME_REQUIREMENTS)
700 rmap.connect('delete_repo_fields', '/{repo_name}/settings/fields/{field_id}',
691 rmap.connect('delete_repo_fields', '/{repo_name}/settings/fields/{field_id}',
701 controller='admin/repos', action='delete_repo_field',
692 controller='admin/repos', action='delete_repo_field',
702 conditions={'method': ['DELETE'], 'function': check_repo},
693 conditions={'method': ['DELETE'], 'function': check_repo},
703 requirements=URL_NAME_REQUIREMENTS)
694 requirements=URL_NAME_REQUIREMENTS)
704
695
705 rmap.connect('edit_repo_advanced', '/{repo_name}/settings/advanced',
696 rmap.connect('edit_repo_advanced', '/{repo_name}/settings/advanced',
706 controller='admin/repos', action='edit_advanced',
697 controller='admin/repos', action='edit_advanced',
707 conditions={'method': ['GET'], 'function': check_repo},
698 conditions={'method': ['GET'], 'function': check_repo},
708 requirements=URL_NAME_REQUIREMENTS)
699 requirements=URL_NAME_REQUIREMENTS)
709
700
710 rmap.connect('edit_repo_advanced_locking', '/{repo_name}/settings/advanced/locking',
701 rmap.connect('edit_repo_advanced_locking', '/{repo_name}/settings/advanced/locking',
711 controller='admin/repos', action='edit_advanced_locking',
702 controller='admin/repos', action='edit_advanced_locking',
712 conditions={'method': ['PUT'], 'function': check_repo},
703 conditions={'method': ['PUT'], 'function': check_repo},
713 requirements=URL_NAME_REQUIREMENTS)
704 requirements=URL_NAME_REQUIREMENTS)
714 rmap.connect('toggle_locking', '/{repo_name}/settings/advanced/locking_toggle',
705 rmap.connect('toggle_locking', '/{repo_name}/settings/advanced/locking_toggle',
715 controller='admin/repos', action='toggle_locking',
706 controller='admin/repos', action='toggle_locking',
716 conditions={'method': ['GET'], 'function': check_repo},
707 conditions={'method': ['GET'], 'function': check_repo},
717 requirements=URL_NAME_REQUIREMENTS)
708 requirements=URL_NAME_REQUIREMENTS)
718
709
719 rmap.connect('edit_repo_advanced_journal', '/{repo_name}/settings/advanced/journal',
710 rmap.connect('edit_repo_advanced_journal', '/{repo_name}/settings/advanced/journal',
720 controller='admin/repos', action='edit_advanced_journal',
711 controller='admin/repos', action='edit_advanced_journal',
721 conditions={'method': ['PUT'], 'function': check_repo},
712 conditions={'method': ['PUT'], 'function': check_repo},
722 requirements=URL_NAME_REQUIREMENTS)
713 requirements=URL_NAME_REQUIREMENTS)
723
714
724 rmap.connect('edit_repo_advanced_fork', '/{repo_name}/settings/advanced/fork',
715 rmap.connect('edit_repo_advanced_fork', '/{repo_name}/settings/advanced/fork',
725 controller='admin/repos', action='edit_advanced_fork',
716 controller='admin/repos', action='edit_advanced_fork',
726 conditions={'method': ['PUT'], 'function': check_repo},
717 conditions={'method': ['PUT'], 'function': check_repo},
727 requirements=URL_NAME_REQUIREMENTS)
718 requirements=URL_NAME_REQUIREMENTS)
728
719
729 rmap.connect('edit_repo_caches', '/{repo_name}/settings/caches',
720 rmap.connect('edit_repo_caches', '/{repo_name}/settings/caches',
730 controller='admin/repos', action='edit_caches_form',
721 controller='admin/repos', action='edit_caches_form',
731 conditions={'method': ['GET'], 'function': check_repo},
722 conditions={'method': ['GET'], 'function': check_repo},
732 requirements=URL_NAME_REQUIREMENTS)
723 requirements=URL_NAME_REQUIREMENTS)
733 rmap.connect('edit_repo_caches', '/{repo_name}/settings/caches',
724 rmap.connect('edit_repo_caches', '/{repo_name}/settings/caches',
734 controller='admin/repos', action='edit_caches',
725 controller='admin/repos', action='edit_caches',
735 conditions={'method': ['PUT'], 'function': check_repo},
726 conditions={'method': ['PUT'], 'function': check_repo},
736 requirements=URL_NAME_REQUIREMENTS)
727 requirements=URL_NAME_REQUIREMENTS)
737
728
738 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
729 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
739 controller='admin/repos', action='edit_remote_form',
730 controller='admin/repos', action='edit_remote_form',
740 conditions={'method': ['GET'], 'function': check_repo},
731 conditions={'method': ['GET'], 'function': check_repo},
741 requirements=URL_NAME_REQUIREMENTS)
732 requirements=URL_NAME_REQUIREMENTS)
742 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
733 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
743 controller='admin/repos', action='edit_remote',
734 controller='admin/repos', action='edit_remote',
744 conditions={'method': ['PUT'], 'function': check_repo},
735 conditions={'method': ['PUT'], 'function': check_repo},
745 requirements=URL_NAME_REQUIREMENTS)
736 requirements=URL_NAME_REQUIREMENTS)
746
737
747 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
738 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
748 controller='admin/repos', action='edit_statistics_form',
739 controller='admin/repos', action='edit_statistics_form',
749 conditions={'method': ['GET'], 'function': check_repo},
740 conditions={'method': ['GET'], 'function': check_repo},
750 requirements=URL_NAME_REQUIREMENTS)
741 requirements=URL_NAME_REQUIREMENTS)
751 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
742 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
752 controller='admin/repos', action='edit_statistics',
743 controller='admin/repos', action='edit_statistics',
753 conditions={'method': ['PUT'], 'function': check_repo},
744 conditions={'method': ['PUT'], 'function': check_repo},
754 requirements=URL_NAME_REQUIREMENTS)
745 requirements=URL_NAME_REQUIREMENTS)
755 rmap.connect('repo_settings_issuetracker',
746 rmap.connect('repo_settings_issuetracker',
756 '/{repo_name}/settings/issue-tracker',
747 '/{repo_name}/settings/issue-tracker',
757 controller='admin/repos', action='repo_issuetracker',
748 controller='admin/repos', action='repo_issuetracker',
758 conditions={'method': ['GET'], 'function': check_repo},
749 conditions={'method': ['GET'], 'function': check_repo},
759 requirements=URL_NAME_REQUIREMENTS)
750 requirements=URL_NAME_REQUIREMENTS)
760 rmap.connect('repo_issuetracker_test',
751 rmap.connect('repo_issuetracker_test',
761 '/{repo_name}/settings/issue-tracker/test',
752 '/{repo_name}/settings/issue-tracker/test',
762 controller='admin/repos', action='repo_issuetracker_test',
753 controller='admin/repos', action='repo_issuetracker_test',
763 conditions={'method': ['POST'], 'function': check_repo},
754 conditions={'method': ['POST'], 'function': check_repo},
764 requirements=URL_NAME_REQUIREMENTS)
755 requirements=URL_NAME_REQUIREMENTS)
765 rmap.connect('repo_issuetracker_delete',
756 rmap.connect('repo_issuetracker_delete',
766 '/{repo_name}/settings/issue-tracker/delete',
757 '/{repo_name}/settings/issue-tracker/delete',
767 controller='admin/repos', action='repo_issuetracker_delete',
758 controller='admin/repos', action='repo_issuetracker_delete',
768 conditions={'method': ['DELETE'], 'function': check_repo},
759 conditions={'method': ['DELETE'], 'function': check_repo},
769 requirements=URL_NAME_REQUIREMENTS)
760 requirements=URL_NAME_REQUIREMENTS)
770 rmap.connect('repo_issuetracker_save',
761 rmap.connect('repo_issuetracker_save',
771 '/{repo_name}/settings/issue-tracker/save',
762 '/{repo_name}/settings/issue-tracker/save',
772 controller='admin/repos', action='repo_issuetracker_save',
763 controller='admin/repos', action='repo_issuetracker_save',
773 conditions={'method': ['POST'], 'function': check_repo},
764 conditions={'method': ['POST'], 'function': check_repo},
774 requirements=URL_NAME_REQUIREMENTS)
765 requirements=URL_NAME_REQUIREMENTS)
775 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
766 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
776 controller='admin/repos', action='repo_settings_vcs_update',
767 controller='admin/repos', action='repo_settings_vcs_update',
777 conditions={'method': ['POST'], 'function': check_repo},
768 conditions={'method': ['POST'], 'function': check_repo},
778 requirements=URL_NAME_REQUIREMENTS)
769 requirements=URL_NAME_REQUIREMENTS)
779 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
770 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
780 controller='admin/repos', action='repo_settings_vcs',
771 controller='admin/repos', action='repo_settings_vcs',
781 conditions={'method': ['GET'], 'function': check_repo},
772 conditions={'method': ['GET'], 'function': check_repo},
782 requirements=URL_NAME_REQUIREMENTS)
773 requirements=URL_NAME_REQUIREMENTS)
783 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
774 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
784 controller='admin/repos', action='repo_delete_svn_pattern',
775 controller='admin/repos', action='repo_delete_svn_pattern',
785 conditions={'method': ['DELETE'], 'function': check_repo},
776 conditions={'method': ['DELETE'], 'function': check_repo},
786 requirements=URL_NAME_REQUIREMENTS)
777 requirements=URL_NAME_REQUIREMENTS)
787 rmap.connect('repo_pullrequest_settings', '/{repo_name}/settings/pullrequest',
778 rmap.connect('repo_pullrequest_settings', '/{repo_name}/settings/pullrequest',
788 controller='admin/repos', action='repo_settings_pullrequest',
779 controller='admin/repos', action='repo_settings_pullrequest',
789 conditions={'method': ['GET', 'POST'], 'function': check_repo},
780 conditions={'method': ['GET', 'POST'], 'function': check_repo},
790 requirements=URL_NAME_REQUIREMENTS)
781 requirements=URL_NAME_REQUIREMENTS)
791
782
792 # still working url for backward compat.
783 # still working url for backward compat.
793 rmap.connect('raw_changeset_home_depraced',
784 rmap.connect('raw_changeset_home_depraced',
794 '/{repo_name}/raw-changeset/{revision}',
785 '/{repo_name}/raw-changeset/{revision}',
795 controller='changeset', action='changeset_raw',
786 controller='changeset', action='changeset_raw',
796 revision='tip', conditions={'function': check_repo},
787 revision='tip', conditions={'function': check_repo},
797 requirements=URL_NAME_REQUIREMENTS)
788 requirements=URL_NAME_REQUIREMENTS)
798
789
799 # new URLs
790 # new URLs
800 rmap.connect('changeset_raw_home',
791 rmap.connect('changeset_raw_home',
801 '/{repo_name}/changeset-diff/{revision}',
792 '/{repo_name}/changeset-diff/{revision}',
802 controller='changeset', action='changeset_raw',
793 controller='changeset', action='changeset_raw',
803 revision='tip', conditions={'function': check_repo},
794 revision='tip', conditions={'function': check_repo},
804 requirements=URL_NAME_REQUIREMENTS)
795 requirements=URL_NAME_REQUIREMENTS)
805
796
806 rmap.connect('changeset_patch_home',
797 rmap.connect('changeset_patch_home',
807 '/{repo_name}/changeset-patch/{revision}',
798 '/{repo_name}/changeset-patch/{revision}',
808 controller='changeset', action='changeset_patch',
799 controller='changeset', action='changeset_patch',
809 revision='tip', conditions={'function': check_repo},
800 revision='tip', conditions={'function': check_repo},
810 requirements=URL_NAME_REQUIREMENTS)
801 requirements=URL_NAME_REQUIREMENTS)
811
802
812 rmap.connect('changeset_download_home',
803 rmap.connect('changeset_download_home',
813 '/{repo_name}/changeset-download/{revision}',
804 '/{repo_name}/changeset-download/{revision}',
814 controller='changeset', action='changeset_download',
805 controller='changeset', action='changeset_download',
815 revision='tip', conditions={'function': check_repo},
806 revision='tip', conditions={'function': check_repo},
816 requirements=URL_NAME_REQUIREMENTS)
807 requirements=URL_NAME_REQUIREMENTS)
817
808
818 rmap.connect('changeset_comment',
809 rmap.connect('changeset_comment',
819 '/{repo_name}/changeset/{revision}/comment', jsroute=True,
810 '/{repo_name}/changeset/{revision}/comment', jsroute=True,
820 controller='changeset', revision='tip', action='comment',
811 controller='changeset', revision='tip', action='comment',
821 conditions={'function': check_repo},
812 conditions={'function': check_repo},
822 requirements=URL_NAME_REQUIREMENTS)
813 requirements=URL_NAME_REQUIREMENTS)
823
814
824 rmap.connect('changeset_comment_preview',
815 rmap.connect('changeset_comment_preview',
825 '/{repo_name}/changeset/comment/preview', jsroute=True,
816 '/{repo_name}/changeset/comment/preview', jsroute=True,
826 controller='changeset', action='preview_comment',
817 controller='changeset', action='preview_comment',
827 conditions={'function': check_repo, 'method': ['POST']},
818 conditions={'function': check_repo, 'method': ['POST']},
828 requirements=URL_NAME_REQUIREMENTS)
819 requirements=URL_NAME_REQUIREMENTS)
829
820
830 rmap.connect('changeset_comment_delete',
821 rmap.connect('changeset_comment_delete',
831 '/{repo_name}/changeset/comment/{comment_id}/delete',
822 '/{repo_name}/changeset/comment/{comment_id}/delete',
832 controller='changeset', action='delete_comment',
823 controller='changeset', action='delete_comment',
833 conditions={'function': check_repo, 'method': ['DELETE']},
824 conditions={'function': check_repo, 'method': ['DELETE']},
834 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
825 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
835
826
836 rmap.connect('changeset_info', '/{repo_name}/changeset_info/{revision}',
827 rmap.connect('changeset_info', '/{repo_name}/changeset_info/{revision}',
837 controller='changeset', action='changeset_info',
828 controller='changeset', action='changeset_info',
838 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
829 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
839
830
840 rmap.connect('compare_home',
831 rmap.connect('compare_home',
841 '/{repo_name}/compare',
832 '/{repo_name}/compare',
842 controller='compare', action='index',
833 controller='compare', action='index',
843 conditions={'function': check_repo},
834 conditions={'function': check_repo},
844 requirements=URL_NAME_REQUIREMENTS)
835 requirements=URL_NAME_REQUIREMENTS)
845
836
846 rmap.connect('compare_url',
837 rmap.connect('compare_url',
847 '/{repo_name}/compare/{source_ref_type}@{source_ref:.*?}...{target_ref_type}@{target_ref:.*?}',
838 '/{repo_name}/compare/{source_ref_type}@{source_ref:.*?}...{target_ref_type}@{target_ref:.*?}',
848 controller='compare', action='compare',
839 controller='compare', action='compare',
849 conditions={'function': check_repo},
840 conditions={'function': check_repo},
850 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
841 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
851
842
852 rmap.connect('pullrequest_home',
843 rmap.connect('pullrequest_home',
853 '/{repo_name}/pull-request/new', controller='pullrequests',
844 '/{repo_name}/pull-request/new', controller='pullrequests',
854 action='index', conditions={'function': check_repo,
845 action='index', conditions={'function': check_repo,
855 'method': ['GET']},
846 'method': ['GET']},
856 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
847 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
857
848
858 rmap.connect('pullrequest',
849 rmap.connect('pullrequest',
859 '/{repo_name}/pull-request/new', controller='pullrequests',
850 '/{repo_name}/pull-request/new', controller='pullrequests',
860 action='create', conditions={'function': check_repo,
851 action='create', conditions={'function': check_repo,
861 'method': ['POST']},
852 'method': ['POST']},
862 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
853 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
863
854
864 rmap.connect('pullrequest_repo_refs',
855 rmap.connect('pullrequest_repo_refs',
865 '/{repo_name}/pull-request/refs/{target_repo_name:.*?[^/]}',
856 '/{repo_name}/pull-request/refs/{target_repo_name:.*?[^/]}',
866 controller='pullrequests',
857 controller='pullrequests',
867 action='get_repo_refs',
858 action='get_repo_refs',
868 conditions={'function': check_repo, 'method': ['GET']},
859 conditions={'function': check_repo, 'method': ['GET']},
869 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
860 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
870
861
871 rmap.connect('pullrequest_repo_destinations',
862 rmap.connect('pullrequest_repo_destinations',
872 '/{repo_name}/pull-request/repo-destinations',
863 '/{repo_name}/pull-request/repo-destinations',
873 controller='pullrequests',
864 controller='pullrequests',
874 action='get_repo_destinations',
865 action='get_repo_destinations',
875 conditions={'function': check_repo, 'method': ['GET']},
866 conditions={'function': check_repo, 'method': ['GET']},
876 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
867 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
877
868
878 rmap.connect('pullrequest_show',
869 rmap.connect('pullrequest_show',
879 '/{repo_name}/pull-request/{pull_request_id}',
870 '/{repo_name}/pull-request/{pull_request_id}',
880 controller='pullrequests',
871 controller='pullrequests',
881 action='show', conditions={'function': check_repo,
872 action='show', conditions={'function': check_repo,
882 'method': ['GET']},
873 'method': ['GET']},
883 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
874 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
884
875
885 rmap.connect('pullrequest_update',
876 rmap.connect('pullrequest_update',
886 '/{repo_name}/pull-request/{pull_request_id}',
877 '/{repo_name}/pull-request/{pull_request_id}',
887 controller='pullrequests',
878 controller='pullrequests',
888 action='update', conditions={'function': check_repo,
879 action='update', conditions={'function': check_repo,
889 'method': ['PUT']},
880 'method': ['PUT']},
890 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
881 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
891
882
892 rmap.connect('pullrequest_merge',
883 rmap.connect('pullrequest_merge',
893 '/{repo_name}/pull-request/{pull_request_id}',
884 '/{repo_name}/pull-request/{pull_request_id}',
894 controller='pullrequests',
885 controller='pullrequests',
895 action='merge', conditions={'function': check_repo,
886 action='merge', conditions={'function': check_repo,
896 'method': ['POST']},
887 'method': ['POST']},
897 requirements=URL_NAME_REQUIREMENTS)
888 requirements=URL_NAME_REQUIREMENTS)
898
889
899 rmap.connect('pullrequest_delete',
890 rmap.connect('pullrequest_delete',
900 '/{repo_name}/pull-request/{pull_request_id}',
891 '/{repo_name}/pull-request/{pull_request_id}',
901 controller='pullrequests',
892 controller='pullrequests',
902 action='delete', conditions={'function': check_repo,
893 action='delete', conditions={'function': check_repo,
903 'method': ['DELETE']},
894 'method': ['DELETE']},
904 requirements=URL_NAME_REQUIREMENTS)
895 requirements=URL_NAME_REQUIREMENTS)
905
896
906 rmap.connect('pullrequest_show_all',
897 rmap.connect('pullrequest_show_all',
907 '/{repo_name}/pull-request',
898 '/{repo_name}/pull-request',
908 controller='pullrequests',
899 controller='pullrequests',
909 action='show_all', conditions={'function': check_repo,
900 action='show_all', conditions={'function': check_repo,
910 'method': ['GET']},
901 'method': ['GET']},
911 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
902 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
912
903
913 rmap.connect('pullrequest_comment',
904 rmap.connect('pullrequest_comment',
914 '/{repo_name}/pull-request-comment/{pull_request_id}',
905 '/{repo_name}/pull-request-comment/{pull_request_id}',
915 controller='pullrequests',
906 controller='pullrequests',
916 action='comment', conditions={'function': check_repo,
907 action='comment', conditions={'function': check_repo,
917 'method': ['POST']},
908 'method': ['POST']},
918 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
909 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
919
910
920 rmap.connect('pullrequest_comment_delete',
911 rmap.connect('pullrequest_comment_delete',
921 '/{repo_name}/pull-request-comment/{comment_id}/delete',
912 '/{repo_name}/pull-request-comment/{comment_id}/delete',
922 controller='pullrequests', action='delete_comment',
913 controller='pullrequests', action='delete_comment',
923 conditions={'function': check_repo, 'method': ['DELETE']},
914 conditions={'function': check_repo, 'method': ['DELETE']},
924 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
915 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
925
916
926 rmap.connect('summary_home_explicit', '/{repo_name}/summary',
917 rmap.connect('summary_home_explicit', '/{repo_name}/summary',
927 controller='summary', conditions={'function': check_repo},
918 controller='summary', conditions={'function': check_repo},
928 requirements=URL_NAME_REQUIREMENTS)
919 requirements=URL_NAME_REQUIREMENTS)
929
920
930 rmap.connect('branches_home', '/{repo_name}/branches',
921 rmap.connect('branches_home', '/{repo_name}/branches',
931 controller='branches', conditions={'function': check_repo},
922 controller='branches', conditions={'function': check_repo},
932 requirements=URL_NAME_REQUIREMENTS)
923 requirements=URL_NAME_REQUIREMENTS)
933
924
934 rmap.connect('tags_home', '/{repo_name}/tags',
925 rmap.connect('tags_home', '/{repo_name}/tags',
935 controller='tags', conditions={'function': check_repo},
926 controller='tags', conditions={'function': check_repo},
936 requirements=URL_NAME_REQUIREMENTS)
927 requirements=URL_NAME_REQUIREMENTS)
937
928
938 rmap.connect('bookmarks_home', '/{repo_name}/bookmarks',
929 rmap.connect('bookmarks_home', '/{repo_name}/bookmarks',
939 controller='bookmarks', conditions={'function': check_repo},
930 controller='bookmarks', conditions={'function': check_repo},
940 requirements=URL_NAME_REQUIREMENTS)
931 requirements=URL_NAME_REQUIREMENTS)
941
932
942 rmap.connect('changelog_home', '/{repo_name}/changelog', jsroute=True,
933 rmap.connect('changelog_home', '/{repo_name}/changelog', jsroute=True,
943 controller='changelog', conditions={'function': check_repo},
934 controller='changelog', conditions={'function': check_repo},
944 requirements=URL_NAME_REQUIREMENTS)
935 requirements=URL_NAME_REQUIREMENTS)
945
936
946 rmap.connect('changelog_summary_home', '/{repo_name}/changelog_summary',
937 rmap.connect('changelog_summary_home', '/{repo_name}/changelog_summary',
947 controller='changelog', action='changelog_summary',
938 controller='changelog', action='changelog_summary',
948 conditions={'function': check_repo},
939 conditions={'function': check_repo},
949 requirements=URL_NAME_REQUIREMENTS)
940 requirements=URL_NAME_REQUIREMENTS)
950
941
951 rmap.connect('changelog_file_home',
942 rmap.connect('changelog_file_home',
952 '/{repo_name}/changelog/{revision}/{f_path}',
943 '/{repo_name}/changelog/{revision}/{f_path}',
953 controller='changelog', f_path=None,
944 controller='changelog', f_path=None,
954 conditions={'function': check_repo},
945 conditions={'function': check_repo},
955 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
946 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
956
947
957 rmap.connect('changelog_elements', '/{repo_name}/changelog_details',
948 rmap.connect('changelog_elements', '/{repo_name}/changelog_details',
958 controller='changelog', action='changelog_elements',
949 controller='changelog', action='changelog_elements',
959 conditions={'function': check_repo},
950 conditions={'function': check_repo},
960 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
951 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
961
952
962 rmap.connect('files_home', '/{repo_name}/files/{revision}/{f_path}',
953 rmap.connect('files_home', '/{repo_name}/files/{revision}/{f_path}',
963 controller='files', revision='tip', f_path='',
954 controller='files', revision='tip', f_path='',
964 conditions={'function': check_repo},
955 conditions={'function': check_repo},
965 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
956 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
966
957
967 rmap.connect('files_home_simple_catchrev',
958 rmap.connect('files_home_simple_catchrev',
968 '/{repo_name}/files/{revision}',
959 '/{repo_name}/files/{revision}',
969 controller='files', revision='tip', f_path='',
960 controller='files', revision='tip', f_path='',
970 conditions={'function': check_repo},
961 conditions={'function': check_repo},
971 requirements=URL_NAME_REQUIREMENTS)
962 requirements=URL_NAME_REQUIREMENTS)
972
963
973 rmap.connect('files_home_simple_catchall',
964 rmap.connect('files_home_simple_catchall',
974 '/{repo_name}/files',
965 '/{repo_name}/files',
975 controller='files', revision='tip', f_path='',
966 controller='files', revision='tip', f_path='',
976 conditions={'function': check_repo},
967 conditions={'function': check_repo},
977 requirements=URL_NAME_REQUIREMENTS)
968 requirements=URL_NAME_REQUIREMENTS)
978
969
979 rmap.connect('files_history_home',
970 rmap.connect('files_history_home',
980 '/{repo_name}/history/{revision}/{f_path}',
971 '/{repo_name}/history/{revision}/{f_path}',
981 controller='files', action='history', revision='tip', f_path='',
972 controller='files', action='history', revision='tip', f_path='',
982 conditions={'function': check_repo},
973 conditions={'function': check_repo},
983 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
974 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
984
975
985 rmap.connect('files_authors_home',
976 rmap.connect('files_authors_home',
986 '/{repo_name}/authors/{revision}/{f_path}',
977 '/{repo_name}/authors/{revision}/{f_path}',
987 controller='files', action='authors', revision='tip', f_path='',
978 controller='files', action='authors', revision='tip', f_path='',
988 conditions={'function': check_repo},
979 conditions={'function': check_repo},
989 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
980 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
990
981
991 rmap.connect('files_diff_home', '/{repo_name}/diff/{f_path}',
982 rmap.connect('files_diff_home', '/{repo_name}/diff/{f_path}',
992 controller='files', action='diff', f_path='',
983 controller='files', action='diff', f_path='',
993 conditions={'function': check_repo},
984 conditions={'function': check_repo},
994 requirements=URL_NAME_REQUIREMENTS)
985 requirements=URL_NAME_REQUIREMENTS)
995
986
996 rmap.connect('files_diff_2way_home',
987 rmap.connect('files_diff_2way_home',
997 '/{repo_name}/diff-2way/{f_path}',
988 '/{repo_name}/diff-2way/{f_path}',
998 controller='files', action='diff_2way', f_path='',
989 controller='files', action='diff_2way', f_path='',
999 conditions={'function': check_repo},
990 conditions={'function': check_repo},
1000 requirements=URL_NAME_REQUIREMENTS)
991 requirements=URL_NAME_REQUIREMENTS)
1001
992
1002 rmap.connect('files_rawfile_home',
993 rmap.connect('files_rawfile_home',
1003 '/{repo_name}/rawfile/{revision}/{f_path}',
994 '/{repo_name}/rawfile/{revision}/{f_path}',
1004 controller='files', action='rawfile', revision='tip',
995 controller='files', action='rawfile', revision='tip',
1005 f_path='', conditions={'function': check_repo},
996 f_path='', conditions={'function': check_repo},
1006 requirements=URL_NAME_REQUIREMENTS)
997 requirements=URL_NAME_REQUIREMENTS)
1007
998
1008 rmap.connect('files_raw_home',
999 rmap.connect('files_raw_home',
1009 '/{repo_name}/raw/{revision}/{f_path}',
1000 '/{repo_name}/raw/{revision}/{f_path}',
1010 controller='files', action='raw', revision='tip', f_path='',
1001 controller='files', action='raw', revision='tip', f_path='',
1011 conditions={'function': check_repo},
1002 conditions={'function': check_repo},
1012 requirements=URL_NAME_REQUIREMENTS)
1003 requirements=URL_NAME_REQUIREMENTS)
1013
1004
1014 rmap.connect('files_render_home',
1005 rmap.connect('files_render_home',
1015 '/{repo_name}/render/{revision}/{f_path}',
1006 '/{repo_name}/render/{revision}/{f_path}',
1016 controller='files', action='index', revision='tip', f_path='',
1007 controller='files', action='index', revision='tip', f_path='',
1017 rendered=True, conditions={'function': check_repo},
1008 rendered=True, conditions={'function': check_repo},
1018 requirements=URL_NAME_REQUIREMENTS)
1009 requirements=URL_NAME_REQUIREMENTS)
1019
1010
1020 rmap.connect('files_annotate_home',
1011 rmap.connect('files_annotate_home',
1021 '/{repo_name}/annotate/{revision}/{f_path}',
1012 '/{repo_name}/annotate/{revision}/{f_path}',
1022 controller='files', action='index', revision='tip',
1013 controller='files', action='index', revision='tip',
1023 f_path='', annotate=True, conditions={'function': check_repo},
1014 f_path='', annotate=True, conditions={'function': check_repo},
1024 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1015 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1025
1016
1026 rmap.connect('files_annotate_previous',
1017 rmap.connect('files_annotate_previous',
1027 '/{repo_name}/annotate-previous/{revision}/{f_path}',
1018 '/{repo_name}/annotate-previous/{revision}/{f_path}',
1028 controller='files', action='annotate_previous', revision='tip',
1019 controller='files', action='annotate_previous', revision='tip',
1029 f_path='', annotate=True, conditions={'function': check_repo},
1020 f_path='', annotate=True, conditions={'function': check_repo},
1030 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1021 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1031
1022
1032 rmap.connect('files_edit',
1023 rmap.connect('files_edit',
1033 '/{repo_name}/edit/{revision}/{f_path}',
1024 '/{repo_name}/edit/{revision}/{f_path}',
1034 controller='files', action='edit', revision='tip',
1025 controller='files', action='edit', revision='tip',
1035 f_path='',
1026 f_path='',
1036 conditions={'function': check_repo, 'method': ['POST']},
1027 conditions={'function': check_repo, 'method': ['POST']},
1037 requirements=URL_NAME_REQUIREMENTS)
1028 requirements=URL_NAME_REQUIREMENTS)
1038
1029
1039 rmap.connect('files_edit_home',
1030 rmap.connect('files_edit_home',
1040 '/{repo_name}/edit/{revision}/{f_path}',
1031 '/{repo_name}/edit/{revision}/{f_path}',
1041 controller='files', action='edit_home', revision='tip',
1032 controller='files', action='edit_home', revision='tip',
1042 f_path='', conditions={'function': check_repo},
1033 f_path='', conditions={'function': check_repo},
1043 requirements=URL_NAME_REQUIREMENTS)
1034 requirements=URL_NAME_REQUIREMENTS)
1044
1035
1045 rmap.connect('files_add',
1036 rmap.connect('files_add',
1046 '/{repo_name}/add/{revision}/{f_path}',
1037 '/{repo_name}/add/{revision}/{f_path}',
1047 controller='files', action='add', revision='tip',
1038 controller='files', action='add', revision='tip',
1048 f_path='',
1039 f_path='',
1049 conditions={'function': check_repo, 'method': ['POST']},
1040 conditions={'function': check_repo, 'method': ['POST']},
1050 requirements=URL_NAME_REQUIREMENTS)
1041 requirements=URL_NAME_REQUIREMENTS)
1051
1042
1052 rmap.connect('files_add_home',
1043 rmap.connect('files_add_home',
1053 '/{repo_name}/add/{revision}/{f_path}',
1044 '/{repo_name}/add/{revision}/{f_path}',
1054 controller='files', action='add_home', revision='tip',
1045 controller='files', action='add_home', revision='tip',
1055 f_path='', conditions={'function': check_repo},
1046 f_path='', conditions={'function': check_repo},
1056 requirements=URL_NAME_REQUIREMENTS)
1047 requirements=URL_NAME_REQUIREMENTS)
1057
1048
1058 rmap.connect('files_delete',
1049 rmap.connect('files_delete',
1059 '/{repo_name}/delete/{revision}/{f_path}',
1050 '/{repo_name}/delete/{revision}/{f_path}',
1060 controller='files', action='delete', revision='tip',
1051 controller='files', action='delete', revision='tip',
1061 f_path='',
1052 f_path='',
1062 conditions={'function': check_repo, 'method': ['POST']},
1053 conditions={'function': check_repo, 'method': ['POST']},
1063 requirements=URL_NAME_REQUIREMENTS)
1054 requirements=URL_NAME_REQUIREMENTS)
1064
1055
1065 rmap.connect('files_delete_home',
1056 rmap.connect('files_delete_home',
1066 '/{repo_name}/delete/{revision}/{f_path}',
1057 '/{repo_name}/delete/{revision}/{f_path}',
1067 controller='files', action='delete_home', revision='tip',
1058 controller='files', action='delete_home', revision='tip',
1068 f_path='', conditions={'function': check_repo},
1059 f_path='', conditions={'function': check_repo},
1069 requirements=URL_NAME_REQUIREMENTS)
1060 requirements=URL_NAME_REQUIREMENTS)
1070
1061
1071 rmap.connect('files_archive_home', '/{repo_name}/archive/{fname}',
1062 rmap.connect('files_archive_home', '/{repo_name}/archive/{fname}',
1072 controller='files', action='archivefile',
1063 controller='files', action='archivefile',
1073 conditions={'function': check_repo},
1064 conditions={'function': check_repo},
1074 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1065 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1075
1066
1076 rmap.connect('files_nodelist_home',
1067 rmap.connect('files_nodelist_home',
1077 '/{repo_name}/nodelist/{revision}/{f_path}',
1068 '/{repo_name}/nodelist/{revision}/{f_path}',
1078 controller='files', action='nodelist',
1069 controller='files', action='nodelist',
1079 conditions={'function': check_repo},
1070 conditions={'function': check_repo},
1080 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1071 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1081
1072
1082 rmap.connect('files_nodetree_full',
1073 rmap.connect('files_nodetree_full',
1083 '/{repo_name}/nodetree_full/{commit_id}/{f_path}',
1074 '/{repo_name}/nodetree_full/{commit_id}/{f_path}',
1084 controller='files', action='nodetree_full',
1075 controller='files', action='nodetree_full',
1085 conditions={'function': check_repo},
1076 conditions={'function': check_repo},
1086 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1077 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1087
1078
1088 rmap.connect('repo_fork_create_home', '/{repo_name}/fork',
1079 rmap.connect('repo_fork_create_home', '/{repo_name}/fork',
1089 controller='forks', action='fork_create',
1080 controller='forks', action='fork_create',
1090 conditions={'function': check_repo, 'method': ['POST']},
1081 conditions={'function': check_repo, 'method': ['POST']},
1091 requirements=URL_NAME_REQUIREMENTS)
1082 requirements=URL_NAME_REQUIREMENTS)
1092
1083
1093 rmap.connect('repo_fork_home', '/{repo_name}/fork',
1084 rmap.connect('repo_fork_home', '/{repo_name}/fork',
1094 controller='forks', action='fork',
1085 controller='forks', action='fork',
1095 conditions={'function': check_repo},
1086 conditions={'function': check_repo},
1096 requirements=URL_NAME_REQUIREMENTS)
1087 requirements=URL_NAME_REQUIREMENTS)
1097
1088
1098 rmap.connect('repo_forks_home', '/{repo_name}/forks',
1089 rmap.connect('repo_forks_home', '/{repo_name}/forks',
1099 controller='forks', action='forks',
1090 controller='forks', action='forks',
1100 conditions={'function': check_repo},
1091 conditions={'function': check_repo},
1101 requirements=URL_NAME_REQUIREMENTS)
1092 requirements=URL_NAME_REQUIREMENTS)
1102
1093
1103 # must be here for proper group/repo catching pattern
1094 # must be here for proper group/repo catching pattern
1104 _connect_with_slash(
1095 _connect_with_slash(
1105 rmap, 'repo_group_home', '/{group_name}',
1096 rmap, 'repo_group_home', '/{group_name}',
1106 controller='home', action='index_repo_group',
1097 controller='home', action='index_repo_group',
1107 conditions={'function': check_group},
1098 conditions={'function': check_group},
1108 requirements=URL_NAME_REQUIREMENTS)
1099 requirements=URL_NAME_REQUIREMENTS)
1109
1100
1110 # catch all, at the end
1101 # catch all, at the end
1111 _connect_with_slash(
1102 _connect_with_slash(
1112 rmap, 'summary_home', '/{repo_name}', jsroute=True,
1103 rmap, 'summary_home', '/{repo_name}', jsroute=True,
1113 controller='summary', action='index',
1104 controller='summary', action='index',
1114 conditions={'function': check_repo},
1105 conditions={'function': check_repo},
1115 requirements=URL_NAME_REQUIREMENTS)
1106 requirements=URL_NAME_REQUIREMENTS)
1116
1107
1117 return rmap
1108 return rmap
1118
1109
1119
1110
1120 def _connect_with_slash(mapper, name, path, *args, **kwargs):
1111 def _connect_with_slash(mapper, name, path, *args, **kwargs):
1121 """
1112 """
1122 Connect a route with an optional trailing slash in `path`.
1113 Connect a route with an optional trailing slash in `path`.
1123 """
1114 """
1124 mapper.connect(name + '_slash', path + '/', *args, **kwargs)
1115 mapper.connect(name + '_slash', path + '/', *args, **kwargs)
1125 mapper.connect(name, path, *args, **kwargs)
1116 mapper.connect(name, path, *args, **kwargs)
@@ -1,601 +1,601 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="root.mako"/>
2 <%inherit file="root.mako"/>
3
3
4 <div class="outerwrapper">
4 <div class="outerwrapper">
5 <!-- HEADER -->
5 <!-- HEADER -->
6 <div class="header">
6 <div class="header">
7 <div id="header-inner" class="wrapper">
7 <div id="header-inner" class="wrapper">
8 <div id="logo">
8 <div id="logo">
9 <div class="logo-wrapper">
9 <div class="logo-wrapper">
10 <a href="${h.url('home')}"><img src="${h.asset('images/rhodecode-logo-white-216x60.png')}" alt="RhodeCode"/></a>
10 <a href="${h.url('home')}"><img src="${h.asset('images/rhodecode-logo-white-216x60.png')}" alt="RhodeCode"/></a>
11 </div>
11 </div>
12 %if c.rhodecode_name:
12 %if c.rhodecode_name:
13 <div class="branding">- ${h.branding(c.rhodecode_name)}</div>
13 <div class="branding">- ${h.branding(c.rhodecode_name)}</div>
14 %endif
14 %endif
15 </div>
15 </div>
16 <!-- MENU BAR NAV -->
16 <!-- MENU BAR NAV -->
17 ${self.menu_bar_nav()}
17 ${self.menu_bar_nav()}
18 <!-- END MENU BAR NAV -->
18 <!-- END MENU BAR NAV -->
19 </div>
19 </div>
20 </div>
20 </div>
21 ${self.menu_bar_subnav()}
21 ${self.menu_bar_subnav()}
22 <!-- END HEADER -->
22 <!-- END HEADER -->
23
23
24 <!-- CONTENT -->
24 <!-- CONTENT -->
25 <div id="content" class="wrapper">
25 <div id="content" class="wrapper">
26
26
27 <rhodecode-toast id="notifications"></rhodecode-toast>
27 <rhodecode-toast id="notifications"></rhodecode-toast>
28
28
29 <div class="main">
29 <div class="main">
30 ${next.main()}
30 ${next.main()}
31 </div>
31 </div>
32 </div>
32 </div>
33 <!-- END CONTENT -->
33 <!-- END CONTENT -->
34
34
35 </div>
35 </div>
36 <!-- FOOTER -->
36 <!-- FOOTER -->
37 <div id="footer">
37 <div id="footer">
38 <div id="footer-inner" class="title wrapper">
38 <div id="footer-inner" class="title wrapper">
39 <div>
39 <div>
40 <p class="footer-link-right">
40 <p class="footer-link-right">
41 % if c.visual.show_version:
41 % if c.visual.show_version:
42 RhodeCode Enterprise ${c.rhodecode_version} ${c.rhodecode_edition}
42 RhodeCode Enterprise ${c.rhodecode_version} ${c.rhodecode_edition}
43 % endif
43 % endif
44 &copy; 2010-${h.datetime.today().year}, <a href="${h.route_url('rhodecode_official')}" target="_blank">RhodeCode GmbH</a>. All rights reserved.
44 &copy; 2010-${h.datetime.today().year}, <a href="${h.route_url('rhodecode_official')}" target="_blank">RhodeCode GmbH</a>. All rights reserved.
45 % if c.visual.rhodecode_support_url:
45 % if c.visual.rhodecode_support_url:
46 <a href="${c.visual.rhodecode_support_url}" target="_blank">${_('Support')}</a>
46 <a href="${c.visual.rhodecode_support_url}" target="_blank">${_('Support')}</a>
47 % endif
47 % endif
48 </p>
48 </p>
49 <% sid = 'block' if request.GET.get('showrcid') else 'none' %>
49 <% sid = 'block' if request.GET.get('showrcid') else 'none' %>
50 <p class="server-instance" style="display:${sid}">
50 <p class="server-instance" style="display:${sid}">
51 ## display hidden instance ID if specially defined
51 ## display hidden instance ID if specially defined
52 % if c.rhodecode_instanceid:
52 % if c.rhodecode_instanceid:
53 ${_('RhodeCode instance id: %s') % c.rhodecode_instanceid}
53 ${_('RhodeCode instance id: %s') % c.rhodecode_instanceid}
54 % endif
54 % endif
55 </p>
55 </p>
56 </div>
56 </div>
57 </div>
57 </div>
58 </div>
58 </div>
59
59
60 <!-- END FOOTER -->
60 <!-- END FOOTER -->
61
61
62 ### MAKO DEFS ###
62 ### MAKO DEFS ###
63
63
64 <%def name="menu_bar_subnav()">
64 <%def name="menu_bar_subnav()">
65 </%def>
65 </%def>
66
66
67 <%def name="breadcrumbs(class_='breadcrumbs')">
67 <%def name="breadcrumbs(class_='breadcrumbs')">
68 <div class="${class_}">
68 <div class="${class_}">
69 ${self.breadcrumbs_links()}
69 ${self.breadcrumbs_links()}
70 </div>
70 </div>
71 </%def>
71 </%def>
72
72
73 <%def name="admin_menu()">
73 <%def name="admin_menu()">
74 <ul class="admin_menu submenu">
74 <ul class="admin_menu submenu">
75 <li><a href="${h.url('admin_home')}">${_('Admin journal')}</a></li>
75 <li><a href="${h.url('admin_home')}">${_('Admin journal')}</a></li>
76 <li><a href="${h.url('repos')}">${_('Repositories')}</a></li>
76 <li><a href="${h.url('repos')}">${_('Repositories')}</a></li>
77 <li><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
77 <li><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
78 <li><a href="${h.route_path('users')}">${_('Users')}</a></li>
78 <li><a href="${h.route_path('users')}">${_('Users')}</a></li>
79 <li><a href="${h.url('users_groups')}">${_('User groups')}</a></li>
79 <li><a href="${h.url('users_groups')}">${_('User groups')}</a></li>
80 <li><a href="${h.url('admin_permissions_application')}">${_('Permissions')}</a></li>
80 <li><a href="${h.url('admin_permissions_application')}">${_('Permissions')}</a></li>
81 <li><a href="${h.route_path('auth_home', traverse='')}">${_('Authentication')}</a></li>
81 <li><a href="${h.route_path('auth_home', traverse='')}">${_('Authentication')}</a></li>
82 <li><a href="${h.route_path('global_integrations_home')}">${_('Integrations')}</a></li>
82 <li><a href="${h.route_path('global_integrations_home')}">${_('Integrations')}</a></li>
83 <li><a href="${h.url('admin_defaults_repositories')}">${_('Defaults')}</a></li>
83 <li><a href="${h.url('admin_defaults_repositories')}">${_('Defaults')}</a></li>
84 <li class="last"><a href="${h.url('admin_settings')}">${_('Settings')}</a></li>
84 <li class="last"><a href="${h.url('admin_settings')}">${_('Settings')}</a></li>
85 </ul>
85 </ul>
86 </%def>
86 </%def>
87
87
88
88
89 <%def name="dt_info_panel(elements)">
89 <%def name="dt_info_panel(elements)">
90 <dl class="dl-horizontal">
90 <dl class="dl-horizontal">
91 %for dt, dd, title, show_items in elements:
91 %for dt, dd, title, show_items in elements:
92 <dt>${dt}:</dt>
92 <dt>${dt}:</dt>
93 <dd title="${title}">
93 <dd title="${title}">
94 %if callable(dd):
94 %if callable(dd):
95 ## allow lazy evaluation of elements
95 ## allow lazy evaluation of elements
96 ${dd()}
96 ${dd()}
97 %else:
97 %else:
98 ${dd}
98 ${dd}
99 %endif
99 %endif
100 %if show_items:
100 %if show_items:
101 <span class="btn-collapse" data-toggle="item-${h.md5_safe(dt)[:6]}-details">${_('Show More')} </span>
101 <span class="btn-collapse" data-toggle="item-${h.md5_safe(dt)[:6]}-details">${_('Show More')} </span>
102 %endif
102 %endif
103 </dd>
103 </dd>
104
104
105 %if show_items:
105 %if show_items:
106 <div class="collapsable-content" data-toggle="item-${h.md5_safe(dt)[:6]}-details" style="display: none">
106 <div class="collapsable-content" data-toggle="item-${h.md5_safe(dt)[:6]}-details" style="display: none">
107 %for item in show_items:
107 %for item in show_items:
108 <dt></dt>
108 <dt></dt>
109 <dd>${item}</dd>
109 <dd>${item}</dd>
110 %endfor
110 %endfor
111 </div>
111 </div>
112 %endif
112 %endif
113
113
114 %endfor
114 %endfor
115 </dl>
115 </dl>
116 </%def>
116 </%def>
117
117
118
118
119 <%def name="gravatar(email, size=16)">
119 <%def name="gravatar(email, size=16)">
120 <%
120 <%
121 if (size > 16):
121 if (size > 16):
122 gravatar_class = 'gravatar gravatar-large'
122 gravatar_class = 'gravatar gravatar-large'
123 else:
123 else:
124 gravatar_class = 'gravatar'
124 gravatar_class = 'gravatar'
125 %>
125 %>
126 <%doc>
126 <%doc>
127 TODO: johbo: For now we serve double size images to make it smooth
127 TODO: johbo: For now we serve double size images to make it smooth
128 for retina. This is how it worked until now. Should be replaced
128 for retina. This is how it worked until now. Should be replaced
129 with a better solution at some point.
129 with a better solution at some point.
130 </%doc>
130 </%doc>
131 <img class="${gravatar_class}" src="${h.gravatar_url(email, size * 2)}" height="${size}" width="${size}">
131 <img class="${gravatar_class}" src="${h.gravatar_url(email, size * 2)}" height="${size}" width="${size}">
132 </%def>
132 </%def>
133
133
134
134
135 <%def name="gravatar_with_user(contact, size=16, show_disabled=False)">
135 <%def name="gravatar_with_user(contact, size=16, show_disabled=False)">
136 <% email = h.email_or_none(contact) %>
136 <% email = h.email_or_none(contact) %>
137 <div class="rc-user tooltip" title="${h.author_string(email)}">
137 <div class="rc-user tooltip" title="${h.author_string(email)}">
138 ${self.gravatar(email, size)}
138 ${self.gravatar(email, size)}
139 <span class="${'user user-disabled' if show_disabled else 'user'}"> ${h.link_to_user(contact)}</span>
139 <span class="${'user user-disabled' if show_disabled else 'user'}"> ${h.link_to_user(contact)}</span>
140 </div>
140 </div>
141 </%def>
141 </%def>
142
142
143
143
144 ## admin menu used for people that have some admin resources
144 ## admin menu used for people that have some admin resources
145 <%def name="admin_menu_simple(repositories=None, repository_groups=None, user_groups=None)">
145 <%def name="admin_menu_simple(repositories=None, repository_groups=None, user_groups=None)">
146 <ul class="submenu">
146 <ul class="submenu">
147 %if repositories:
147 %if repositories:
148 <li class="local-admin-repos"><a href="${h.url('repos')}">${_('Repositories')}</a></li>
148 <li class="local-admin-repos"><a href="${h.url('repos')}">${_('Repositories')}</a></li>
149 %endif
149 %endif
150 %if repository_groups:
150 %if repository_groups:
151 <li class="local-admin-repo-groups"><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
151 <li class="local-admin-repo-groups"><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
152 %endif
152 %endif
153 %if user_groups:
153 %if user_groups:
154 <li class="local-admin-user-groups"><a href="${h.url('users_groups')}">${_('User groups')}</a></li>
154 <li class="local-admin-user-groups"><a href="${h.url('users_groups')}">${_('User groups')}</a></li>
155 %endif
155 %endif
156 </ul>
156 </ul>
157 </%def>
157 </%def>
158
158
159 <%def name="repo_page_title(repo_instance)">
159 <%def name="repo_page_title(repo_instance)">
160 <div class="title-content">
160 <div class="title-content">
161 <div class="title-main">
161 <div class="title-main">
162 ## SVN/HG/GIT icons
162 ## SVN/HG/GIT icons
163 %if h.is_hg(repo_instance):
163 %if h.is_hg(repo_instance):
164 <i class="icon-hg"></i>
164 <i class="icon-hg"></i>
165 %endif
165 %endif
166 %if h.is_git(repo_instance):
166 %if h.is_git(repo_instance):
167 <i class="icon-git"></i>
167 <i class="icon-git"></i>
168 %endif
168 %endif
169 %if h.is_svn(repo_instance):
169 %if h.is_svn(repo_instance):
170 <i class="icon-svn"></i>
170 <i class="icon-svn"></i>
171 %endif
171 %endif
172
172
173 ## public/private
173 ## public/private
174 %if repo_instance.private:
174 %if repo_instance.private:
175 <i class="icon-repo-private"></i>
175 <i class="icon-repo-private"></i>
176 %else:
176 %else:
177 <i class="icon-repo-public"></i>
177 <i class="icon-repo-public"></i>
178 %endif
178 %endif
179
179
180 ## repo name with group name
180 ## repo name with group name
181 ${h.breadcrumb_repo_link(c.rhodecode_db_repo)}
181 ${h.breadcrumb_repo_link(c.rhodecode_db_repo)}
182
182
183 </div>
183 </div>
184
184
185 ## FORKED
185 ## FORKED
186 %if repo_instance.fork:
186 %if repo_instance.fork:
187 <p>
187 <p>
188 <i class="icon-code-fork"></i> ${_('Fork of')}
188 <i class="icon-code-fork"></i> ${_('Fork of')}
189 <a href="${h.url('summary_home',repo_name=repo_instance.fork.repo_name)}">${repo_instance.fork.repo_name}</a>
189 <a href="${h.url('summary_home',repo_name=repo_instance.fork.repo_name)}">${repo_instance.fork.repo_name}</a>
190 </p>
190 </p>
191 %endif
191 %endif
192
192
193 ## IMPORTED FROM REMOTE
193 ## IMPORTED FROM REMOTE
194 %if repo_instance.clone_uri:
194 %if repo_instance.clone_uri:
195 <p>
195 <p>
196 <i class="icon-code-fork"></i> ${_('Clone from')}
196 <i class="icon-code-fork"></i> ${_('Clone from')}
197 <a href="${h.url(h.safe_str(h.hide_credentials(repo_instance.clone_uri)))}">${h.hide_credentials(repo_instance.clone_uri)}</a>
197 <a href="${h.url(h.safe_str(h.hide_credentials(repo_instance.clone_uri)))}">${h.hide_credentials(repo_instance.clone_uri)}</a>
198 </p>
198 </p>
199 %endif
199 %endif
200
200
201 ## LOCKING STATUS
201 ## LOCKING STATUS
202 %if repo_instance.locked[0]:
202 %if repo_instance.locked[0]:
203 <p class="locking_locked">
203 <p class="locking_locked">
204 <i class="icon-repo-lock"></i>
204 <i class="icon-repo-lock"></i>
205 ${_('Repository locked by %(user)s') % {'user': h.person_by_id(repo_instance.locked[0])}}
205 ${_('Repository locked by %(user)s') % {'user': h.person_by_id(repo_instance.locked[0])}}
206 </p>
206 </p>
207 %elif repo_instance.enable_locking:
207 %elif repo_instance.enable_locking:
208 <p class="locking_unlocked">
208 <p class="locking_unlocked">
209 <i class="icon-repo-unlock"></i>
209 <i class="icon-repo-unlock"></i>
210 ${_('Repository not locked. Pull repository to lock it.')}
210 ${_('Repository not locked. Pull repository to lock it.')}
211 </p>
211 </p>
212 %endif
212 %endif
213
213
214 </div>
214 </div>
215 </%def>
215 </%def>
216
216
217 <%def name="repo_menu(active=None)">
217 <%def name="repo_menu(active=None)">
218 <%
218 <%
219 def is_active(selected):
219 def is_active(selected):
220 if selected == active:
220 if selected == active:
221 return "active"
221 return "active"
222 %>
222 %>
223
223
224 <!--- CONTEXT BAR -->
224 <!--- CONTEXT BAR -->
225 <div id="context-bar">
225 <div id="context-bar">
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.url('summary_home', repo_name=c.repo_name)}"><div class="menulabel">${_('Summary')}</div></a></li>
228 <li class="${is_active('summary')}"><a class="menulink" href="${h.url('summary_home', 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.url('changelog_home', repo_name=c.repo_name)}"><div class="menulabel">${_('Changelog')}</div></a></li>
230 <li class="${is_active('files')}"><a class="menulink" href="${h.url('files_home', repo_name=c.repo_name, revision=c.rhodecode_db_repo.landing_rev[1])}"><div class="menulabel">${_('Files')}</div></a></li>
230 <li class="${is_active('files')}"><a class="menulink" href="${h.url('files_home', repo_name=c.repo_name, revision=c.rhodecode_db_repo.landing_rev[1])}"><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>
233 </li>
233 </li>
234 ## TODO: anderson: ideally it would have a function on the scm_instance "enable_pullrequest() and enable_fork()"
234 ## TODO: anderson: ideally it would have a function on the scm_instance "enable_pullrequest() and enable_fork()"
235 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
235 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
236 <li class="${is_active('showpullrequest')}">
236 <li class="${is_active('showpullrequest')}">
237 <a class="menulink" href="${h.url('pullrequest_show_all',repo_name=c.repo_name)}" title="${_('Show Pull Requests for %s') % c.repo_name}">
237 <a class="menulink" href="${h.url('pullrequest_show_all',repo_name=c.repo_name)}" title="${_('Show Pull Requests for %s') % c.repo_name}">
238 %if c.repository_pull_requests:
238 %if c.repository_pull_requests:
239 <span class="pr_notifications">${c.repository_pull_requests}</span>
239 <span class="pr_notifications">${c.repository_pull_requests}</span>
240 %endif
240 %endif
241 <div class="menulabel">${_('Pull Requests')}</div>
241 <div class="menulabel">${_('Pull Requests')}</div>
242 </a>
242 </a>
243 </li>
243 </li>
244 %endif
244 %endif
245 <li class="${is_active('options')}">
245 <li class="${is_active('options')}">
246 <a class="menulink" href="#" class="dropdown"><div class="menulabel">${_('Options')} <div class="show_more"></div></div></a>
246 <a class="menulink" href="#" class="dropdown"><div class="menulabel">${_('Options')} <div class="show_more"></div></div></a>
247 <ul class="submenu">
247 <ul class="submenu">
248 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
248 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
249 <li><a href="${h.url('edit_repo',repo_name=c.repo_name)}">${_('Settings')}</a></li>
249 <li><a href="${h.url('edit_repo',repo_name=c.repo_name)}">${_('Settings')}</a></li>
250 %endif
250 %endif
251 %if c.rhodecode_db_repo.fork:
251 %if c.rhodecode_db_repo.fork:
252 <li><a href="${h.url('compare_url',repo_name=c.rhodecode_db_repo.fork.repo_name,source_ref_type=c.rhodecode_db_repo.landing_rev[0],source_ref=c.rhodecode_db_repo.landing_rev[1], target_repo=c.repo_name,target_ref_type='branch' if request.GET.get('branch') else c.rhodecode_db_repo.landing_rev[0],target_ref=request.GET.get('branch') or c.rhodecode_db_repo.landing_rev[1], merge=1)}">
252 <li><a href="${h.url('compare_url',repo_name=c.rhodecode_db_repo.fork.repo_name,source_ref_type=c.rhodecode_db_repo.landing_rev[0],source_ref=c.rhodecode_db_repo.landing_rev[1], target_repo=c.repo_name,target_ref_type='branch' if request.GET.get('branch') else c.rhodecode_db_repo.landing_rev[0],target_ref=request.GET.get('branch') or c.rhodecode_db_repo.landing_rev[1], merge=1)}">
253 ${_('Compare fork')}</a></li>
253 ${_('Compare fork')}</a></li>
254 %endif
254 %endif
255
255
256 <li><a href="${h.url('search_repo_home',repo_name=c.repo_name)}">${_('Search')}</a></li>
256 <li><a href="${h.route_path('search_repo',repo_name=c.repo_name)}">${_('Search')}</a></li>
257
257
258 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking:
258 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking:
259 %if c.rhodecode_db_repo.locked[0]:
259 %if c.rhodecode_db_repo.locked[0]:
260 <li><a class="locking_del" href="${h.url('toggle_locking',repo_name=c.repo_name)}">${_('Unlock')}</a></li>
260 <li><a class="locking_del" href="${h.url('toggle_locking',repo_name=c.repo_name)}">${_('Unlock')}</a></li>
261 %else:
261 %else:
262 <li><a class="locking_add" href="${h.url('toggle_locking',repo_name=c.repo_name)}">${_('Lock')}</a></li>
262 <li><a class="locking_add" href="${h.url('toggle_locking',repo_name=c.repo_name)}">${_('Lock')}</a></li>
263 %endif
263 %endif
264 %endif
264 %endif
265 %if c.rhodecode_user.username != h.DEFAULT_USER:
265 %if c.rhodecode_user.username != h.DEFAULT_USER:
266 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
266 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
267 <li><a href="${h.url('repo_fork_home',repo_name=c.repo_name)}">${_('Fork')}</a></li>
267 <li><a href="${h.url('repo_fork_home',repo_name=c.repo_name)}">${_('Fork')}</a></li>
268 <li><a href="${h.url('pullrequest_home',repo_name=c.repo_name)}">${_('Create Pull Request')}</a></li>
268 <li><a href="${h.url('pullrequest_home',repo_name=c.repo_name)}">${_('Create Pull Request')}</a></li>
269 %endif
269 %endif
270 %endif
270 %endif
271 </ul>
271 </ul>
272 </li>
272 </li>
273 </ul>
273 </ul>
274 </div>
274 </div>
275 <div class="clear"></div>
275 <div class="clear"></div>
276 </div>
276 </div>
277 <!--- END CONTEXT BAR -->
277 <!--- END CONTEXT BAR -->
278
278
279 </%def>
279 </%def>
280
280
281 <%def name="usermenu(active=False)">
281 <%def name="usermenu(active=False)">
282 ## USER MENU
282 ## USER MENU
283 <li id="quick_login_li" class="${'active' if active else ''}">
283 <li id="quick_login_li" class="${'active' if active else ''}">
284 <a id="quick_login_link" class="menulink childs">
284 <a id="quick_login_link" class="menulink childs">
285 ${gravatar(c.rhodecode_user.email, 20)}
285 ${gravatar(c.rhodecode_user.email, 20)}
286 <span class="user">
286 <span class="user">
287 %if c.rhodecode_user.username != h.DEFAULT_USER:
287 %if c.rhodecode_user.username != h.DEFAULT_USER:
288 <span class="menu_link_user">${c.rhodecode_user.username}</span><div class="show_more"></div>
288 <span class="menu_link_user">${c.rhodecode_user.username}</span><div class="show_more"></div>
289 %else:
289 %else:
290 <span>${_('Sign in')}</span>
290 <span>${_('Sign in')}</span>
291 %endif
291 %endif
292 </span>
292 </span>
293 </a>
293 </a>
294
294
295 <div class="user-menu submenu">
295 <div class="user-menu submenu">
296 <div id="quick_login">
296 <div id="quick_login">
297 %if c.rhodecode_user.username == h.DEFAULT_USER:
297 %if c.rhodecode_user.username == h.DEFAULT_USER:
298 <h4>${_('Sign in to your account')}</h4>
298 <h4>${_('Sign in to your account')}</h4>
299 ${h.form(h.route_path('login', _query={'came_from': h.url.current()}), needs_csrf_token=False)}
299 ${h.form(h.route_path('login', _query={'came_from': h.url.current()}), needs_csrf_token=False)}
300 <div class="form form-vertical">
300 <div class="form form-vertical">
301 <div class="fields">
301 <div class="fields">
302 <div class="field">
302 <div class="field">
303 <div class="label">
303 <div class="label">
304 <label for="username">${_('Username')}:</label>
304 <label for="username">${_('Username')}:</label>
305 </div>
305 </div>
306 <div class="input">
306 <div class="input">
307 ${h.text('username',class_='focus',tabindex=1)}
307 ${h.text('username',class_='focus',tabindex=1)}
308 </div>
308 </div>
309
309
310 </div>
310 </div>
311 <div class="field">
311 <div class="field">
312 <div class="label">
312 <div class="label">
313 <label for="password">${_('Password')}:</label>
313 <label for="password">${_('Password')}:</label>
314 %if h.HasPermissionAny('hg.password_reset.enabled')():
314 %if h.HasPermissionAny('hg.password_reset.enabled')():
315 <span class="forgot_password">${h.link_to(_('(Forgot password?)'),h.route_path('reset_password'), class_='pwd_reset')}</span>
315 <span class="forgot_password">${h.link_to(_('(Forgot password?)'),h.route_path('reset_password'), class_='pwd_reset')}</span>
316 %endif
316 %endif
317 </div>
317 </div>
318 <div class="input">
318 <div class="input">
319 ${h.password('password',class_='focus',tabindex=2)}
319 ${h.password('password',class_='focus',tabindex=2)}
320 </div>
320 </div>
321 </div>
321 </div>
322 <div class="buttons">
322 <div class="buttons">
323 <div class="register">
323 <div class="register">
324 %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')():
324 %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')():
325 ${h.link_to(_("Don't have an account ?"),h.route_path('register'))}
325 ${h.link_to(_("Don't have an account ?"),h.route_path('register'))}
326 %endif
326 %endif
327 </div>
327 </div>
328 <div class="submit">
328 <div class="submit">
329 ${h.submit('sign_in',_('Sign In'),class_="btn btn-small",tabindex=3)}
329 ${h.submit('sign_in',_('Sign In'),class_="btn btn-small",tabindex=3)}
330 </div>
330 </div>
331 </div>
331 </div>
332 </div>
332 </div>
333 </div>
333 </div>
334 ${h.end_form()}
334 ${h.end_form()}
335 %else:
335 %else:
336 <div class="">
336 <div class="">
337 <div class="big_gravatar">${gravatar(c.rhodecode_user.email, 48)}</div>
337 <div class="big_gravatar">${gravatar(c.rhodecode_user.email, 48)}</div>
338 <div class="full_name">${c.rhodecode_user.full_name_or_username}</div>
338 <div class="full_name">${c.rhodecode_user.full_name_or_username}</div>
339 <div class="email">${c.rhodecode_user.email}</div>
339 <div class="email">${c.rhodecode_user.email}</div>
340 </div>
340 </div>
341 <div class="">
341 <div class="">
342 <ol class="links">
342 <ol class="links">
343 <li>${h.link_to(_(u'My account'),h.route_path('my_account_profile'))}</li>
343 <li>${h.link_to(_(u'My account'),h.route_path('my_account_profile'))}</li>
344 % if c.rhodecode_user.personal_repo_group:
344 % if c.rhodecode_user.personal_repo_group:
345 <li>${h.link_to(_(u'My personal group'), h.url('repo_group_home', group_name=c.rhodecode_user.personal_repo_group.group_name))}</li>
345 <li>${h.link_to(_(u'My personal group'), h.url('repo_group_home', group_name=c.rhodecode_user.personal_repo_group.group_name))}</li>
346 % endif
346 % endif
347 <li class="logout">
347 <li class="logout">
348 ${h.secure_form(h.route_path('logout'))}
348 ${h.secure_form(h.route_path('logout'))}
349 ${h.submit('log_out', _(u'Sign Out'),class_="btn btn-primary")}
349 ${h.submit('log_out', _(u'Sign Out'),class_="btn btn-primary")}
350 ${h.end_form()}
350 ${h.end_form()}
351 </li>
351 </li>
352 </ol>
352 </ol>
353 </div>
353 </div>
354 %endif
354 %endif
355 </div>
355 </div>
356 </div>
356 </div>
357 %if c.rhodecode_user.username != h.DEFAULT_USER:
357 %if c.rhodecode_user.username != h.DEFAULT_USER:
358 <div class="pill_container">
358 <div class="pill_container">
359 % if c.unread_notifications == 0:
359 % if c.unread_notifications == 0:
360 <a class="menu_link_notifications empty" href="${h.url('notifications')}">${c.unread_notifications}</a>
360 <a class="menu_link_notifications empty" href="${h.url('notifications')}">${c.unread_notifications}</a>
361 % else:
361 % else:
362 <a class="menu_link_notifications" href="${h.url('notifications')}">${c.unread_notifications}</a>
362 <a class="menu_link_notifications" href="${h.url('notifications')}">${c.unread_notifications}</a>
363 % endif
363 % endif
364 </div>
364 </div>
365 % endif
365 % endif
366 </li>
366 </li>
367 </%def>
367 </%def>
368
368
369 <%def name="menu_items(active=None)">
369 <%def name="menu_items(active=None)">
370 <%
370 <%
371 def is_active(selected):
371 def is_active(selected):
372 if selected == active:
372 if selected == active:
373 return "active"
373 return "active"
374 return ""
374 return ""
375 %>
375 %>
376 <ul id="quick" class="main_nav navigation horizontal-list">
376 <ul id="quick" class="main_nav navigation horizontal-list">
377 <!-- repo switcher -->
377 <!-- repo switcher -->
378 <li class="${is_active('repositories')} repo_switcher_li has_select2">
378 <li class="${is_active('repositories')} repo_switcher_li has_select2">
379 <input id="repo_switcher" name="repo_switcher" type="hidden">
379 <input id="repo_switcher" name="repo_switcher" type="hidden">
380 </li>
380 </li>
381
381
382 ## ROOT MENU
382 ## ROOT MENU
383 %if c.rhodecode_user.username != h.DEFAULT_USER:
383 %if c.rhodecode_user.username != h.DEFAULT_USER:
384 <li class="${is_active('journal')}">
384 <li class="${is_active('journal')}">
385 <a class="menulink" title="${_('Show activity journal')}" href="${h.url('journal')}">
385 <a class="menulink" title="${_('Show activity journal')}" href="${h.url('journal')}">
386 <div class="menulabel">${_('Journal')}</div>
386 <div class="menulabel">${_('Journal')}</div>
387 </a>
387 </a>
388 </li>
388 </li>
389 %else:
389 %else:
390 <li class="${is_active('journal')}">
390 <li class="${is_active('journal')}">
391 <a class="menulink" title="${_('Show Public activity journal')}" href="${h.url('public_journal')}">
391 <a class="menulink" title="${_('Show Public activity journal')}" href="${h.url('public_journal')}">
392 <div class="menulabel">${_('Public journal')}</div>
392 <div class="menulabel">${_('Public journal')}</div>
393 </a>
393 </a>
394 </li>
394 </li>
395 %endif
395 %endif
396 <li class="${is_active('gists')}">
396 <li class="${is_active('gists')}">
397 <a class="menulink childs" title="${_('Show Gists')}" href="${h.url('gists')}">
397 <a class="menulink childs" title="${_('Show Gists')}" href="${h.url('gists')}">
398 <div class="menulabel">${_('Gists')}</div>
398 <div class="menulabel">${_('Gists')}</div>
399 </a>
399 </a>
400 </li>
400 </li>
401 <li class="${is_active('search')}">
401 <li class="${is_active('search')}">
402 <a class="menulink" title="${_('Search in repositories you have access to')}" href="${h.url('search')}">
402 <a class="menulink" title="${_('Search in repositories you have access to')}" href="${h.route_path('search')}">
403 <div class="menulabel">${_('Search')}</div>
403 <div class="menulabel">${_('Search')}</div>
404 </a>
404 </a>
405 </li>
405 </li>
406 % if h.HasPermissionAll('hg.admin')('access admin main page'):
406 % if h.HasPermissionAll('hg.admin')('access admin main page'):
407 <li class="${is_active('admin')}">
407 <li class="${is_active('admin')}">
408 <a class="menulink childs" title="${_('Admin settings')}" href="#" onclick="return false;">
408 <a class="menulink childs" title="${_('Admin settings')}" href="#" onclick="return false;">
409 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
409 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
410 </a>
410 </a>
411 ${admin_menu()}
411 ${admin_menu()}
412 </li>
412 </li>
413 % elif c.rhodecode_user.repositories_admin or c.rhodecode_user.repository_groups_admin or c.rhodecode_user.user_groups_admin:
413 % elif c.rhodecode_user.repositories_admin or c.rhodecode_user.repository_groups_admin or c.rhodecode_user.user_groups_admin:
414 <li class="${is_active('admin')}">
414 <li class="${is_active('admin')}">
415 <a class="menulink childs" title="${_('Delegated Admin settings')}">
415 <a class="menulink childs" title="${_('Delegated Admin settings')}">
416 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
416 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
417 </a>
417 </a>
418 ${admin_menu_simple(c.rhodecode_user.repositories_admin,
418 ${admin_menu_simple(c.rhodecode_user.repositories_admin,
419 c.rhodecode_user.repository_groups_admin,
419 c.rhodecode_user.repository_groups_admin,
420 c.rhodecode_user.user_groups_admin or h.HasPermissionAny('hg.usergroup.create.true')())}
420 c.rhodecode_user.user_groups_admin or h.HasPermissionAny('hg.usergroup.create.true')())}
421 </li>
421 </li>
422 % endif
422 % endif
423 % if c.debug_style:
423 % if c.debug_style:
424 <li class="${is_active('debug_style')}">
424 <li class="${is_active('debug_style')}">
425 <a class="menulink" title="${_('Style')}" href="${h.url('debug_style_home')}">
425 <a class="menulink" title="${_('Style')}" href="${h.url('debug_style_home')}">
426 <div class="menulabel">${_('Style')}</div>
426 <div class="menulabel">${_('Style')}</div>
427 </a>
427 </a>
428 </li>
428 </li>
429 % endif
429 % endif
430 ## render extra user menu
430 ## render extra user menu
431 ${usermenu(active=(active=='my_account'))}
431 ${usermenu(active=(active=='my_account'))}
432 </ul>
432 </ul>
433
433
434 <script type="text/javascript">
434 <script type="text/javascript">
435 var visual_show_public_icon = "${c.visual.show_public_icon}" == "True";
435 var visual_show_public_icon = "${c.visual.show_public_icon}" == "True";
436
436
437 /*format the look of items in the list*/
437 /*format the look of items in the list*/
438 var format = function(state, escapeMarkup){
438 var format = function(state, escapeMarkup){
439 if (!state.id){
439 if (!state.id){
440 return state.text; // optgroup
440 return state.text; // optgroup
441 }
441 }
442 var obj_dict = state.obj;
442 var obj_dict = state.obj;
443 var tmpl = '';
443 var tmpl = '';
444
444
445 if(obj_dict && state.type == 'repo'){
445 if(obj_dict && state.type == 'repo'){
446 if(obj_dict['repo_type'] === 'hg'){
446 if(obj_dict['repo_type'] === 'hg'){
447 tmpl += '<i class="icon-hg"></i> ';
447 tmpl += '<i class="icon-hg"></i> ';
448 }
448 }
449 else if(obj_dict['repo_type'] === 'git'){
449 else if(obj_dict['repo_type'] === 'git'){
450 tmpl += '<i class="icon-git"></i> ';
450 tmpl += '<i class="icon-git"></i> ';
451 }
451 }
452 else if(obj_dict['repo_type'] === 'svn'){
452 else if(obj_dict['repo_type'] === 'svn'){
453 tmpl += '<i class="icon-svn"></i> ';
453 tmpl += '<i class="icon-svn"></i> ';
454 }
454 }
455 if(obj_dict['private']){
455 if(obj_dict['private']){
456 tmpl += '<i class="icon-lock" ></i> ';
456 tmpl += '<i class="icon-lock" ></i> ';
457 }
457 }
458 else if(visual_show_public_icon){
458 else if(visual_show_public_icon){
459 tmpl += '<i class="icon-unlock-alt"></i> ';
459 tmpl += '<i class="icon-unlock-alt"></i> ';
460 }
460 }
461 }
461 }
462 if(obj_dict && state.type == 'commit') {
462 if(obj_dict && state.type == 'commit') {
463 tmpl += '<i class="icon-tag"></i>';
463 tmpl += '<i class="icon-tag"></i>';
464 }
464 }
465 if(obj_dict && state.type == 'group'){
465 if(obj_dict && state.type == 'group'){
466 tmpl += '<i class="icon-folder-close"></i> ';
466 tmpl += '<i class="icon-folder-close"></i> ';
467 }
467 }
468 tmpl += escapeMarkup(state.text);
468 tmpl += escapeMarkup(state.text);
469 return tmpl;
469 return tmpl;
470 };
470 };
471
471
472 var formatResult = function(result, container, query, escapeMarkup) {
472 var formatResult = function(result, container, query, escapeMarkup) {
473 return format(result, escapeMarkup);
473 return format(result, escapeMarkup);
474 };
474 };
475
475
476 var formatSelection = function(data, container, escapeMarkup) {
476 var formatSelection = function(data, container, escapeMarkup) {
477 return format(data, escapeMarkup);
477 return format(data, escapeMarkup);
478 };
478 };
479
479
480 $("#repo_switcher").select2({
480 $("#repo_switcher").select2({
481 cachedDataSource: {},
481 cachedDataSource: {},
482 minimumInputLength: 2,
482 minimumInputLength: 2,
483 placeholder: '<div class="menulabel">${_('Go to')} <div class="show_more"></div></div>',
483 placeholder: '<div class="menulabel">${_('Go to')} <div class="show_more"></div></div>',
484 dropdownAutoWidth: true,
484 dropdownAutoWidth: true,
485 formatResult: formatResult,
485 formatResult: formatResult,
486 formatSelection: formatSelection,
486 formatSelection: formatSelection,
487 containerCssClass: "repo-switcher",
487 containerCssClass: "repo-switcher",
488 dropdownCssClass: "repo-switcher-dropdown",
488 dropdownCssClass: "repo-switcher-dropdown",
489 escapeMarkup: function(m){
489 escapeMarkup: function(m){
490 // don't escape our custom placeholder
490 // don't escape our custom placeholder
491 if(m.substr(0,23) == '<div class="menulabel">'){
491 if(m.substr(0,23) == '<div class="menulabel">'){
492 return m;
492 return m;
493 }
493 }
494
494
495 return Select2.util.escapeMarkup(m);
495 return Select2.util.escapeMarkup(m);
496 },
496 },
497 query: $.debounce(250, function(query){
497 query: $.debounce(250, function(query){
498 self = this;
498 self = this;
499 var cacheKey = query.term;
499 var cacheKey = query.term;
500 var cachedData = self.cachedDataSource[cacheKey];
500 var cachedData = self.cachedDataSource[cacheKey];
501
501
502 if (cachedData) {
502 if (cachedData) {
503 query.callback({results: cachedData.results});
503 query.callback({results: cachedData.results});
504 } else {
504 } else {
505 $.ajax({
505 $.ajax({
506 url: pyroutes.url('goto_switcher_data'),
506 url: pyroutes.url('goto_switcher_data'),
507 data: {'query': query.term},
507 data: {'query': query.term},
508 dataType: 'json',
508 dataType: 'json',
509 type: 'GET',
509 type: 'GET',
510 success: function(data) {
510 success: function(data) {
511 self.cachedDataSource[cacheKey] = data;
511 self.cachedDataSource[cacheKey] = data;
512 query.callback({results: data.results});
512 query.callback({results: data.results});
513 },
513 },
514 error: function(data, textStatus, errorThrown) {
514 error: function(data, textStatus, errorThrown) {
515 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
515 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
516 }
516 }
517 })
517 })
518 }
518 }
519 })
519 })
520 });
520 });
521
521
522 $("#repo_switcher").on('select2-selecting', function(e){
522 $("#repo_switcher").on('select2-selecting', function(e){
523 e.preventDefault();
523 e.preventDefault();
524 window.location = e.choice.url;
524 window.location = e.choice.url;
525 });
525 });
526
526
527 </script>
527 </script>
528 <script src="${h.asset('js/rhodecode/base/keyboard-bindings.js', ver=c.rhodecode_version_hash)}"></script>
528 <script src="${h.asset('js/rhodecode/base/keyboard-bindings.js', ver=c.rhodecode_version_hash)}"></script>
529 </%def>
529 </%def>
530
530
531 <div class="modal" id="help_kb" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
531 <div class="modal" id="help_kb" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
532 <div class="modal-dialog">
532 <div class="modal-dialog">
533 <div class="modal-content">
533 <div class="modal-content">
534 <div class="modal-header">
534 <div class="modal-header">
535 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
535 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
536 <h4 class="modal-title" id="myModalLabel">${_('Keyboard shortcuts')}</h4>
536 <h4 class="modal-title" id="myModalLabel">${_('Keyboard shortcuts')}</h4>
537 </div>
537 </div>
538 <div class="modal-body">
538 <div class="modal-body">
539 <div class="block-left">
539 <div class="block-left">
540 <table class="keyboard-mappings">
540 <table class="keyboard-mappings">
541 <tbody>
541 <tbody>
542 <tr>
542 <tr>
543 <th></th>
543 <th></th>
544 <th>${_('Site-wide shortcuts')}</th>
544 <th>${_('Site-wide shortcuts')}</th>
545 </tr>
545 </tr>
546 <%
546 <%
547 elems = [
547 elems = [
548 ('/', 'Open quick search box'),
548 ('/', 'Open quick search box'),
549 ('g h', 'Goto home page'),
549 ('g h', 'Goto home page'),
550 ('g g', 'Goto my private gists page'),
550 ('g g', 'Goto my private gists page'),
551 ('g G', 'Goto my public gists page'),
551 ('g G', 'Goto my public gists page'),
552 ('n r', 'New repository page'),
552 ('n r', 'New repository page'),
553 ('n g', 'New gist page'),
553 ('n g', 'New gist page'),
554 ]
554 ]
555 %>
555 %>
556 %for key, desc in elems:
556 %for key, desc in elems:
557 <tr>
557 <tr>
558 <td class="keys">
558 <td class="keys">
559 <span class="key tag">${key}</span>
559 <span class="key tag">${key}</span>
560 </td>
560 </td>
561 <td>${desc}</td>
561 <td>${desc}</td>
562 </tr>
562 </tr>
563 %endfor
563 %endfor
564 </tbody>
564 </tbody>
565 </table>
565 </table>
566 </div>
566 </div>
567 <div class="block-left">
567 <div class="block-left">
568 <table class="keyboard-mappings">
568 <table class="keyboard-mappings">
569 <tbody>
569 <tbody>
570 <tr>
570 <tr>
571 <th></th>
571 <th></th>
572 <th>${_('Repositories')}</th>
572 <th>${_('Repositories')}</th>
573 </tr>
573 </tr>
574 <%
574 <%
575 elems = [
575 elems = [
576 ('g s', 'Goto summary page'),
576 ('g s', 'Goto summary page'),
577 ('g c', 'Goto changelog page'),
577 ('g c', 'Goto changelog page'),
578 ('g f', 'Goto files page'),
578 ('g f', 'Goto files page'),
579 ('g F', 'Goto files page with file search activated'),
579 ('g F', 'Goto files page with file search activated'),
580 ('g p', 'Goto pull requests page'),
580 ('g p', 'Goto pull requests page'),
581 ('g o', 'Goto repository settings'),
581 ('g o', 'Goto repository settings'),
582 ('g O', 'Goto repository permissions settings'),
582 ('g O', 'Goto repository permissions settings'),
583 ]
583 ]
584 %>
584 %>
585 %for key, desc in elems:
585 %for key, desc in elems:
586 <tr>
586 <tr>
587 <td class="keys">
587 <td class="keys">
588 <span class="key tag">${key}</span>
588 <span class="key tag">${key}</span>
589 </td>
589 </td>
590 <td>${desc}</td>
590 <td>${desc}</td>
591 </tr>
591 </tr>
592 %endfor
592 %endfor
593 </tbody>
593 </tbody>
594 </table>
594 </table>
595 </div>
595 </div>
596 </div>
596 </div>
597 <div class="modal-footer">
597 <div class="modal-footer">
598 </div>
598 </div>
599 </div><!-- /.modal-content -->
599 </div><!-- /.modal-content -->
600 </div><!-- /.modal-dialog -->
600 </div><!-- /.modal-dialog -->
601 </div><!-- /.modal -->
601 </div><!-- /.modal -->
@@ -1,108 +1,108 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.mako"/>
2 <%inherit file="/base/base.mako"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 %if c.repo_name:
5 %if c.repo_name:
6 ${_('Search inside repository %(repo_name)s') % {'repo_name': c.repo_name}}
6 ${_('Search inside repository %(repo_name)s') % {'repo_name': c.repo_name}}
7 %else:
7 %else:
8 ${_('Search inside all accessible repositories')}
8 ${_('Search inside all accessible repositories')}
9 %endif
9 %endif
10 %if c.rhodecode_name:
10 %if c.rhodecode_name:
11 &middot; ${h.branding(c.rhodecode_name)}
11 &middot; ${h.branding(c.rhodecode_name)}
12 %endif
12 %endif
13 </%def>
13 </%def>
14
14
15 <%def name="breadcrumbs_links()">
15 <%def name="breadcrumbs_links()">
16 %if c.repo_name:
16 %if c.repo_name:
17 ${_('Search inside repository %(repo_name)s') % {'repo_name': c.repo_name}}
17 ${_('Search inside repository %(repo_name)s') % {'repo_name': c.repo_name}}
18 %else:
18 %else:
19 ${_('Search inside all accessible repositories')}
19 ${_('Search inside all accessible repositories')}
20 %endif
20 %endif
21 %if c.cur_query:
21 %if c.cur_query:
22 &raquo;
22 &raquo;
23 ${c.cur_query}
23 ${c.cur_query}
24 %endif
24 %endif
25 </%def>
25 </%def>
26
26
27 <%def name="menu_bar_nav()">
27 <%def name="menu_bar_nav()">
28 %if c.repo_name:
28 %if c.repo_name:
29 ${self.menu_items(active='repositories')}
29 ${self.menu_items(active='repositories')}
30 %else:
30 %else:
31 ${self.menu_items(active='search')}
31 ${self.menu_items(active='search')}
32 %endif
32 %endif
33 </%def>
33 </%def>
34
34
35 <%def name="menu_bar_subnav()">
35 <%def name="menu_bar_subnav()">
36 %if c.repo_name:
36 %if c.repo_name:
37 ${self.repo_menu(active='options')}
37 ${self.repo_menu(active='options')}
38 %endif
38 %endif
39 </%def>
39 </%def>
40
40
41 <%def name="main()">
41 <%def name="main()">
42 <div class="box">
42 <div class="box">
43 %if c.repo_name:
43 %if c.repo_name:
44 <!-- box / title -->
44 <!-- box / title -->
45 <div class="title">
45 <div class="title">
46 ${self.repo_page_title(c.rhodecode_db_repo)}
46 ${self.repo_page_title(c.rhodecode_db_repo)}
47 </div>
47 </div>
48 ${h.form(h.url('search_repo_home',repo_name=c.repo_name),method='get')}
48 ${h.form(h.route_path('search_repo',repo_name=c.repo_name),method='get')}
49 %else:
49 %else:
50 <!-- box / title -->
50 <!-- box / title -->
51 <div class="title">
51 <div class="title">
52 ${self.breadcrumbs()}
52 ${self.breadcrumbs()}
53 <ul class="links">&nbsp;</ul>
53 <ul class="links">&nbsp;</ul>
54 </div>
54 </div>
55 <!-- end box / title -->
55 <!-- end box / title -->
56 ${h.form(h.url('search'),method='get')}
56 ${h.form(h.route_path('search'), method='get')}
57 %endif
57 %endif
58 <div class="form search-form">
58 <div class="form search-form">
59 <div class="fields">
59 <div class="fields">
60 <label for="q">${_('Search item')}:</label>
60 <label for="q">${_('Search item')}:</label>
61 ${h.text('q', c.cur_query)}
61 ${h.text('q', c.cur_query)}
62
62
63 ${h.select('type',c.search_type,[('content',_('File contents')), ('commit',_('Commit messages')), ('path',_('File names')),],id='id_search_type')}
63 ${h.select('type',c.search_type,[('content',_('File contents')), ('commit',_('Commit messages')), ('path',_('File names')),],id='id_search_type')}
64 <input type="submit" value="${_('Search')}" class="btn"/>
64 <input type="submit" value="${_('Search')}" class="btn"/>
65 <br/>
65 <br/>
66
66
67 <div class="search-feedback-items">
67 <div class="search-feedback-items">
68 % for error in c.errors:
68 % for error in c.errors:
69 <span class="error-message">
69 <span class="error-message">
70 % for k,v in error.asdict().items():
70 % for k,v in error.asdict().items():
71 ${k} - ${v}
71 ${k} - ${v}
72 % endfor
72 % endfor
73 </span>
73 </span>
74 % endfor
74 % endfor
75 <div class="field">
75 <div class="field">
76 <p class="filterexample" style="position: inherit" onclick="$('#search-help').toggle()">${_('Example Queries')}</p>
76 <p class="filterexample" style="position: inherit" onclick="$('#search-help').toggle()">${_('Example Queries')}</p>
77 <pre id="search-help" style="display: none">${h.tooltip(h.search_filter_help(c.searcher))}</pre>
77 <pre id="search-help" style="display: none">${h.tooltip(h.search_filter_help(c.searcher))}</pre>
78 </div>
78 </div>
79
79
80 <div class="field">${c.runtime}</div>
80 <div class="field">${c.runtime}</div>
81 </div>
81 </div>
82 </div>
82 </div>
83 </div>
83 </div>
84
84
85 ${h.end_form()}
85 ${h.end_form()}
86 <div class="search">
86 <div class="search">
87 % if c.search_type == 'content':
87 % if c.search_type == 'content':
88 <%include file='search_content.mako'/>
88 <%include file='search_content.mako'/>
89 % elif c.search_type == 'path':
89 % elif c.search_type == 'path':
90 <%include file='search_path.mako'/>
90 <%include file='search_path.mako'/>
91 % elif c.search_type == 'commit':
91 % elif c.search_type == 'commit':
92 <%include file='search_commit.mako'/>
92 <%include file='search_commit.mako'/>
93 % elif c.search_type == 'repository':
93 % elif c.search_type == 'repository':
94 <%include file='search_repository.mako'/>
94 <%include file='search_repository.mako'/>
95 % endif
95 % endif
96 </div>
96 </div>
97 </div>
97 </div>
98 <script>
98 <script>
99 $(document).ready(function(){
99 $(document).ready(function(){
100 $("#id_search_type").select2({
100 $("#id_search_type").select2({
101 'containerCssClass': "drop-menu",
101 'containerCssClass': "drop-menu",
102 'dropdownCssClass': "drop-menu-dropdown",
102 'dropdownCssClass': "drop-menu-dropdown",
103 'dropdownAutoWidth': true,
103 'dropdownAutoWidth': true,
104 'minimumResultsForSearch': -1
104 'minimumResultsForSearch': -1
105 });
105 });
106 })
106 })
107 </script>
107 </script>
108 </%def>
108 </%def>
@@ -1,101 +1,101 b''
1 <%def name="highlight_text_file(terms, text, url, line_context=3,
1 <%def name="highlight_text_file(terms, text, url, line_context=3,
2 max_lines=10,
2 max_lines=10,
3 mimetype=None, filepath=None)">
3 mimetype=None, filepath=None)">
4 <%
4 <%
5 lines = text.split('\n')
5 lines = text.split('\n')
6 lines_of_interest = set()
6 lines_of_interest = set()
7 matching_lines = h.get_matching_line_offsets(lines, terms)
7 matching_lines = h.get_matching_line_offsets(lines, terms)
8 shown_matching_lines = 0
8 shown_matching_lines = 0
9
9
10 for line_number in matching_lines:
10 for line_number in matching_lines:
11 if len(lines_of_interest) < max_lines:
11 if len(lines_of_interest) < max_lines:
12 lines_of_interest |= set(range(
12 lines_of_interest |= set(range(
13 max(line_number - line_context, 0),
13 max(line_number - line_context, 0),
14 min(line_number + line_context, len(lines) + 1)))
14 min(line_number + line_context, len(lines) + 1)))
15 shown_matching_lines += 1
15 shown_matching_lines += 1
16
16
17 %>
17 %>
18 ${h.code_highlight(
18 ${h.code_highlight(
19 text,
19 text,
20 h.get_lexer_safe(
20 h.get_lexer_safe(
21 mimetype=mimetype,
21 mimetype=mimetype,
22 filepath=filepath,
22 filepath=filepath,
23 ),
23 ),
24 h.SearchContentCodeHtmlFormatter(
24 h.SearchContentCodeHtmlFormatter(
25 linenos=True,
25 linenos=True,
26 cssclass="code-highlight",
26 cssclass="code-highlight",
27 url=url,
27 url=url,
28 query_terms=terms,
28 query_terms=terms,
29 only_line_numbers=lines_of_interest
29 only_line_numbers=lines_of_interest
30 ))|n}
30 ))|n}
31 %if len(matching_lines) > shown_matching_lines:
31 %if len(matching_lines) > shown_matching_lines:
32 <a href="${url}">
32 <a href="${url}">
33 ${len(matching_lines) - shown_matching_lines} ${_('more matches in this file')}
33 ${len(matching_lines) - shown_matching_lines} ${_('more matches in this file')}
34 </a>
34 </a>
35 %endif
35 %endif
36 </%def>
36 </%def>
37
37
38 <div class="search-results">
38 <div class="search-results">
39 %for entry in c.formatted_results:
39 %for entry in c.formatted_results:
40 ## search results are additionally filtered, and this check is just a safe gate
40 ## search results are additionally filtered, and this check is just a safe gate
41 % if h.HasRepoPermissionAny('repository.write','repository.read','repository.admin')(entry['repository'], 'search results content check'):
41 % if h.HasRepoPermissionAny('repository.write','repository.read','repository.admin')(entry['repository'], 'search results content check'):
42 <div id="codeblock" class="codeblock">
42 <div id="codeblock" class="codeblock">
43 <div class="codeblock-header">
43 <div class="codeblock-header">
44 <h2>
44 <h2>
45 %if h.get_repo_type_by_name(entry.get('repository')) == 'hg':
45 %if h.get_repo_type_by_name(entry.get('repository')) == 'hg':
46 <i class="icon-hg"></i>
46 <i class="icon-hg"></i>
47 %elif h.get_repo_type_by_name(entry.get('repository')) == 'git':
47 %elif h.get_repo_type_by_name(entry.get('repository')) == 'git':
48 <i class="icon-git"></i>
48 <i class="icon-git"></i>
49 %elif h.get_repo_type_by_name(entry.get('repository')) == 'svn':
49 %elif h.get_repo_type_by_name(entry.get('repository')) == 'svn':
50 <i class="icon-svn"></i>
50 <i class="icon-svn"></i>
51 %endif
51 %endif
52 ${h.link_to(entry['repository'], h.url('summary_home',repo_name=entry['repository']))}
52 ${h.link_to(entry['repository'], h.url('summary_home',repo_name=entry['repository']))}
53 </h2>
53 </h2>
54 <div class="stats">
54 <div class="stats">
55 ${h.link_to(h.literal(entry['f_path']), h.url('files_home',repo_name=entry['repository'],revision=entry.get('commit_id', 'tip'),f_path=entry['f_path']))}
55 ${h.link_to(h.literal(entry['f_path']), h.url('files_home',repo_name=entry['repository'],revision=entry.get('commit_id', 'tip'),f_path=entry['f_path']))}
56 %if entry.get('lines'):
56 %if entry.get('lines'):
57 | ${entry.get('lines', 0.)} ${ungettext('line', 'lines', entry.get('lines', 0.))}
57 | ${entry.get('lines', 0.)} ${_ungettext('line', 'lines', entry.get('lines', 0.))}
58 %endif
58 %endif
59 %if entry.get('size'):
59 %if entry.get('size'):
60 | ${h.format_byte_size_binary(entry['size'])}
60 | ${h.format_byte_size_binary(entry['size'])}
61 %endif
61 %endif
62 %if entry.get('mimetype'):
62 %if entry.get('mimetype'):
63 | ${entry.get('mimetype', "unknown mimetype")}
63 | ${entry.get('mimetype', "unknown mimetype")}
64 %endif
64 %endif
65 </div>
65 </div>
66 <div class="buttons">
66 <div class="buttons">
67 <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',''))}">
67 <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 ${_('Show Full History')}
68 ${_('Show Full History')}
69 </a> |
69 </a> |
70 ${h.link_to(_('Annotation'), h.url('files_annotate_home', repo_name=entry.get('repository',''),revision=entry.get('commit_id', 'tip'),f_path=entry.get('f_path','')))}
70 ${h.link_to(_('Annotation'), h.url('files_annotate_home', repo_name=entry.get('repository',''),revision=entry.get('commit_id', 'tip'),f_path=entry.get('f_path','')))}
71 | ${h.link_to(_('Raw'), h.url('files_raw_home', repo_name=entry.get('repository',''),revision=entry.get('commit_id', 'tip'),f_path=entry.get('f_path','')))}
71 | ${h.link_to(_('Raw'), h.url('files_raw_home', repo_name=entry.get('repository',''),revision=entry.get('commit_id', 'tip'),f_path=entry.get('f_path','')))}
72 | <a href="${h.url('files_rawfile_home',repo_name=entry.get('repository',''),revision=entry.get('commit_id', 'tip'),f_path=entry.get('f_path',''))}">
72 | <a href="${h.url('files_rawfile_home',repo_name=entry.get('repository',''),revision=entry.get('commit_id', 'tip'),f_path=entry.get('f_path',''))}">
73 ${_('Download')}
73 ${_('Download')}
74 </a>
74 </a>
75 </div>
75 </div>
76 </div>
76 </div>
77 <div class="code-body search-code-body">
77 <div class="code-body search-code-body">
78 ${highlight_text_file(c.cur_query, entry['content'],
78 ${highlight_text_file(c.cur_query, entry['content'],
79 url=h.url('files_home',repo_name=entry['repository'],revision=entry.get('commit_id', 'tip'),f_path=entry['f_path']),
79 url=h.url('files_home',repo_name=entry['repository'],revision=entry.get('commit_id', 'tip'),f_path=entry['f_path']),
80 mimetype=entry.get('mimetype'), filepath=entry.get('path'))}
80 mimetype=entry.get('mimetype'), filepath=entry.get('path'))}
81 </div>
81 </div>
82 </div>
82 </div>
83 % endif
83 % endif
84 %endfor
84 %endfor
85 </div>
85 </div>
86 %if c.cur_query and c.formatted_results:
86 %if c.cur_query and c.formatted_results:
87 <div class="pagination-wh pagination-left" >
87 <div class="pagination-wh pagination-left" >
88 ${c.formatted_results.pager('$link_previous ~2~ $link_next')}
88 ${c.formatted_results.pager('$link_previous ~2~ $link_next')}
89 </div>
89 </div>
90 %endif
90 %endif
91
91
92 %if c.cur_query:
92 %if c.cur_query:
93 <script type="text/javascript">
93 <script type="text/javascript">
94 $(function(){
94 $(function(){
95 $(".code").mark(
95 $(".code").mark(
96 '${' '.join(h.normalize_text_for_matching(c.cur_query).split())}',
96 '${' '.join(h.normalize_text_for_matching(c.cur_query).split())}',
97 {"className": 'match',
97 {"className": 'match',
98 });
98 });
99 })
99 })
100 </script>
100 </script>
101 %endif No newline at end of file
101 %endif
1 NO CONTENT: file renamed from rhodecode/tests/controllers/test_search.py to rhodecode/tests/lib/test_search.py
NO CONTENT: file renamed from rhodecode/tests/controllers/test_search.py to rhodecode/tests/lib/test_search.py
1 NO CONTENT: file was removed
NO CONTENT: file was removed
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