##// END OF EJS Templates
repositories: preserve order of defined backends, and switched repo type selector to radios.
marcink -
r4321:711124f9 default
parent child Browse files
Show More
@@ -1,57 +1,60 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2020 RhodeCode GmbH
3 # Copyright (C) 2010-2020 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 import os
21 import os
22 from collections import OrderedDict
23
22 import sys
24 import sys
23 import platform
25 import platform
24
26
25 VERSION = tuple(open(os.path.join(
27 VERSION = tuple(open(os.path.join(
26 os.path.dirname(__file__), 'VERSION')).read().split('.'))
28 os.path.dirname(__file__), 'VERSION')).read().split('.'))
27
29
28 BACKENDS = {
30 BACKENDS = OrderedDict()
29 'hg': 'Mercurial repository',
31
30 'git': 'Git repository',
32 BACKENDS['hg'] = 'Mercurial repository'
31 'svn': 'Subversion repository',
33 BACKENDS['git'] = 'Git repository'
32 }
34 BACKENDS['svn'] = 'Subversion repository'
35
33
36
34 CELERY_ENABLED = False
37 CELERY_ENABLED = False
35 CELERY_EAGER = False
38 CELERY_EAGER = False
36
39
37 # link to config for pyramid
40 # link to config for pyramid
38 CONFIG = {}
41 CONFIG = {}
39
42
40 # Populated with the settings dictionary from application init in
43 # Populated with the settings dictionary from application init in
41 # rhodecode.conf.environment.load_pyramid_environment
44 # rhodecode.conf.environment.load_pyramid_environment
42 PYRAMID_SETTINGS = {}
45 PYRAMID_SETTINGS = {}
43
46
44 # Linked module for extensions
47 # Linked module for extensions
45 EXTENSIONS = {}
48 EXTENSIONS = {}
46
49
47 __version__ = ('.'.join((str(each) for each in VERSION[:3])))
50 __version__ = ('.'.join((str(each) for each in VERSION[:3])))
48 __dbversion__ = 105 # defines current db version for migrations
51 __dbversion__ = 105 # defines current db version for migrations
49 __platform__ = platform.system()
52 __platform__ = platform.system()
50 __license__ = 'AGPLv3, and Commercial License'
53 __license__ = 'AGPLv3, and Commercial License'
51 __author__ = 'RhodeCode GmbH'
54 __author__ = 'RhodeCode GmbH'
52 __url__ = 'https://code.rhodecode.com'
55 __url__ = 'https://code.rhodecode.com'
53
56
54 is_windows = __platform__ in ['Windows']
57 is_windows = __platform__ in ['Windows']
55 is_unix = not is_windows
58 is_unix = not is_windows
56 is_test = False
59 is_test = False
57 disable_error_handler = False
60 disable_error_handler = False
@@ -1,81 +1,86 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2020 RhodeCode GmbH
3 # Copyright (C) 2010-2020 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 import os
21 import os
22 import logging
22 import logging
23 import rhodecode
23 import rhodecode
24
24
25 from rhodecode.config import utils
25 from rhodecode.config import utils
26
26
27 from rhodecode.lib.utils import load_rcextensions
27 from rhodecode.lib.utils import load_rcextensions
28 from rhodecode.lib.utils2 import str2bool
28 from rhodecode.lib.utils2 import str2bool
29 from rhodecode.lib.vcs import connect_vcs
29 from rhodecode.lib.vcs import connect_vcs
30
30
31 log = logging.getLogger(__name__)
31 log = logging.getLogger(__name__)
32
32
33
33
34 def load_pyramid_environment(global_config, settings):
34 def load_pyramid_environment(global_config, settings):
35 # Some parts of the code expect a merge of global and app settings.
35 # Some parts of the code expect a merge of global and app settings.
36 settings_merged = global_config.copy()
36 settings_merged = global_config.copy()
37 settings_merged.update(settings)
37 settings_merged.update(settings)
38
38
39 # TODO(marcink): probably not required anymore
39 # TODO(marcink): probably not required anymore
40 # configure channelstream,
40 # configure channelstream,
41 settings_merged['channelstream_config'] = {
41 settings_merged['channelstream_config'] = {
42 'enabled': str2bool(settings_merged.get('channelstream.enabled', False)),
42 'enabled': str2bool(settings_merged.get('channelstream.enabled', False)),
43 'server': settings_merged.get('channelstream.server'),
43 'server': settings_merged.get('channelstream.server'),
44 'secret': settings_merged.get('channelstream.secret')
44 'secret': settings_merged.get('channelstream.secret')
45 }
45 }
46
46
47 # If this is a test run we prepare the test environment like
47 # If this is a test run we prepare the test environment like
48 # creating a test database, test search index and test repositories.
48 # creating a test database, test search index and test repositories.
49 # This has to be done before the database connection is initialized.
49 # This has to be done before the database connection is initialized.
50 if settings['is_test']:
50 if settings['is_test']:
51 rhodecode.is_test = True
51 rhodecode.is_test = True
52 rhodecode.disable_error_handler = True
52 rhodecode.disable_error_handler = True
53 from rhodecode import authentication
53 from rhodecode import authentication
54 authentication.plugin_default_auth_ttl = 0
54 authentication.plugin_default_auth_ttl = 0
55
55
56 utils.initialize_test_environment(settings_merged)
56 utils.initialize_test_environment(settings_merged)
57
57
58 # Initialize the database connection.
58 # Initialize the database connection.
59 utils.initialize_database(settings_merged)
59 utils.initialize_database(settings_merged)
60
60
61 load_rcextensions(root_path=settings_merged['here'])
61 load_rcextensions(root_path=settings_merged['here'])
62
62
63 # Limit backends to `vcs.backends` from configuration
63 # Limit backends to `vcs.backends` from configuration, and preserve the order
64 for alias in rhodecode.BACKENDS.keys():
64 for alias in rhodecode.BACKENDS.keys():
65 if alias not in settings['vcs.backends']:
65 if alias not in settings['vcs.backends']:
66 del rhodecode.BACKENDS[alias]
66 del rhodecode.BACKENDS[alias]
67
68 def sorter(item):
69 return settings['vcs.backends'].index(item[0])
70 rhodecode.BACKENDS = rhodecode.OrderedDict(sorted(rhodecode.BACKENDS.items(), key=sorter))
71
67 log.info('Enabled VCS backends: %s', rhodecode.BACKENDS.keys())
72 log.info('Enabled VCS backends: %s', rhodecode.BACKENDS.keys())
68
73
69 # initialize vcs client and optionally run the server if enabled
74 # initialize vcs client and optionally run the server if enabled
70 vcs_server_uri = settings['vcs.server']
75 vcs_server_uri = settings['vcs.server']
71 vcs_server_enabled = settings['vcs.server.enable']
76 vcs_server_enabled = settings['vcs.server.enable']
72
77
73 utils.configure_vcs(settings)
78 utils.configure_vcs(settings)
74
79
75 # Store the settings to make them available to other modules.
80 # Store the settings to make them available to other modules.
76
81
77 rhodecode.PYRAMID_SETTINGS = settings_merged
82 rhodecode.PYRAMID_SETTINGS = settings_merged
78 rhodecode.CONFIG = settings_merged
83 rhodecode.CONFIG = settings_merged
79
84
80 if vcs_server_enabled:
85 if vcs_server_enabled:
81 connect_vcs(vcs_server_uri, utils.get_vcs_server_protocol(settings))
86 connect_vcs(vcs_server_uri, utils.get_vcs_server_protocol(settings))
@@ -1,617 +1,617 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2020 RhodeCode GmbH
3 # Copyright (C) 2010-2020 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 The base Controller API
22 The base Controller API
23 Provides the BaseController class for subclassing. And usage in different
23 Provides the BaseController class for subclassing. And usage in different
24 controllers
24 controllers
25 """
25 """
26
26
27 import logging
27 import logging
28 import socket
28 import socket
29
29
30 import markupsafe
30 import markupsafe
31 import ipaddress
31 import ipaddress
32
32
33 from paste.auth.basic import AuthBasicAuthenticator
33 from paste.auth.basic import AuthBasicAuthenticator
34 from paste.httpexceptions import HTTPUnauthorized, HTTPForbidden, get_exception
34 from paste.httpexceptions import HTTPUnauthorized, HTTPForbidden, get_exception
35 from paste.httpheaders import WWW_AUTHENTICATE, AUTHORIZATION
35 from paste.httpheaders import WWW_AUTHENTICATE, AUTHORIZATION
36
36
37 import rhodecode
37 import rhodecode
38 from rhodecode.apps._base import TemplateArgs
38 from rhodecode.apps._base import TemplateArgs
39 from rhodecode.authentication.base import VCS_TYPE
39 from rhodecode.authentication.base import VCS_TYPE
40 from rhodecode.lib import auth, utils2
40 from rhodecode.lib import auth, utils2
41 from rhodecode.lib import helpers as h
41 from rhodecode.lib import helpers as h
42 from rhodecode.lib.auth import AuthUser, CookieStoreWrapper
42 from rhodecode.lib.auth import AuthUser, CookieStoreWrapper
43 from rhodecode.lib.exceptions import UserCreationError
43 from rhodecode.lib.exceptions import UserCreationError
44 from rhodecode.lib.utils import (password_changed, get_enabled_hook_classes)
44 from rhodecode.lib.utils import (password_changed, get_enabled_hook_classes)
45 from rhodecode.lib.utils2 import (
45 from rhodecode.lib.utils2 import (
46 str2bool, safe_unicode, AttributeDict, safe_int, sha1, aslist, safe_str)
46 str2bool, safe_unicode, AttributeDict, safe_int, sha1, aslist, safe_str)
47 from rhodecode.model.db import Repository, User, ChangesetComment, UserBookmark
47 from rhodecode.model.db import Repository, User, ChangesetComment, UserBookmark
48 from rhodecode.model.notification import NotificationModel
48 from rhodecode.model.notification import NotificationModel
49 from rhodecode.model.settings import VcsSettingsModel, SettingsModel
49 from rhodecode.model.settings import VcsSettingsModel, SettingsModel
50
50
51 log = logging.getLogger(__name__)
51 log = logging.getLogger(__name__)
52
52
53
53
54 def _filter_proxy(ip):
54 def _filter_proxy(ip):
55 """
55 """
56 Passed in IP addresses in HEADERS can be in a special format of multiple
56 Passed in IP addresses in HEADERS can be in a special format of multiple
57 ips. Those comma separated IPs are passed from various proxies in the
57 ips. Those comma separated IPs are passed from various proxies in the
58 chain of request processing. The left-most being the original client.
58 chain of request processing. The left-most being the original client.
59 We only care about the first IP which came from the org. client.
59 We only care about the first IP which came from the org. client.
60
60
61 :param ip: ip string from headers
61 :param ip: ip string from headers
62 """
62 """
63 if ',' in ip:
63 if ',' in ip:
64 _ips = ip.split(',')
64 _ips = ip.split(',')
65 _first_ip = _ips[0].strip()
65 _first_ip = _ips[0].strip()
66 log.debug('Got multiple IPs %s, using %s', ','.join(_ips), _first_ip)
66 log.debug('Got multiple IPs %s, using %s', ','.join(_ips), _first_ip)
67 return _first_ip
67 return _first_ip
68 return ip
68 return ip
69
69
70
70
71 def _filter_port(ip):
71 def _filter_port(ip):
72 """
72 """
73 Removes a port from ip, there are 4 main cases to handle here.
73 Removes a port from ip, there are 4 main cases to handle here.
74 - ipv4 eg. 127.0.0.1
74 - ipv4 eg. 127.0.0.1
75 - ipv6 eg. ::1
75 - ipv6 eg. ::1
76 - ipv4+port eg. 127.0.0.1:8080
76 - ipv4+port eg. 127.0.0.1:8080
77 - ipv6+port eg. [::1]:8080
77 - ipv6+port eg. [::1]:8080
78
78
79 :param ip:
79 :param ip:
80 """
80 """
81 def is_ipv6(ip_addr):
81 def is_ipv6(ip_addr):
82 if hasattr(socket, 'inet_pton'):
82 if hasattr(socket, 'inet_pton'):
83 try:
83 try:
84 socket.inet_pton(socket.AF_INET6, ip_addr)
84 socket.inet_pton(socket.AF_INET6, ip_addr)
85 except socket.error:
85 except socket.error:
86 return False
86 return False
87 else:
87 else:
88 # fallback to ipaddress
88 # fallback to ipaddress
89 try:
89 try:
90 ipaddress.IPv6Address(safe_unicode(ip_addr))
90 ipaddress.IPv6Address(safe_unicode(ip_addr))
91 except Exception:
91 except Exception:
92 return False
92 return False
93 return True
93 return True
94
94
95 if ':' not in ip: # must be ipv4 pure ip
95 if ':' not in ip: # must be ipv4 pure ip
96 return ip
96 return ip
97
97
98 if '[' in ip and ']' in ip: # ipv6 with port
98 if '[' in ip and ']' in ip: # ipv6 with port
99 return ip.split(']')[0][1:].lower()
99 return ip.split(']')[0][1:].lower()
100
100
101 # must be ipv6 or ipv4 with port
101 # must be ipv6 or ipv4 with port
102 if is_ipv6(ip):
102 if is_ipv6(ip):
103 return ip
103 return ip
104 else:
104 else:
105 ip, _port = ip.split(':')[:2] # means ipv4+port
105 ip, _port = ip.split(':')[:2] # means ipv4+port
106 return ip
106 return ip
107
107
108
108
109 def get_ip_addr(environ):
109 def get_ip_addr(environ):
110 proxy_key = 'HTTP_X_REAL_IP'
110 proxy_key = 'HTTP_X_REAL_IP'
111 proxy_key2 = 'HTTP_X_FORWARDED_FOR'
111 proxy_key2 = 'HTTP_X_FORWARDED_FOR'
112 def_key = 'REMOTE_ADDR'
112 def_key = 'REMOTE_ADDR'
113 _filters = lambda x: _filter_port(_filter_proxy(x))
113 _filters = lambda x: _filter_port(_filter_proxy(x))
114
114
115 ip = environ.get(proxy_key)
115 ip = environ.get(proxy_key)
116 if ip:
116 if ip:
117 return _filters(ip)
117 return _filters(ip)
118
118
119 ip = environ.get(proxy_key2)
119 ip = environ.get(proxy_key2)
120 if ip:
120 if ip:
121 return _filters(ip)
121 return _filters(ip)
122
122
123 ip = environ.get(def_key, '0.0.0.0')
123 ip = environ.get(def_key, '0.0.0.0')
124 return _filters(ip)
124 return _filters(ip)
125
125
126
126
127 def get_server_ip_addr(environ, log_errors=True):
127 def get_server_ip_addr(environ, log_errors=True):
128 hostname = environ.get('SERVER_NAME')
128 hostname = environ.get('SERVER_NAME')
129 try:
129 try:
130 return socket.gethostbyname(hostname)
130 return socket.gethostbyname(hostname)
131 except Exception as e:
131 except Exception as e:
132 if log_errors:
132 if log_errors:
133 # in some cases this lookup is not possible, and we don't want to
133 # in some cases this lookup is not possible, and we don't want to
134 # make it an exception in logs
134 # make it an exception in logs
135 log.exception('Could not retrieve server ip address: %s', e)
135 log.exception('Could not retrieve server ip address: %s', e)
136 return hostname
136 return hostname
137
137
138
138
139 def get_server_port(environ):
139 def get_server_port(environ):
140 return environ.get('SERVER_PORT')
140 return environ.get('SERVER_PORT')
141
141
142
142
143 def get_access_path(environ):
143 def get_access_path(environ):
144 path = environ.get('PATH_INFO')
144 path = environ.get('PATH_INFO')
145 org_req = environ.get('pylons.original_request')
145 org_req = environ.get('pylons.original_request')
146 if org_req:
146 if org_req:
147 path = org_req.environ.get('PATH_INFO')
147 path = org_req.environ.get('PATH_INFO')
148 return path
148 return path
149
149
150
150
151 def get_user_agent(environ):
151 def get_user_agent(environ):
152 return environ.get('HTTP_USER_AGENT')
152 return environ.get('HTTP_USER_AGENT')
153
153
154
154
155 def vcs_operation_context(
155 def vcs_operation_context(
156 environ, repo_name, username, action, scm, check_locking=True,
156 environ, repo_name, username, action, scm, check_locking=True,
157 is_shadow_repo=False, check_branch_perms=False, detect_force_push=False):
157 is_shadow_repo=False, check_branch_perms=False, detect_force_push=False):
158 """
158 """
159 Generate the context for a vcs operation, e.g. push or pull.
159 Generate the context for a vcs operation, e.g. push or pull.
160
160
161 This context is passed over the layers so that hooks triggered by the
161 This context is passed over the layers so that hooks triggered by the
162 vcs operation know details like the user, the user's IP address etc.
162 vcs operation know details like the user, the user's IP address etc.
163
163
164 :param check_locking: Allows to switch of the computation of the locking
164 :param check_locking: Allows to switch of the computation of the locking
165 data. This serves mainly the need of the simplevcs middleware to be
165 data. This serves mainly the need of the simplevcs middleware to be
166 able to disable this for certain operations.
166 able to disable this for certain operations.
167
167
168 """
168 """
169 # Tri-state value: False: unlock, None: nothing, True: lock
169 # Tri-state value: False: unlock, None: nothing, True: lock
170 make_lock = None
170 make_lock = None
171 locked_by = [None, None, None]
171 locked_by = [None, None, None]
172 is_anonymous = username == User.DEFAULT_USER
172 is_anonymous = username == User.DEFAULT_USER
173 user = User.get_by_username(username)
173 user = User.get_by_username(username)
174 if not is_anonymous and check_locking:
174 if not is_anonymous and check_locking:
175 log.debug('Checking locking on repository "%s"', repo_name)
175 log.debug('Checking locking on repository "%s"', repo_name)
176 repo = Repository.get_by_repo_name(repo_name)
176 repo = Repository.get_by_repo_name(repo_name)
177 make_lock, __, locked_by = repo.get_locking_state(
177 make_lock, __, locked_by = repo.get_locking_state(
178 action, user.user_id)
178 action, user.user_id)
179 user_id = user.user_id
179 user_id = user.user_id
180 settings_model = VcsSettingsModel(repo=repo_name)
180 settings_model = VcsSettingsModel(repo=repo_name)
181 ui_settings = settings_model.get_ui_settings()
181 ui_settings = settings_model.get_ui_settings()
182
182
183 # NOTE(marcink): This should be also in sync with
183 # NOTE(marcink): This should be also in sync with
184 # rhodecode/apps/ssh_support/lib/backends/base.py:update_environment scm_data
184 # rhodecode/apps/ssh_support/lib/backends/base.py:update_environment scm_data
185 store = [x for x in ui_settings if x.key == '/']
185 store = [x for x in ui_settings if x.key == '/']
186 repo_store = ''
186 repo_store = ''
187 if store:
187 if store:
188 repo_store = store[0].value
188 repo_store = store[0].value
189
189
190 scm_data = {
190 scm_data = {
191 'ip': get_ip_addr(environ),
191 'ip': get_ip_addr(environ),
192 'username': username,
192 'username': username,
193 'user_id': user_id,
193 'user_id': user_id,
194 'action': action,
194 'action': action,
195 'repository': repo_name,
195 'repository': repo_name,
196 'scm': scm,
196 'scm': scm,
197 'config': rhodecode.CONFIG['__file__'],
197 'config': rhodecode.CONFIG['__file__'],
198 'repo_store': repo_store,
198 'repo_store': repo_store,
199 'make_lock': make_lock,
199 'make_lock': make_lock,
200 'locked_by': locked_by,
200 'locked_by': locked_by,
201 'server_url': utils2.get_server_url(environ),
201 'server_url': utils2.get_server_url(environ),
202 'user_agent': get_user_agent(environ),
202 'user_agent': get_user_agent(environ),
203 'hooks': get_enabled_hook_classes(ui_settings),
203 'hooks': get_enabled_hook_classes(ui_settings),
204 'is_shadow_repo': is_shadow_repo,
204 'is_shadow_repo': is_shadow_repo,
205 'detect_force_push': detect_force_push,
205 'detect_force_push': detect_force_push,
206 'check_branch_perms': check_branch_perms,
206 'check_branch_perms': check_branch_perms,
207 }
207 }
208 return scm_data
208 return scm_data
209
209
210
210
211 class BasicAuth(AuthBasicAuthenticator):
211 class BasicAuth(AuthBasicAuthenticator):
212
212
213 def __init__(self, realm, authfunc, registry, auth_http_code=None,
213 def __init__(self, realm, authfunc, registry, auth_http_code=None,
214 initial_call_detection=False, acl_repo_name=None, rc_realm=''):
214 initial_call_detection=False, acl_repo_name=None, rc_realm=''):
215 self.realm = realm
215 self.realm = realm
216 self.rc_realm = rc_realm
216 self.rc_realm = rc_realm
217 self.initial_call = initial_call_detection
217 self.initial_call = initial_call_detection
218 self.authfunc = authfunc
218 self.authfunc = authfunc
219 self.registry = registry
219 self.registry = registry
220 self.acl_repo_name = acl_repo_name
220 self.acl_repo_name = acl_repo_name
221 self._rc_auth_http_code = auth_http_code
221 self._rc_auth_http_code = auth_http_code
222
222
223 def _get_response_from_code(self, http_code):
223 def _get_response_from_code(self, http_code):
224 try:
224 try:
225 return get_exception(safe_int(http_code))
225 return get_exception(safe_int(http_code))
226 except Exception:
226 except Exception:
227 log.exception('Failed to fetch response for code %s', http_code)
227 log.exception('Failed to fetch response for code %s', http_code)
228 return HTTPForbidden
228 return HTTPForbidden
229
229
230 def get_rc_realm(self):
230 def get_rc_realm(self):
231 return safe_str(self.rc_realm)
231 return safe_str(self.rc_realm)
232
232
233 def build_authentication(self):
233 def build_authentication(self):
234 head = WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm)
234 head = WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm)
235 if self._rc_auth_http_code and not self.initial_call:
235 if self._rc_auth_http_code and not self.initial_call:
236 # return alternative HTTP code if alternative http return code
236 # return alternative HTTP code if alternative http return code
237 # is specified in RhodeCode config, but ONLY if it's not the
237 # is specified in RhodeCode config, but ONLY if it's not the
238 # FIRST call
238 # FIRST call
239 custom_response_klass = self._get_response_from_code(
239 custom_response_klass = self._get_response_from_code(
240 self._rc_auth_http_code)
240 self._rc_auth_http_code)
241 return custom_response_klass(headers=head)
241 return custom_response_klass(headers=head)
242 return HTTPUnauthorized(headers=head)
242 return HTTPUnauthorized(headers=head)
243
243
244 def authenticate(self, environ):
244 def authenticate(self, environ):
245 authorization = AUTHORIZATION(environ)
245 authorization = AUTHORIZATION(environ)
246 if not authorization:
246 if not authorization:
247 return self.build_authentication()
247 return self.build_authentication()
248 (authmeth, auth) = authorization.split(' ', 1)
248 (authmeth, auth) = authorization.split(' ', 1)
249 if 'basic' != authmeth.lower():
249 if 'basic' != authmeth.lower():
250 return self.build_authentication()
250 return self.build_authentication()
251 auth = auth.strip().decode('base64')
251 auth = auth.strip().decode('base64')
252 _parts = auth.split(':', 1)
252 _parts = auth.split(':', 1)
253 if len(_parts) == 2:
253 if len(_parts) == 2:
254 username, password = _parts
254 username, password = _parts
255 auth_data = self.authfunc(
255 auth_data = self.authfunc(
256 username, password, environ, VCS_TYPE,
256 username, password, environ, VCS_TYPE,
257 registry=self.registry, acl_repo_name=self.acl_repo_name)
257 registry=self.registry, acl_repo_name=self.acl_repo_name)
258 if auth_data:
258 if auth_data:
259 return {'username': username, 'auth_data': auth_data}
259 return {'username': username, 'auth_data': auth_data}
260 if username and password:
260 if username and password:
261 # we mark that we actually executed authentication once, at
261 # we mark that we actually executed authentication once, at
262 # that point we can use the alternative auth code
262 # that point we can use the alternative auth code
263 self.initial_call = False
263 self.initial_call = False
264
264
265 return self.build_authentication()
265 return self.build_authentication()
266
266
267 __call__ = authenticate
267 __call__ = authenticate
268
268
269
269
270 def calculate_version_hash(config):
270 def calculate_version_hash(config):
271 return sha1(
271 return sha1(
272 config.get('beaker.session.secret', '') +
272 config.get('beaker.session.secret', '') +
273 rhodecode.__version__)[:8]
273 rhodecode.__version__)[:8]
274
274
275
275
276 def get_current_lang(request):
276 def get_current_lang(request):
277 # NOTE(marcink): remove after pyramid move
277 # NOTE(marcink): remove after pyramid move
278 try:
278 try:
279 return translation.get_lang()[0]
279 return translation.get_lang()[0]
280 except:
280 except:
281 pass
281 pass
282
282
283 return getattr(request, '_LOCALE_', request.locale_name)
283 return getattr(request, '_LOCALE_', request.locale_name)
284
284
285
285
286 def attach_context_attributes(context, request, user_id=None, is_api=None):
286 def attach_context_attributes(context, request, user_id=None, is_api=None):
287 """
287 """
288 Attach variables into template context called `c`.
288 Attach variables into template context called `c`.
289 """
289 """
290 config = request.registry.settings
290 config = request.registry.settings
291
291
292 rc_config = SettingsModel().get_all_settings(cache=True, from_request=False)
292 rc_config = SettingsModel().get_all_settings(cache=True, from_request=False)
293 context.rc_config = rc_config
293 context.rc_config = rc_config
294 context.rhodecode_version = rhodecode.__version__
294 context.rhodecode_version = rhodecode.__version__
295 context.rhodecode_edition = config.get('rhodecode.edition')
295 context.rhodecode_edition = config.get('rhodecode.edition')
296 # unique secret + version does not leak the version but keep consistency
296 # unique secret + version does not leak the version but keep consistency
297 context.rhodecode_version_hash = calculate_version_hash(config)
297 context.rhodecode_version_hash = calculate_version_hash(config)
298
298
299 # Default language set for the incoming request
299 # Default language set for the incoming request
300 context.language = get_current_lang(request)
300 context.language = get_current_lang(request)
301
301
302 # Visual options
302 # Visual options
303 context.visual = AttributeDict({})
303 context.visual = AttributeDict({})
304
304
305 # DB stored Visual Items
305 # DB stored Visual Items
306 context.visual.show_public_icon = str2bool(
306 context.visual.show_public_icon = str2bool(
307 rc_config.get('rhodecode_show_public_icon'))
307 rc_config.get('rhodecode_show_public_icon'))
308 context.visual.show_private_icon = str2bool(
308 context.visual.show_private_icon = str2bool(
309 rc_config.get('rhodecode_show_private_icon'))
309 rc_config.get('rhodecode_show_private_icon'))
310 context.visual.stylify_metatags = str2bool(
310 context.visual.stylify_metatags = str2bool(
311 rc_config.get('rhodecode_stylify_metatags'))
311 rc_config.get('rhodecode_stylify_metatags'))
312 context.visual.dashboard_items = safe_int(
312 context.visual.dashboard_items = safe_int(
313 rc_config.get('rhodecode_dashboard_items', 100))
313 rc_config.get('rhodecode_dashboard_items', 100))
314 context.visual.admin_grid_items = safe_int(
314 context.visual.admin_grid_items = safe_int(
315 rc_config.get('rhodecode_admin_grid_items', 100))
315 rc_config.get('rhodecode_admin_grid_items', 100))
316 context.visual.show_revision_number = str2bool(
316 context.visual.show_revision_number = str2bool(
317 rc_config.get('rhodecode_show_revision_number', True))
317 rc_config.get('rhodecode_show_revision_number', True))
318 context.visual.show_sha_length = safe_int(
318 context.visual.show_sha_length = safe_int(
319 rc_config.get('rhodecode_show_sha_length', 100))
319 rc_config.get('rhodecode_show_sha_length', 100))
320 context.visual.repository_fields = str2bool(
320 context.visual.repository_fields = str2bool(
321 rc_config.get('rhodecode_repository_fields'))
321 rc_config.get('rhodecode_repository_fields'))
322 context.visual.show_version = str2bool(
322 context.visual.show_version = str2bool(
323 rc_config.get('rhodecode_show_version'))
323 rc_config.get('rhodecode_show_version'))
324 context.visual.use_gravatar = str2bool(
324 context.visual.use_gravatar = str2bool(
325 rc_config.get('rhodecode_use_gravatar'))
325 rc_config.get('rhodecode_use_gravatar'))
326 context.visual.gravatar_url = rc_config.get('rhodecode_gravatar_url')
326 context.visual.gravatar_url = rc_config.get('rhodecode_gravatar_url')
327 context.visual.default_renderer = rc_config.get(
327 context.visual.default_renderer = rc_config.get(
328 'rhodecode_markup_renderer', 'rst')
328 'rhodecode_markup_renderer', 'rst')
329 context.visual.comment_types = ChangesetComment.COMMENT_TYPES
329 context.visual.comment_types = ChangesetComment.COMMENT_TYPES
330 context.visual.rhodecode_support_url = \
330 context.visual.rhodecode_support_url = \
331 rc_config.get('rhodecode_support_url') or h.route_url('rhodecode_support')
331 rc_config.get('rhodecode_support_url') or h.route_url('rhodecode_support')
332
332
333 context.visual.affected_files_cut_off = 60
333 context.visual.affected_files_cut_off = 60
334
334
335 context.pre_code = rc_config.get('rhodecode_pre_code')
335 context.pre_code = rc_config.get('rhodecode_pre_code')
336 context.post_code = rc_config.get('rhodecode_post_code')
336 context.post_code = rc_config.get('rhodecode_post_code')
337 context.rhodecode_name = rc_config.get('rhodecode_title')
337 context.rhodecode_name = rc_config.get('rhodecode_title')
338 context.default_encodings = aslist(config.get('default_encoding'), sep=',')
338 context.default_encodings = aslist(config.get('default_encoding'), sep=',')
339 # if we have specified default_encoding in the request, it has more
339 # if we have specified default_encoding in the request, it has more
340 # priority
340 # priority
341 if request.GET.get('default_encoding'):
341 if request.GET.get('default_encoding'):
342 context.default_encodings.insert(0, request.GET.get('default_encoding'))
342 context.default_encodings.insert(0, request.GET.get('default_encoding'))
343 context.clone_uri_tmpl = rc_config.get('rhodecode_clone_uri_tmpl')
343 context.clone_uri_tmpl = rc_config.get('rhodecode_clone_uri_tmpl')
344 context.clone_uri_ssh_tmpl = rc_config.get('rhodecode_clone_uri_ssh_tmpl')
344 context.clone_uri_ssh_tmpl = rc_config.get('rhodecode_clone_uri_ssh_tmpl')
345
345
346 # INI stored
346 # INI stored
347 context.labs_active = str2bool(
347 context.labs_active = str2bool(
348 config.get('labs_settings_active', 'false'))
348 config.get('labs_settings_active', 'false'))
349 context.ssh_enabled = str2bool(
349 context.ssh_enabled = str2bool(
350 config.get('ssh.generate_authorized_keyfile', 'false'))
350 config.get('ssh.generate_authorized_keyfile', 'false'))
351 context.ssh_key_generator_enabled = str2bool(
351 context.ssh_key_generator_enabled = str2bool(
352 config.get('ssh.enable_ui_key_generator', 'true'))
352 config.get('ssh.enable_ui_key_generator', 'true'))
353
353
354 context.visual.allow_repo_location_change = str2bool(
354 context.visual.allow_repo_location_change = str2bool(
355 config.get('allow_repo_location_change', True))
355 config.get('allow_repo_location_change', True))
356 context.visual.allow_custom_hooks_settings = str2bool(
356 context.visual.allow_custom_hooks_settings = str2bool(
357 config.get('allow_custom_hooks_settings', True))
357 config.get('allow_custom_hooks_settings', True))
358 context.debug_style = str2bool(config.get('debug_style', False))
358 context.debug_style = str2bool(config.get('debug_style', False))
359
359
360 context.rhodecode_instanceid = config.get('instance_id')
360 context.rhodecode_instanceid = config.get('instance_id')
361
361
362 context.visual.cut_off_limit_diff = safe_int(
362 context.visual.cut_off_limit_diff = safe_int(
363 config.get('cut_off_limit_diff'))
363 config.get('cut_off_limit_diff'))
364 context.visual.cut_off_limit_file = safe_int(
364 context.visual.cut_off_limit_file = safe_int(
365 config.get('cut_off_limit_file'))
365 config.get('cut_off_limit_file'))
366
366
367 context.license = AttributeDict({})
367 context.license = AttributeDict({})
368 context.license.hide_license_info = str2bool(
368 context.license.hide_license_info = str2bool(
369 config.get('license.hide_license_info', False))
369 config.get('license.hide_license_info', False))
370
370
371 # AppEnlight
371 # AppEnlight
372 context.appenlight_enabled = str2bool(config.get('appenlight', 'false'))
372 context.appenlight_enabled = str2bool(config.get('appenlight', 'false'))
373 context.appenlight_api_public_key = config.get(
373 context.appenlight_api_public_key = config.get(
374 'appenlight.api_public_key', '')
374 'appenlight.api_public_key', '')
375 context.appenlight_server_url = config.get('appenlight.server_url', '')
375 context.appenlight_server_url = config.get('appenlight.server_url', '')
376
376
377 diffmode = {
377 diffmode = {
378 "unified": "unified",
378 "unified": "unified",
379 "sideside": "sideside"
379 "sideside": "sideside"
380 }.get(request.GET.get('diffmode'))
380 }.get(request.GET.get('diffmode'))
381
381
382 if is_api is not None:
382 if is_api is not None:
383 is_api = hasattr(request, 'rpc_user')
383 is_api = hasattr(request, 'rpc_user')
384 session_attrs = {
384 session_attrs = {
385 # defaults
385 # defaults
386 "clone_url_format": "http",
386 "clone_url_format": "http",
387 "diffmode": "sideside"
387 "diffmode": "sideside"
388 }
388 }
389
389
390 if not is_api:
390 if not is_api:
391 # don't access pyramid session for API calls
391 # don't access pyramid session for API calls
392 if diffmode and diffmode != request.session.get('rc_user_session_attr.diffmode'):
392 if diffmode and diffmode != request.session.get('rc_user_session_attr.diffmode'):
393 request.session['rc_user_session_attr.diffmode'] = diffmode
393 request.session['rc_user_session_attr.diffmode'] = diffmode
394
394
395 # session settings per user
395 # session settings per user
396
396
397 for k, v in request.session.items():
397 for k, v in request.session.items():
398 pref = 'rc_user_session_attr.'
398 pref = 'rc_user_session_attr.'
399 if k and k.startswith(pref):
399 if k and k.startswith(pref):
400 k = k[len(pref):]
400 k = k[len(pref):]
401 session_attrs[k] = v
401 session_attrs[k] = v
402
402
403 context.user_session_attrs = session_attrs
403 context.user_session_attrs = session_attrs
404
404
405 # JS template context
405 # JS template context
406 context.template_context = {
406 context.template_context = {
407 'repo_name': None,
407 'repo_name': None,
408 'repo_type': None,
408 'repo_type': None,
409 'repo_landing_commit': None,
409 'repo_landing_commit': None,
410 'rhodecode_user': {
410 'rhodecode_user': {
411 'username': None,
411 'username': None,
412 'email': None,
412 'email': None,
413 'notification_status': False
413 'notification_status': False
414 },
414 },
415 'session_attrs': session_attrs,
415 'session_attrs': session_attrs,
416 'visual': {
416 'visual': {
417 'default_renderer': None
417 'default_renderer': None
418 },
418 },
419 'commit_data': {
419 'commit_data': {
420 'commit_id': None
420 'commit_id': None
421 },
421 },
422 'pull_request_data': {'pull_request_id': None},
422 'pull_request_data': {'pull_request_id': None},
423 'timeago': {
423 'timeago': {
424 'refresh_time': 120 * 1000,
424 'refresh_time': 120 * 1000,
425 'cutoff_limit': 1000 * 60 * 60 * 24 * 7
425 'cutoff_limit': 1000 * 60 * 60 * 24 * 7
426 },
426 },
427 'pyramid_dispatch': {
427 'pyramid_dispatch': {
428
428
429 },
429 },
430 'extra': {'plugins': {}}
430 'extra': {'plugins': {}}
431 }
431 }
432 # END CONFIG VARS
432 # END CONFIG VARS
433 if is_api:
433 if is_api:
434 csrf_token = None
434 csrf_token = None
435 else:
435 else:
436 csrf_token = auth.get_csrf_token(session=request.session)
436 csrf_token = auth.get_csrf_token(session=request.session)
437
437
438 context.csrf_token = csrf_token
438 context.csrf_token = csrf_token
439 context.backends = rhodecode.BACKENDS.keys()
439 context.backends = rhodecode.BACKENDS.keys()
440 context.backends.sort()
440
441 unread_count = 0
441 unread_count = 0
442 user_bookmark_list = []
442 user_bookmark_list = []
443 if user_id:
443 if user_id:
444 unread_count = NotificationModel().get_unread_cnt_for_user(user_id)
444 unread_count = NotificationModel().get_unread_cnt_for_user(user_id)
445 user_bookmark_list = UserBookmark.get_bookmarks_for_user(user_id)
445 user_bookmark_list = UserBookmark.get_bookmarks_for_user(user_id)
446 context.unread_notifications = unread_count
446 context.unread_notifications = unread_count
447 context.bookmark_items = user_bookmark_list
447 context.bookmark_items = user_bookmark_list
448
448
449 # web case
449 # web case
450 if hasattr(request, 'user'):
450 if hasattr(request, 'user'):
451 context.auth_user = request.user
451 context.auth_user = request.user
452 context.rhodecode_user = request.user
452 context.rhodecode_user = request.user
453
453
454 # api case
454 # api case
455 if hasattr(request, 'rpc_user'):
455 if hasattr(request, 'rpc_user'):
456 context.auth_user = request.rpc_user
456 context.auth_user = request.rpc_user
457 context.rhodecode_user = request.rpc_user
457 context.rhodecode_user = request.rpc_user
458
458
459 # attach the whole call context to the request
459 # attach the whole call context to the request
460 request.call_context = context
460 request.call_context = context
461
461
462
462
463 def get_auth_user(request):
463 def get_auth_user(request):
464 environ = request.environ
464 environ = request.environ
465 session = request.session
465 session = request.session
466
466
467 ip_addr = get_ip_addr(environ)
467 ip_addr = get_ip_addr(environ)
468
468
469 # make sure that we update permissions each time we call controller
469 # make sure that we update permissions each time we call controller
470 _auth_token = (request.GET.get('auth_token', '') or request.GET.get('api_key', ''))
470 _auth_token = (request.GET.get('auth_token', '') or request.GET.get('api_key', ''))
471 if not _auth_token and request.matchdict:
471 if not _auth_token and request.matchdict:
472 url_auth_token = request.matchdict.get('_auth_token')
472 url_auth_token = request.matchdict.get('_auth_token')
473 _auth_token = url_auth_token
473 _auth_token = url_auth_token
474 if _auth_token:
474 if _auth_token:
475 log.debug('Using URL extracted auth token `...%s`', _auth_token[-4:])
475 log.debug('Using URL extracted auth token `...%s`', _auth_token[-4:])
476
476
477 if _auth_token:
477 if _auth_token:
478 # when using API_KEY we assume user exists, and
478 # when using API_KEY we assume user exists, and
479 # doesn't need auth based on cookies.
479 # doesn't need auth based on cookies.
480 auth_user = AuthUser(api_key=_auth_token, ip_addr=ip_addr)
480 auth_user = AuthUser(api_key=_auth_token, ip_addr=ip_addr)
481 authenticated = False
481 authenticated = False
482 else:
482 else:
483 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
483 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
484 try:
484 try:
485 auth_user = AuthUser(user_id=cookie_store.get('user_id', None),
485 auth_user = AuthUser(user_id=cookie_store.get('user_id', None),
486 ip_addr=ip_addr)
486 ip_addr=ip_addr)
487 except UserCreationError as e:
487 except UserCreationError as e:
488 h.flash(e, 'error')
488 h.flash(e, 'error')
489 # container auth or other auth functions that create users
489 # container auth or other auth functions that create users
490 # on the fly can throw this exception signaling that there's
490 # on the fly can throw this exception signaling that there's
491 # issue with user creation, explanation should be provided
491 # issue with user creation, explanation should be provided
492 # in Exception itself. We then create a simple blank
492 # in Exception itself. We then create a simple blank
493 # AuthUser
493 # AuthUser
494 auth_user = AuthUser(ip_addr=ip_addr)
494 auth_user = AuthUser(ip_addr=ip_addr)
495
495
496 # in case someone changes a password for user it triggers session
496 # in case someone changes a password for user it triggers session
497 # flush and forces a re-login
497 # flush and forces a re-login
498 if password_changed(auth_user, session):
498 if password_changed(auth_user, session):
499 session.invalidate()
499 session.invalidate()
500 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
500 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
501 auth_user = AuthUser(ip_addr=ip_addr)
501 auth_user = AuthUser(ip_addr=ip_addr)
502
502
503 authenticated = cookie_store.get('is_authenticated')
503 authenticated = cookie_store.get('is_authenticated')
504
504
505 if not auth_user.is_authenticated and auth_user.is_user_object:
505 if not auth_user.is_authenticated and auth_user.is_user_object:
506 # user is not authenticated and not empty
506 # user is not authenticated and not empty
507 auth_user.set_authenticated(authenticated)
507 auth_user.set_authenticated(authenticated)
508
508
509 return auth_user, _auth_token
509 return auth_user, _auth_token
510
510
511
511
512 def h_filter(s):
512 def h_filter(s):
513 """
513 """
514 Custom filter for Mako templates. Mako by standard uses `markupsafe.escape`
514 Custom filter for Mako templates. Mako by standard uses `markupsafe.escape`
515 we wrap this with additional functionality that converts None to empty
515 we wrap this with additional functionality that converts None to empty
516 strings
516 strings
517 """
517 """
518 if s is None:
518 if s is None:
519 return markupsafe.Markup()
519 return markupsafe.Markup()
520 return markupsafe.escape(s)
520 return markupsafe.escape(s)
521
521
522
522
523 def add_events_routes(config):
523 def add_events_routes(config):
524 """
524 """
525 Adds routing that can be used in events. Because some events are triggered
525 Adds routing that can be used in events. Because some events are triggered
526 outside of pyramid context, we need to bootstrap request with some
526 outside of pyramid context, we need to bootstrap request with some
527 routing registered
527 routing registered
528 """
528 """
529
529
530 from rhodecode.apps._base import ADMIN_PREFIX
530 from rhodecode.apps._base import ADMIN_PREFIX
531
531
532 config.add_route(name='home', pattern='/')
532 config.add_route(name='home', pattern='/')
533 config.add_route(name='main_page_repos_data', pattern='/_home_repos')
533 config.add_route(name='main_page_repos_data', pattern='/_home_repos')
534 config.add_route(name='main_page_repo_groups_data', pattern='/_home_repo_groups')
534 config.add_route(name='main_page_repo_groups_data', pattern='/_home_repo_groups')
535
535
536 config.add_route(name='login', pattern=ADMIN_PREFIX + '/login')
536 config.add_route(name='login', pattern=ADMIN_PREFIX + '/login')
537 config.add_route(name='logout', pattern=ADMIN_PREFIX + '/logout')
537 config.add_route(name='logout', pattern=ADMIN_PREFIX + '/logout')
538 config.add_route(name='repo_summary', pattern='/{repo_name}')
538 config.add_route(name='repo_summary', pattern='/{repo_name}')
539 config.add_route(name='repo_summary_explicit', pattern='/{repo_name}/summary')
539 config.add_route(name='repo_summary_explicit', pattern='/{repo_name}/summary')
540 config.add_route(name='repo_group_home', pattern='/{repo_group_name}')
540 config.add_route(name='repo_group_home', pattern='/{repo_group_name}')
541
541
542 config.add_route(name='pullrequest_show',
542 config.add_route(name='pullrequest_show',
543 pattern='/{repo_name}/pull-request/{pull_request_id}')
543 pattern='/{repo_name}/pull-request/{pull_request_id}')
544 config.add_route(name='pull_requests_global',
544 config.add_route(name='pull_requests_global',
545 pattern='/pull-request/{pull_request_id}')
545 pattern='/pull-request/{pull_request_id}')
546
546
547 config.add_route(name='repo_commit',
547 config.add_route(name='repo_commit',
548 pattern='/{repo_name}/changeset/{commit_id}')
548 pattern='/{repo_name}/changeset/{commit_id}')
549 config.add_route(name='repo_files',
549 config.add_route(name='repo_files',
550 pattern='/{repo_name}/files/{commit_id}/{f_path}')
550 pattern='/{repo_name}/files/{commit_id}/{f_path}')
551
551
552 config.add_route(name='hovercard_user',
552 config.add_route(name='hovercard_user',
553 pattern='/_hovercard/user/{user_id}')
553 pattern='/_hovercard/user/{user_id}')
554
554
555 config.add_route(name='hovercard_user_group',
555 config.add_route(name='hovercard_user_group',
556 pattern='/_hovercard/user_group/{user_group_id}')
556 pattern='/_hovercard/user_group/{user_group_id}')
557
557
558 config.add_route(name='hovercard_pull_request',
558 config.add_route(name='hovercard_pull_request',
559 pattern='/_hovercard/pull_request/{pull_request_id}')
559 pattern='/_hovercard/pull_request/{pull_request_id}')
560
560
561 config.add_route(name='hovercard_repo_commit',
561 config.add_route(name='hovercard_repo_commit',
562 pattern='/_hovercard/commit/{repo_name}/{commit_id}')
562 pattern='/_hovercard/commit/{repo_name}/{commit_id}')
563
563
564
564
565 def bootstrap_config(request):
565 def bootstrap_config(request):
566 import pyramid.testing
566 import pyramid.testing
567 registry = pyramid.testing.Registry('RcTestRegistry')
567 registry = pyramid.testing.Registry('RcTestRegistry')
568
568
569 config = pyramid.testing.setUp(registry=registry, request=request)
569 config = pyramid.testing.setUp(registry=registry, request=request)
570
570
571 # allow pyramid lookup in testing
571 # allow pyramid lookup in testing
572 config.include('pyramid_mako')
572 config.include('pyramid_mako')
573 config.include('rhodecode.lib.rc_beaker')
573 config.include('rhodecode.lib.rc_beaker')
574 config.include('rhodecode.lib.rc_cache')
574 config.include('rhodecode.lib.rc_cache')
575
575
576 add_events_routes(config)
576 add_events_routes(config)
577
577
578 return config
578 return config
579
579
580
580
581 def bootstrap_request(**kwargs):
581 def bootstrap_request(**kwargs):
582 import pyramid.testing
582 import pyramid.testing
583
583
584 class TestRequest(pyramid.testing.DummyRequest):
584 class TestRequest(pyramid.testing.DummyRequest):
585 application_url = kwargs.pop('application_url', 'http://example.com')
585 application_url = kwargs.pop('application_url', 'http://example.com')
586 host = kwargs.pop('host', 'example.com:80')
586 host = kwargs.pop('host', 'example.com:80')
587 domain = kwargs.pop('domain', 'example.com')
587 domain = kwargs.pop('domain', 'example.com')
588
588
589 def translate(self, msg):
589 def translate(self, msg):
590 return msg
590 return msg
591
591
592 def plularize(self, singular, plural, n):
592 def plularize(self, singular, plural, n):
593 return singular
593 return singular
594
594
595 def get_partial_renderer(self, tmpl_name):
595 def get_partial_renderer(self, tmpl_name):
596
596
597 from rhodecode.lib.partial_renderer import get_partial_renderer
597 from rhodecode.lib.partial_renderer import get_partial_renderer
598 return get_partial_renderer(request=self, tmpl_name=tmpl_name)
598 return get_partial_renderer(request=self, tmpl_name=tmpl_name)
599
599
600 _call_context = TemplateArgs()
600 _call_context = TemplateArgs()
601 _call_context.visual = TemplateArgs()
601 _call_context.visual = TemplateArgs()
602 _call_context.visual.show_sha_length = 12
602 _call_context.visual.show_sha_length = 12
603 _call_context.visual.show_revision_number = True
603 _call_context.visual.show_revision_number = True
604
604
605 @property
605 @property
606 def call_context(self):
606 def call_context(self):
607 return self._call_context
607 return self._call_context
608
608
609 class TestDummySession(pyramid.testing.DummySession):
609 class TestDummySession(pyramid.testing.DummySession):
610 def save(*arg, **kw):
610 def save(*arg, **kw):
611 pass
611 pass
612
612
613 request = TestRequest(**kwargs)
613 request = TestRequest(**kwargs)
614 request.session = TestDummySession()
614 request.session = TestDummySession()
615
615
616 return request
616 return request
617
617
@@ -1,404 +1,411 b''
1 // forms.less
1 // forms.less
2 // For use in RhodeCode applications;
2 // For use in RhodeCode applications;
3 // see style guide documentation for guidelines.
3 // see style guide documentation for guidelines.
4
4
5 form.rcform {
5 form.rcform {
6
6
7 // reset for ie
7 // reset for ie
8 // using :not(#ie) prevents older browsers from applying these rules
8 // using :not(#ie) prevents older browsers from applying these rules
9 input[type="radio"],
9 input[type="radio"],
10 input[type="checkbox"] {
10 input[type="checkbox"] {
11 padding: 0;
11 padding: 0;
12 border: none;
12 border: none;
13 }
13 }
14 label { display: inline; border:none; padding:0; }
14 label { display: inline; border:none; padding:0; }
15 .label { display: none; }
15 .label { display: none; }
16
16
17 max-width: 940px;
17 max-width: 940px;
18 line-height: normal;
18 line-height: normal;
19 white-space: normal;
19 white-space: normal;
20 font-size: @basefontsize;
20 font-size: @basefontsize;
21 font-family: @text-light;
21 font-family: @text-light;
22 color: @form-textcolor;
22 color: @form-textcolor;
23
23
24 fieldset,
24 fieldset,
25 .buttons {
25 .buttons {
26 clear: both;
26 clear: both;
27 position: relative;
27 position: relative;
28 display:block;
28 display:block;
29 width: 100%;
29 width: 100%;
30 min-height: 3em;
30 min-height: 3em;
31 margin-bottom: @form-vertical-margin;
31 margin-bottom: @form-vertical-margin;
32 line-height: 1.2em;
32 line-height: 1.2em;
33
33
34 &:after { //clearfix
34 &:after { //clearfix
35 content: "";
35 content: "";
36 clear: both;
36 clear: both;
37 width: 100%;
37 width: 100%;
38 height: 1em;
38 height: 1em;
39 }
39 }
40
40
41 .label:not(#ie) {
41 .label:not(#ie) {
42 display: inline;
42 display: inline;
43 margin: 0 1em 0 .5em;
43 margin: 0 1em 0 .5em;
44 line-height: 1em;
44 line-height: 1em;
45 }
45 }
46 }
46 }
47
47
48 legend {
48 legend {
49 float: left;
49 float: left;
50 display: block;
50 display: block;
51 width: @legend-width;
51 width: @legend-width;
52 margin: 0;
52 margin: 0;
53 padding: 0 @padding 0 0;
53 padding: 0 @padding 0 0;
54 }
54 }
55
55
56 .fields {
56 .fields {
57 float: left;
57 float: left;
58 display: block;
58 display: block;
59 width: 100%;
59 width: 100%;
60 max-width: 500px;
60 max-width: 500px;
61 margin: 0 0 @padding -@legend-width;
61 margin: 0 0 @padding -@legend-width;
62 padding: 0 0 0 @legend-width;
62 padding: 0 0 0 @legend-width;
63
63
64 .btn {
64 .btn {
65 display: inline-block;
65 display: inline-block;
66 margin: 0 1em @padding 0;
66 margin: 0 1em @padding 0;
67 }
67 }
68 }
68 }
69
69
70 input,
70 input,
71 textarea {
71 textarea {
72 float: left;
72 float: left;
73 .box-sizing(content-box);
73 .box-sizing(content-box);
74 padding: @input-padding;
74 padding: @input-padding;
75 border: @border-thickness-inputs solid @grey4;
75 border: @border-thickness-inputs solid @grey4;
76 }
76 }
77
77
78 input {
78 input {
79 float: left;
79 float: left;
80 margin: 0 @input-padding 0 0;
80 margin: 0 @input-padding 0 0;
81 line-height: 1em;
81 line-height: 1em;
82 }
82 }
83
83
84 input[type="text"],
84 input[type="text"],
85 input[type="password"],
85 input[type="password"],
86 textarea {
86 textarea {
87 float: left;
87 float: left;
88 min-width: 200px;
88 min-width: 200px;
89 margin: 0 1em @padding 0;
89 margin: 0 1em @padding 0;
90 color: @form-textcolor;
90 color: @form-textcolor;
91 }
91 }
92
92
93 input[type="text"],
93 input[type="text"],
94 input[type="password"] {
94 input[type="password"] {
95 height: 1em;
95 height: 1em;
96 }
96 }
97
97
98 textarea {
98 textarea {
99 width: 100%;
99 width: 100%;
100 margin-top: -1em; //so it lines up with legend
100 margin-top: -1em; //so it lines up with legend
101 overflow: auto;
101 overflow: auto;
102 }
102 }
103
103
104 label:not(#ie) {
104 label:not(#ie) {
105 cursor: pointer;
105 cursor: pointer;
106 display: inline-block;
106 display: inline-block;
107 position: relative;
107 position: relative;
108 background: white;
108 background: white;
109 border-radius: 4px;
109 border-radius: 4px;
110 box-shadow: none;
110 box-shadow: none;
111
111
112 &:hover::after {
112 &:hover::after {
113 opacity: 0.5;
113 opacity: 0.5;
114 }
114 }
115 }
115 }
116
116
117 input[type="radio"]:not(#ie),
117 input[type="radio"]:not(#ie),
118 input[type="checkbox"]:not(#ie) {
118 input[type="checkbox"]:not(#ie) {
119 // Hide the input, but have it still be clickable
119 // Hide the input, but have it still be clickable
120 opacity: 0;
120 opacity: 0;
121 float: left;
121 float: left;
122 height: 0;
122 height: 0;
123 width: 0;
123 width: 0;
124 margin: 0;
124 margin: 0;
125 padding: 0;
125 padding: 0;
126 }
126 }
127 input[type='radio'] + label:not(#ie),
127 input[type='radio'] + label:not(#ie),
128 input[type='checkbox'] + label:not(#ie) {
128 input[type='checkbox'] + label:not(#ie) {
129 margin: 0;
129 margin: 0;
130 clear: none;
130 clear: none;
131 }
131 }
132
132
133 input[type='radio'] + label:not(#ie) {
133 input[type='radio'] + label:not(#ie) {
134 .circle (@form-radio-width,white);
134 .circle (@form-radio-width,white);
135 float: left;
135 float: left;
136 display: inline-block;
136 display: inline-block;
137 height: @form-radio-width;
137 height: @form-radio-width;
138 width: @form-radio-width;
138 width: @form-radio-width;
139 margin: 2px 6px 2px 0;
139 margin: 2px 6px 2px 0;
140 border: 1px solid @grey4;
140 border: 1px solid @grey4;
141 background-color: white;
141 background-color: white;
142 box-shadow: none;
142 box-shadow: none;
143 text-indent: -9999px;
143 text-indent: -9999px;
144 transition: none;
144 transition: none;
145
145
146 & + .label {
146 & + .label {
147 float: left;
147 float: left;
148 margin-top: 7px
148 margin-top: 7px
149 }
149 }
150 }
150 }
151
151
152 input[type='radio']:checked + label:not(#ie) {
152 input[type='radio']:checked + label:not(#ie) {
153 margin: 0 4px 0 -2px;
153 margin: 0 4px 0 -2px;
154 padding: 3px;
154 padding: 3px;
155 border-style: double;
155 border-style: double;
156 border-color: white;
156 border-color: white;
157 border-width: thick;
157 border-width: thick;
158 background-color: @rcblue;
158 background-color: @rcblue;
159 box-shadow: none;
159 box-shadow: none;
160 }
160 }
161
161
162 input[type='checkbox'] + label:not(#ie) {
162 input[type='checkbox'] + label:not(#ie) {
163 float: left;
163 float: left;
164 width: @form-check-width;
164 width: @form-check-width;
165 height: @form-check-width;
165 height: @form-check-width;
166 margin: 0 5px 1em 0;
166 margin: 0 5px 1em 0;
167 border: 1px solid @grey3;
167 border: 1px solid @grey3;
168 .border-radius(@border-radius);
168 .border-radius(@border-radius);
169 background-color: white;
169 background-color: white;
170 box-shadow: none;
170 box-shadow: none;
171 text-indent: -9999px;
171 text-indent: -9999px;
172 transition: none;
172 transition: none;
173
173
174 &:after {
174 &:after {
175 content: '';
175 content: '';
176 width: 9px;
176 width: 9px;
177 height: 5px;
177 height: 5px;
178 position: absolute;
178 position: absolute;
179 top: 4px;
179 top: 4px;
180 left: 4px;
180 left: 4px;
181 border: 3px solid @grey3;
181 border: 3px solid @grey3;
182 border-top: none;
182 border-top: none;
183 border-right: none;
183 border-right: none;
184 background: transparent;
184 background: transparent;
185 opacity: 0;
185 opacity: 0;
186 transform: rotate(-45deg);
186 transform: rotate(-45deg);
187 filter: progid:DXImageTransform.Microsoft.Matrix(sizingMethod='auto expand', M11=0.7071067811865476, M12=-0.7071067811865475, M21=0.7071067811865475, M22=0.7071067811865476); /* IE6,IE7 */
187 filter: progid:DXImageTransform.Microsoft.Matrix(sizingMethod='auto expand', M11=0.7071067811865476, M12=-0.7071067811865475, M21=0.7071067811865475, M22=0.7071067811865476); /* IE6,IE7 */
188
188
189 -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(SizingMethod='auto expand', M11=0.7071067811865476, M12=-0.7071067811865475, M21=0.7071067811865475, M22=0.7071067811865476)"; /* IE8 */ }
189 -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(SizingMethod='auto expand', M11=0.7071067811865476, M12=-0.7071067811865475, M21=0.7071067811865475, M22=0.7071067811865476)"; /* IE8 */ }
190
190
191 & + .label {
191 & + .label {
192 float: left;
192 float: left;
193 margin-top: 5px
193 margin-top: 5px
194 }
194 }
195 }
195 }
196
196
197 input[type=checkbox]:not(#ie) {
197 input[type=checkbox]:not(#ie) {
198 visibility: hidden;
198 visibility: hidden;
199 &:checked + label:after {
199 &:checked + label:after {
200 opacity: 1;
200 opacity: 1;
201 }
201 }
202 }
202 }
203
203
204 // center checkbox and label on a drop-down
204 // center checkbox and label on a drop-down
205 .drop-menu + select + input[type='checkbox'] + label:not(#ie) {
205 .drop-menu + select + input[type='checkbox'] + label:not(#ie) {
206 margin-top:10px;
206 margin-top:10px;
207
207
208 & + .label {
208 & + .label {
209 margin-top: 15px;
209 margin-top: 15px;
210 }
210 }
211 }
211 }
212
212
213 .formlist {
213 .formlist {
214 position: relative;
214 position: relative;
215 float: left;
215 float: left;
216 margin: 0;
216 margin: 0;
217 padding: 0;
217 padding: 0;
218
218
219 li {
219 li {
220 list-style-type: none;
220 list-style-type: none;
221
221
222 &:after {
222 &:after {
223 content: "";
223 content: "";
224 float: left;
224 float: left;
225 display: block;
225 display: block;
226 height: @padding;
226 height: @padding;
227 width: 100%;
227 width: 100%;
228 }
228 }
229 }
229 }
230 }
230 }
231
231
232 .drop-menu {
232 .drop-menu {
233 float: left;
233 float: left;
234
234
235 & + .last-item {
235 & + .last-item {
236 margin: 0;
236 margin: 0;
237 }
237 }
238
238
239 margin: 0 @input-padding 0 0;
239 margin: 0 @input-padding 0 0;
240 }
240 }
241
241
242 .help-block,
242 .help-block,
243 .error-message {
243 .error-message {
244 display: block;
244 display: block;
245 clear: both;
245 clear: both;
246 margin: @textmargin 0;
246 margin: @textmargin 0;
247 }
247 }
248
248
249 .error-message {
249 .error-message {
250 margin-top: 5px;
250 margin-top: 5px;
251 }
251 }
252
252
253 input[type=submit] {
253 input[type=submit] {
254 &:extend(.btn-primary);
254 &:extend(.btn-primary);
255
255
256 &:hover {
256 &:hover {
257 &:extend(.btn-primary:hover);
257 &:extend(.btn-primary:hover);
258 }
258 }
259 }
259 }
260
260
261 input[type=reset] {
261 input[type=reset] {
262 &:extend(.btn-default);
262 &:extend(.btn-default);
263
263
264 &:hover {
264 &:hover {
265 &:extend(.btn-default:hover);
265 &:extend(.btn-default:hover);
266 }
266 }
267 }
267 }
268
268
269 select,
269 select,
270 option:checked {
270 option:checked {
271 background-color: @rclightblue;
271 background-color: @rclightblue;
272 }
272 }
273
273
274 }
274 }
275
275
276 .rcform-element {
276 .rcform-element {
277
277
278 label { display: inline; border:none; padding:0; }
278 label { display: inline; border:none; padding:0; }
279 .label { display: none; }
279 .label { display: none; }
280
280
281 label:not(#ie) {
281 label:not(#ie) {
282 cursor: pointer;
282 cursor: pointer;
283 display: inline-block;
283 display: inline-block;
284 position: relative;
284 position: relative;
285 background: white;
285 background: white;
286 border-radius: 4px;
286 border-radius: 4px;
287 box-shadow: none;
287 box-shadow: none;
288
288
289 &:hover::after {
289 &:hover::after {
290 opacity: 0.5;
290 opacity: 0.5;
291 }
291 }
292 }
292 }
293
293
294 input[type="radio"],
294 input[type="radio"],
295 input[type="checkbox"] {
295 input[type="checkbox"] {
296 padding: 0;
296 padding: 0;
297 border: none;
297 border: none;
298 }
298 }
299
299
300 input[type="radio"]:not(#ie),
300 input[type="radio"]:not(#ie),
301 input[type="checkbox"]:not(#ie) {
301 input[type="checkbox"]:not(#ie) {
302 // Hide the input, but have it still be clickable
302 // Hide the input, but have it still be clickable
303 opacity: 0;
303 opacity: 0;
304 float: left;
304 float: left;
305 height: 0;
305 height: 0;
306 width: 0;
306 width: 0;
307 margin: 0;
307 margin: 0;
308 padding: 0;
308 padding: 0;
309 }
309 }
310 input[type='radio'] + label:not(#ie),
310 input[type='radio'] + label:not(#ie),
311 input[type='checkbox'] + label:not(#ie) {
311 input[type='checkbox'] + label:not(#ie) {
312 margin: 0;
312 margin: 0;
313 clear: none;
313 clear: none;
314 }
314 }
315
315
316 input[type='radio'] + label:not(#ie) {
316 input[type='radio'] + label:not(#ie) {
317 .circle (@form-radio-width,white);
317 .circle (@form-radio-width,white);
318 float: left;
318 float: left;
319 display: inline-block;
319 display: inline-block;
320 height: @form-radio-width;
320 height: @form-radio-width;
321 width: @form-radio-width;
321 width: @form-radio-width;
322 margin: 2px 2px 2px 0;
322 margin: 2px 2px 2px 0;
323 border: 1px solid @grey4;
323 border: 1px solid @grey4;
324 background-color: white;
324 background-color: white;
325 box-shadow: none;
325 box-shadow: none;
326 text-indent: -9999px;
326 text-indent: -9999px;
327 transition: none;
327 transition: none;
328
328
329 & + .label {
329 & + .label {
330 float: left;
330 float: left;
331 margin-top: 7px
331 margin-top: 7px
332 }
332 }
333 }
333 }
334
334
335 input[type='radio']:checked + label:not(#ie) {
335 input[type='radio']:checked + label:not(#ie) {
336 margin: 0 0px 0 -2px;
336 margin: 0 0px 0 -2px;
337 padding: 3px;
337 padding: 3px;
338 border-style: double;
338 border-style: double;
339 border-color: white;
339 border-color: white;
340 border-width: thick;
340 border-width: thick;
341 background-color: @rcblue;
341 background-color: @rcblue;
342 box-shadow: none;
342 box-shadow: none;
343 }
343 }
344
344
345 fieldset {
345 fieldset {
346 .label:not(#ie) {
346 .label:not(#ie) {
347 display: inline;
347 display: inline;
348 margin: 0 1em 0 .5em;
348 margin: 0 1em 0 .5em;
349 line-height: 1em;
349 line-height: 1em;
350 }
350 }
351 }
351 }
352
352
353 }
353 }
354
354
355 .badged-field {
355 .badged-field {
356 .user-badge {
356 .user-badge {
357 line-height: 25px;
357 line-height: 25px;
358 padding: .4em;
358 padding: .4em;
359 border-radius: @border-radius;
359 border-radius: @border-radius;
360 border-top: 1px solid @grey4;
360 border-top: 1px solid @grey4;
361 border-left: 1px solid @grey4;
361 border-left: 1px solid @grey4;
362 border-bottom: 1px solid @grey4;
362 border-bottom: 1px solid @grey4;
363 font-size: 14px;
363 font-size: 14px;
364 font-style: normal;
364 font-style: normal;
365 color: @text-light;
365 color: @text-light;
366 background: @grey7;
366 background: @grey7;
367 display: inline-block;
367 display: inline-block;
368 vertical-align: top;
368 vertical-align: top;
369 cursor: default;
369 cursor: default;
370 margin-right: -2px;
370 margin-right: -2px;
371 }
371 }
372 .badge-input-container {
372 .badge-input-container {
373 display: flex;
373 display: flex;
374 position: relative;
374 position: relative;
375 }
375 }
376 .user-disabled {
376 .user-disabled {
377 text-decoration: line-through;
377 text-decoration: line-through;
378 }
378 }
379 .badge-input-wrap {
379 .badge-input-wrap {
380 display: inline-block;
380 display: inline-block;
381 }
381 }
382 }
382 }
383
383
384 // for situations where we wish to display the form value but not the form input
384 // for situations where we wish to display the form value but not the form input
385 input.input-valuedisplay {
385 input.input-valuedisplay {
386 border: none;
386 border: none;
387 }
387 }
388
388
389 // for forms which only display information
389 // for forms which only display information
390 .infoform {
390 .infoform {
391 .fields {
391 .fields {
392 .field {
392 .field {
393 label,
393 label,
394 .label,
394 .label,
395 input,
395 input,
396 .input {
396 .input {
397 margin-top: 0;
397 margin-top: 0;
398 margin-bottom: 0;
398 margin-bottom: 0;
399 padding-top: 0;
399 padding-top: 0;
400 padding-bottom: 0;
400 padding-bottom: 0;
401 }
401 }
402 }
402 }
403 }
403 }
404 }
404 }
405
406 .repo-type-radio input {
407 margin: 0 0 0 0
408 }
409 .repo-type-radio label {
410 padding-right: 15px;
411 }
@@ -1,158 +1,175 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2
2
3 ${h.secure_form(h.route_path('repo_create'), request=request)}
3 ${h.secure_form(h.route_path('repo_create'), request=request)}
4 <div class="form">
4 <div class="form">
5 <!-- fields -->
5 <!-- fields -->
6 <div class="fields">
6 <div class="fields">
7 <div class="field">
7 <div class="field">
8 <div class="label">
8 <div class="label">
9 <label for="repo_name">${_('Repository name')}:</label>
9 <label for="repo_name">${_('Repository name')}:</label>
10 </div>
10 </div>
11 <div class="input">
11 <div class="input">
12 ${h.text('repo_name', class_="medium")}
12 ${h.text('repo_name', class_="medium")}
13 <div class="info-block">
13 <div class="info-block">
14 <a id="remote_clone_toggle" href="#">${_('Import Existing Repository ?')}</a>
14 <a id="remote_clone_toggle" href="#">${_('Import Existing Repository ?')}</a>
15 </div>
15 </div>
16 %if not c.rhodecode_user.is_admin:
16 %if not c.rhodecode_user.is_admin:
17 ${h.hidden('user_created',True)}
17 ${h.hidden('user_created',True)}
18 %endif
18 %endif
19 </div>
19 </div>
20 </div>
20 </div>
21 <div id="remote_clone" class="field" style="display: none;">
21 <div id="remote_clone" class="field" style="display: none;">
22 <div class="label">
22 <div class="label">
23 <label for="clone_uri">${_('Clone from')}:</label>
23 <label for="clone_uri">${_('Clone from')}:</label>
24 </div>
24 </div>
25 <div class="input">
25 <div class="input">
26 ${h.text('clone_uri', class_="medium")}
26 ${h.text('clone_uri', class_="medium")}
27 <span class="help-block">
27 <span class="help-block">
28 <pre>
28 <pre>
29 - The repository must be accessible over http:// or https://
29 - The repository must be accessible over http:// or https://
30 - For Git projects it's recommended appending .git to the end of clone url.
30 - For Git projects it's recommended appending .git to the end of clone url.
31 - Make sure to select proper repository type from the below selector before importing it.
31 - Make sure to select proper repository type from the below selector before importing it.
32 - If your HTTP[S] repository is not publicly accessible,
32 - If your HTTP[S] repository is not publicly accessible,
33 add authentication information to the URL: https://username:password@server.company.com/repo-name.
33 add authentication information to the URL: https://username:password@server.company.com/repo-name.
34 - The Git LFS/Mercurial Largefiles objects will not be imported.
34 - The Git LFS/Mercurial Largefiles objects will not be imported.
35 - For very large repositories, it's recommended to manually copy them into the
35 - For very large repositories, it's recommended to manually copy them into the
36 RhodeCode <a href="${h.route_path('admin_settings_vcs', _anchor='vcs-storage-options')}">storage location</a> and run <a href="${h.route_path('admin_settings_mapping')}">Remap and Rescan</a>.
36 RhodeCode <a href="${h.route_path('admin_settings_vcs', _anchor='vcs-storage-options')}">storage location</a> and run <a href="${h.route_path('admin_settings_mapping')}">Remap and Rescan</a>.
37 </pre>
37 </pre>
38 </span>
38 </span>
39 </div>
39 </div>
40 </div>
40 </div>
41 <div class="field">
41 <div class="field">
42 <div class="label">
42 <div class="label">
43 <label for="repo_group">${_('Repository group')}:</label>
43 <label for="repo_group">${_('Repository group')}:</label>
44 </div>
44 </div>
45 <div class="select">
45 <div class="select">
46 ${h.select('repo_group',request.GET.get('parent_group'),c.repo_groups,class_="medium")}
46 ${h.select('repo_group',request.GET.get('parent_group'),c.repo_groups,class_="medium")}
47 % if c.personal_repo_group:
47 % if c.personal_repo_group:
48 <a class="btn" href="#" id="select_my_group" data-personal-group-id="${c.personal_repo_group.group_id}">
48 <a class="btn" href="#" id="select_my_group" data-personal-group-id="${c.personal_repo_group.group_id}">
49 ${_('Select my personal group (%(repo_group_name)s)') % {'repo_group_name': c.personal_repo_group.group_name}}
49 ${_('Select my personal group (%(repo_group_name)s)') % {'repo_group_name': c.personal_repo_group.group_name}}
50 </a>
50 </a>
51 % endif
51 % endif
52 <span class="help-block">${_('Optionally select a group to put this repository into.')}</span>
52 <span class="help-block">${_('Optionally select a group to put this repository into.')}</span>
53 </div>
53 </div>
54 </div>
54 </div>
55
55 <div class="field">
56 <div class="field">
56 <div class="label">
57 <div class="label">
57 <label for="repo_type">${_('Type')}:</label>
58 <label for="repo_type">${_('Type')}:</label>
58 </div>
59 </div>
59 <div class="select">
60 <div class="fields repo-type-radio">
60 ${h.select('repo_type','hg',c.backends)}
61
62
63 % for backend in c.backends:
64 % if loop.index == 0:
65 <input id="repo_type_${backend}" name="repo_type" type="radio" value="${backend}" checked="checked"/>
66 % else:
67 <input id="repo_type_${backend}" name="repo_type" type="radio" value="${backend}" />
68 % endif
69
70 <label for="repo_type_${backend}">
71 <i class="icon-${backend}" style="font-size: 16px"></i>
72 ${backend.upper()}
73 </label>
74
75 % endfor
76
77
61 <span class="help-block">${_('Set the type of repository to create.')}</span>
78 <span class="help-block">${_('Set the type of repository to create.')}</span>
62 </div>
79 </div>
63 </div>
80 </div>
64 <div class="field">
81 <div class="field">
65 <div class="label">
82 <div class="label">
66 <label for="repo_description">${_('Description')}:</label>
83 <label for="repo_description">${_('Description')}:</label>
67 </div>
84 </div>
68 <div class="textarea editor">
85 <div class="textarea editor">
69 ${h.textarea('repo_description',cols=23,rows=5,class_="medium")}
86 ${h.textarea('repo_description',cols=23,rows=5,class_="medium")}
70 <% metatags_url = h.literal('''<a href="#metatagsShow" onclick="$('#meta-tags-desc').toggle();return false">meta-tags</a>''') %>
87 <% metatags_url = h.literal('''<a href="#metatagsShow" onclick="$('#meta-tags-desc').toggle();return false">meta-tags</a>''') %>
71 <span class="help-block">
88 <span class="help-block">
72 % if c.visual.stylify_metatags:
89 % if c.visual.stylify_metatags:
73 ${_('Plain text format with {metatags} support.').format(metatags=metatags_url)|n}
90 ${_('Plain text format with {metatags} support.').format(metatags=metatags_url)|n}
74 % else:
91 % else:
75 ${_('Plain text format.')}
92 ${_('Plain text format.')}
76 % endif
93 % endif
77 ${_('Add a README file for longer descriptions')}
94 ${_('Add a README file for longer descriptions')}
78 </span>
95 </span>
79 <span id="meta-tags-desc" style="display: none">
96 <span id="meta-tags-desc" style="display: none">
80 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
97 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
81 ${dt.metatags_help()}
98 ${dt.metatags_help()}
82 </span>
99 </span>
83 </div>
100 </div>
84 </div>
101 </div>
85 <div id="copy_perms" class="field">
102 <div id="copy_perms" class="field">
86 <div class="label label-checkbox">
103 <div class="label label-checkbox">
87 <label for="repo_copy_permissions">${_('Copy Parent Group Permissions')}:</label>
104 <label for="repo_copy_permissions">${_('Copy Parent Group Permissions')}:</label>
88 </div>
105 </div>
89 <div class="checkboxes">
106 <div class="checkboxes">
90 ${h.checkbox('repo_copy_permissions', value="True", checked="checked")}
107 ${h.checkbox('repo_copy_permissions', value="True", checked="checked")}
91 <span class="help-block">${_('Copy permissions from parent repository group.')}</span>
108 <span class="help-block">${_('Copy permissions from parent repository group.')}</span>
92 </div>
109 </div>
93 </div>
110 </div>
94 <div class="field">
111 <div class="field">
95 <div class="label label-checkbox">
112 <div class="label label-checkbox">
96 <label for="repo_private">${_('Private Repository')}:</label>
113 <label for="repo_private">${_('Private Repository')}:</label>
97 </div>
114 </div>
98 <div class="checkboxes">
115 <div class="checkboxes">
99 ${h.checkbox('repo_private',value="True")}
116 ${h.checkbox('repo_private',value="True")}
100 <span class="help-block">${_('Private repositories are only visible to people explicitly added as collaborators.')}</span>
117 <span class="help-block">${_('Private repositories are only visible to people explicitly added as collaborators.')}</span>
101 </div>
118 </div>
102 </div>
119 </div>
103 <div class="buttons">
120 <div class="buttons">
104 ${h.submit('save',_('Create Repository'),class_="btn")}
121 ${h.submit('save',_('Create Repository'),class_="btn")}
105 </div>
122 </div>
106 </div>
123 </div>
107 </div>
124 </div>
108 <script>
125 <script>
109 $(document).ready(function(){
126 $(document).ready(function(){
110 var setCopyPermsOption = function(group_val){
127 var setCopyPermsOption = function(group_val){
111 if(group_val != "-1"){
128 if(group_val != "-1"){
112 $('#copy_perms').show()
129 $('#copy_perms').show()
113 }
130 }
114 else{
131 else{
115 $('#copy_perms').hide();
132 $('#copy_perms').hide();
116 }
133 }
117 };
134 };
118
135
119 $('#remote_clone_toggle').on('click', function(e){
136 $('#remote_clone_toggle').on('click', function(e){
120 $('#remote_clone').show();
137 $('#remote_clone').show();
121 e.preventDefault();
138 e.preventDefault();
122 });
139 });
123
140
124 if($('#remote_clone input').hasClass('error')){
141 if($('#remote_clone input').hasClass('error')){
125 $('#remote_clone').show();
142 $('#remote_clone').show();
126 }
143 }
127 if($('#remote_clone input').val()){
144 if($('#remote_clone input').val()){
128 $('#remote_clone').show();
145 $('#remote_clone').show();
129 }
146 }
130
147
131 $("#repo_group").select2({
148 $("#repo_group").select2({
132 'containerCssClass': "drop-menu",
149 'containerCssClass': "drop-menu",
133 'dropdownCssClass': "drop-menu-dropdown",
150 'dropdownCssClass': "drop-menu-dropdown",
134 'dropdownAutoWidth': true,
151 'dropdownAutoWidth': true,
135 'width': "resolve"
152 'width': "resolve"
136 });
153 });
137
154
138 setCopyPermsOption($('#repo_group').val());
155 setCopyPermsOption($('#repo_group').val());
139 $("#repo_group").on("change", function(e) {
156 $("#repo_group").on("change", function(e) {
140 setCopyPermsOption(e.val)
157 setCopyPermsOption(e.val)
141 });
158 });
142
159
143 $("#repo_type").select2({
160 $("#repo_type").select2({
144 'containerCssClass': "drop-menu",
161 'containerCssClass': "drop-menu",
145 'dropdownCssClass': "drop-menu-dropdown",
162 'dropdownCssClass': "drop-menu-dropdown",
146 'minimumResultsForSearch': -1,
163 'minimumResultsForSearch': -1,
147 });
164 });
148
165
149 $('#repo_name').focus();
166 $('#repo_name').focus();
150
167
151 $('#select_my_group').on('click', function(e){
168 $('#select_my_group').on('click', function(e){
152 e.preventDefault();
169 e.preventDefault();
153 $("#repo_group").val($(this).data('personalGroupId')).trigger("change");
170 $("#repo_group").val($(this).data('personalGroupId')).trigger("change");
154 })
171 })
155
172
156 })
173 })
157 </script>
174 </script>
158 ${h.end_form()}
175 ${h.end_form()}
General Comments 0
You need to be logged in to leave comments. Login now