##// END OF EJS Templates
Full IP restrictions enabled...
marcink -
r3146:c5169e44 beta
parent child Browse files
Show More
@@ -1,296 +1,294 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.api
3 rhodecode.controllers.api
4 ~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 JSON RPC controller
6 JSON RPC controller
7
7
8 :created_on: Aug 20, 2011
8 :created_on: Aug 20, 2011
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27
27
28 import inspect
28 import inspect
29 import logging
29 import logging
30 import types
30 import types
31 import urllib
31 import urllib
32 import traceback
32 import traceback
33 import time
33 import time
34
34
35 from rhodecode.lib.compat import izip_longest, json
35 from rhodecode.lib.compat import izip_longest, json
36
36
37 from paste.response import replace_header
37 from paste.response import replace_header
38
38
39 from pylons.controllers import WSGIController
39 from pylons.controllers import WSGIController
40
40
41
41
42 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError, \
42 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError, \
43 HTTPBadRequest, HTTPError
43 HTTPBadRequest, HTTPError
44
44
45 from rhodecode.model.db import User
45 from rhodecode.model.db import User
46 from rhodecode.lib.auth import AuthUser, check_ip_access
46 from rhodecode.lib.auth import AuthUser
47 from rhodecode.lib.base import _get_ip_addr, _get_access_path
47 from rhodecode.lib.base import _get_ip_addr, _get_access_path
48 from rhodecode.lib.utils2 import safe_unicode
48 from rhodecode.lib.utils2 import safe_unicode
49
49
50 log = logging.getLogger('JSONRPC')
50 log = logging.getLogger('JSONRPC')
51
51
52
52
53 class JSONRPCError(BaseException):
53 class JSONRPCError(BaseException):
54
54
55 def __init__(self, message):
55 def __init__(self, message):
56 self.message = message
56 self.message = message
57 super(JSONRPCError, self).__init__()
57 super(JSONRPCError, self).__init__()
58
58
59 def __str__(self):
59 def __str__(self):
60 return str(self.message)
60 return str(self.message)
61
61
62
62
63 def jsonrpc_error(message, retid=None, code=None):
63 def jsonrpc_error(message, retid=None, code=None):
64 """
64 """
65 Generate a Response object with a JSON-RPC error body
65 Generate a Response object with a JSON-RPC error body
66 """
66 """
67 from pylons.controllers.util import Response
67 from pylons.controllers.util import Response
68 return Response(
68 return Response(
69 body=json.dumps(dict(id=retid, result=None, error=message)),
69 body=json.dumps(dict(id=retid, result=None, error=message)),
70 status=code,
70 status=code,
71 content_type='application/json'
71 content_type='application/json'
72 )
72 )
73
73
74
74
75 class JSONRPCController(WSGIController):
75 class JSONRPCController(WSGIController):
76 """
76 """
77 A WSGI-speaking JSON-RPC controller class
77 A WSGI-speaking JSON-RPC controller class
78
78
79 See the specification:
79 See the specification:
80 <http://json-rpc.org/wiki/specification>`.
80 <http://json-rpc.org/wiki/specification>`.
81
81
82 Valid controller return values should be json-serializable objects.
82 Valid controller return values should be json-serializable objects.
83
83
84 Sub-classes should catch their exceptions and raise JSONRPCError
84 Sub-classes should catch their exceptions and raise JSONRPCError
85 if they want to pass meaningful errors to the client.
85 if they want to pass meaningful errors to the client.
86
86
87 """
87 """
88
88
89 def _get_ip_addr(self, environ):
89 def _get_ip_addr(self, environ):
90 return _get_ip_addr(environ)
90 return _get_ip_addr(environ)
91
91
92 def _get_method_args(self):
92 def _get_method_args(self):
93 """
93 """
94 Return `self._rpc_args` to dispatched controller method
94 Return `self._rpc_args` to dispatched controller method
95 chosen by __call__
95 chosen by __call__
96 """
96 """
97 return self._rpc_args
97 return self._rpc_args
98
98
99 def __call__(self, environ, start_response):
99 def __call__(self, environ, start_response):
100 """
100 """
101 Parse the request body as JSON, look up the method on the
101 Parse the request body as JSON, look up the method on the
102 controller and if it exists, dispatch to it.
102 controller and if it exists, dispatch to it.
103 """
103 """
104 start = time.time()
104 start = time.time()
105 ip_addr = self.ip_addr = self._get_ip_addr(environ)
105 ip_addr = self.ip_addr = self._get_ip_addr(environ)
106 self._req_id = None
106 self._req_id = None
107 if 'CONTENT_LENGTH' not in environ:
107 if 'CONTENT_LENGTH' not in environ:
108 log.debug("No Content-Length")
108 log.debug("No Content-Length")
109 return jsonrpc_error(retid=self._req_id,
109 return jsonrpc_error(retid=self._req_id,
110 message="No Content-Length in request")
110 message="No Content-Length in request")
111 else:
111 else:
112 length = environ['CONTENT_LENGTH'] or 0
112 length = environ['CONTENT_LENGTH'] or 0
113 length = int(environ['CONTENT_LENGTH'])
113 length = int(environ['CONTENT_LENGTH'])
114 log.debug('Content-Length: %s' % length)
114 log.debug('Content-Length: %s' % length)
115
115
116 if length == 0:
116 if length == 0:
117 log.debug("Content-Length is 0")
117 log.debug("Content-Length is 0")
118 return jsonrpc_error(retid=self._req_id,
118 return jsonrpc_error(retid=self._req_id,
119 message="Content-Length is 0")
119 message="Content-Length is 0")
120
120
121 raw_body = environ['wsgi.input'].read(length)
121 raw_body = environ['wsgi.input'].read(length)
122
122
123 try:
123 try:
124 json_body = json.loads(urllib.unquote_plus(raw_body))
124 json_body = json.loads(urllib.unquote_plus(raw_body))
125 except ValueError, e:
125 except ValueError, e:
126 # catch JSON errors Here
126 # catch JSON errors Here
127 return jsonrpc_error(retid=self._req_id,
127 return jsonrpc_error(retid=self._req_id,
128 message="JSON parse error ERR:%s RAW:%r" \
128 message="JSON parse error ERR:%s RAW:%r" \
129 % (e, urllib.unquote_plus(raw_body)))
129 % (e, urllib.unquote_plus(raw_body)))
130
130
131 # check AUTH based on API KEY
131 # check AUTH based on API KEY
132 try:
132 try:
133 self._req_api_key = json_body['api_key']
133 self._req_api_key = json_body['api_key']
134 self._req_id = json_body['id']
134 self._req_id = json_body['id']
135 self._req_method = json_body['method']
135 self._req_method = json_body['method']
136 self._request_params = json_body['args']
136 self._request_params = json_body['args']
137 log.debug(
137 log.debug(
138 'method: %s, params: %s' % (self._req_method,
138 'method: %s, params: %s' % (self._req_method,
139 self._request_params)
139 self._request_params)
140 )
140 )
141 except KeyError, e:
141 except KeyError, e:
142 return jsonrpc_error(retid=self._req_id,
142 return jsonrpc_error(retid=self._req_id,
143 message='Incorrect JSON query missing %s' % e)
143 message='Incorrect JSON query missing %s' % e)
144
144
145 # check if we can find this session using api_key
145 # check if we can find this session using api_key
146 try:
146 try:
147 u = User.get_by_api_key(self._req_api_key)
147 u = User.get_by_api_key(self._req_api_key)
148 if u is None:
148 if u is None:
149 return jsonrpc_error(retid=self._req_id,
149 return jsonrpc_error(retid=self._req_id,
150 message='Invalid API KEY')
150 message='Invalid API KEY')
151
151 #check if we are allowed to use this IP
152 #check if we are allowed to use this IP
152 allowed_ips = AuthUser.get_allowed_ips(u.user_id)
153 auth_u = AuthUser(u.user_id, self._req_api_key, ip_addr=ip_addr)
153 if check_ip_access(source_ip=ip_addr, allowed_ips=allowed_ips) is False:
154 if not auth_u.ip_allowed:
154 log.info('Access for IP:%s forbidden, '
155 'not in %s' % (ip_addr, allowed_ips))
156 return jsonrpc_error(retid=self._req_id,
155 return jsonrpc_error(retid=self._req_id,
157 message='request from IP:%s not allowed' % (ip_addr))
156 message='request from IP:%s not allowed' % (ip_addr))
158 else:
157 else:
159 log.info('Access for IP:%s allowed' % (ip_addr))
158 log.info('Access for IP:%s allowed' % (ip_addr))
160
159
161 auth_u = AuthUser(u.user_id, self._req_api_key, ip_addr=ip_addr)
162 except Exception, e:
160 except Exception, e:
163 return jsonrpc_error(retid=self._req_id,
161 return jsonrpc_error(retid=self._req_id,
164 message='Invalid API KEY')
162 message='Invalid API KEY')
165
163
166 self._error = None
164 self._error = None
167 try:
165 try:
168 self._func = self._find_method()
166 self._func = self._find_method()
169 except AttributeError, e:
167 except AttributeError, e:
170 return jsonrpc_error(retid=self._req_id,
168 return jsonrpc_error(retid=self._req_id,
171 message=str(e))
169 message=str(e))
172
170
173 # now that we have a method, add self._req_params to
171 # now that we have a method, add self._req_params to
174 # self.kargs and dispatch control to WGIController
172 # self.kargs and dispatch control to WGIController
175 argspec = inspect.getargspec(self._func)
173 argspec = inspect.getargspec(self._func)
176 arglist = argspec[0][1:]
174 arglist = argspec[0][1:]
177 defaults = map(type, argspec[3] or [])
175 defaults = map(type, argspec[3] or [])
178 default_empty = types.NotImplementedType
176 default_empty = types.NotImplementedType
179
177
180 # kw arguments required by this method
178 # kw arguments required by this method
181 func_kwargs = dict(izip_longest(reversed(arglist), reversed(defaults),
179 func_kwargs = dict(izip_longest(reversed(arglist), reversed(defaults),
182 fillvalue=default_empty))
180 fillvalue=default_empty))
183
181
184 # this is little trick to inject logged in user for
182 # this is little trick to inject logged in user for
185 # perms decorators to work they expect the controller class to have
183 # perms decorators to work they expect the controller class to have
186 # rhodecode_user attribute set
184 # rhodecode_user attribute set
187 self.rhodecode_user = auth_u
185 self.rhodecode_user = auth_u
188
186
189 # This attribute will need to be first param of a method that uses
187 # This attribute will need to be first param of a method that uses
190 # api_key, which is translated to instance of user at that name
188 # api_key, which is translated to instance of user at that name
191 USER_SESSION_ATTR = 'apiuser'
189 USER_SESSION_ATTR = 'apiuser'
192
190
193 if USER_SESSION_ATTR not in arglist:
191 if USER_SESSION_ATTR not in arglist:
194 return jsonrpc_error(
192 return jsonrpc_error(
195 retid=self._req_id,
193 retid=self._req_id,
196 message='This method [%s] does not support '
194 message='This method [%s] does not support '
197 'authentication (missing %s param)' % (
195 'authentication (missing %s param)' % (
198 self._func.__name__, USER_SESSION_ATTR)
196 self._func.__name__, USER_SESSION_ATTR)
199 )
197 )
200
198
201 # get our arglist and check if we provided them as args
199 # get our arglist and check if we provided them as args
202 for arg, default in func_kwargs.iteritems():
200 for arg, default in func_kwargs.iteritems():
203 if arg == USER_SESSION_ATTR:
201 if arg == USER_SESSION_ATTR:
204 # USER_SESSION_ATTR is something translated from api key and
202 # USER_SESSION_ATTR is something translated from api key and
205 # this is checked before so we don't need validate it
203 # this is checked before so we don't need validate it
206 continue
204 continue
207
205
208 # skip the required param check if it's default value is
206 # skip the required param check if it's default value is
209 # NotImplementedType (default_empty)
207 # NotImplementedType (default_empty)
210 if (default == default_empty and arg not in self._request_params):
208 if (default == default_empty and arg not in self._request_params):
211 return jsonrpc_error(
209 return jsonrpc_error(
212 retid=self._req_id,
210 retid=self._req_id,
213 message=(
211 message=(
214 'Missing non optional `%s` arg in JSON DATA' % arg
212 'Missing non optional `%s` arg in JSON DATA' % arg
215 )
213 )
216 )
214 )
217
215
218 self._rpc_args = {USER_SESSION_ATTR: u}
216 self._rpc_args = {USER_SESSION_ATTR: u}
219 self._rpc_args.update(self._request_params)
217 self._rpc_args.update(self._request_params)
220
218
221 self._rpc_args['action'] = self._req_method
219 self._rpc_args['action'] = self._req_method
222 self._rpc_args['environ'] = environ
220 self._rpc_args['environ'] = environ
223 self._rpc_args['start_response'] = start_response
221 self._rpc_args['start_response'] = start_response
224
222
225 status = []
223 status = []
226 headers = []
224 headers = []
227 exc_info = []
225 exc_info = []
228
226
229 def change_content(new_status, new_headers, new_exc_info=None):
227 def change_content(new_status, new_headers, new_exc_info=None):
230 status.append(new_status)
228 status.append(new_status)
231 headers.extend(new_headers)
229 headers.extend(new_headers)
232 exc_info.append(new_exc_info)
230 exc_info.append(new_exc_info)
233
231
234 output = WSGIController.__call__(self, environ, change_content)
232 output = WSGIController.__call__(self, environ, change_content)
235 output = list(output)
233 output = list(output)
236 headers.append(('Content-Length', str(len(output[0]))))
234 headers.append(('Content-Length', str(len(output[0]))))
237 replace_header(headers, 'Content-Type', 'application/json')
235 replace_header(headers, 'Content-Type', 'application/json')
238 start_response(status[0], headers, exc_info[0])
236 start_response(status[0], headers, exc_info[0])
239 log.info('IP: %s Request to %s time: %.3fs' % (
237 log.info('IP: %s Request to %s time: %.3fs' % (
240 _get_ip_addr(environ),
238 _get_ip_addr(environ),
241 safe_unicode(_get_access_path(environ)), time.time() - start)
239 safe_unicode(_get_access_path(environ)), time.time() - start)
242 )
240 )
243 return output
241 return output
244
242
245 def _dispatch_call(self):
243 def _dispatch_call(self):
246 """
244 """
247 Implement dispatch interface specified by WSGIController
245 Implement dispatch interface specified by WSGIController
248 """
246 """
249 try:
247 try:
250 raw_response = self._inspect_call(self._func)
248 raw_response = self._inspect_call(self._func)
251 if isinstance(raw_response, HTTPError):
249 if isinstance(raw_response, HTTPError):
252 self._error = str(raw_response)
250 self._error = str(raw_response)
253 except JSONRPCError, e:
251 except JSONRPCError, e:
254 self._error = str(e)
252 self._error = str(e)
255 except Exception, e:
253 except Exception, e:
256 log.error('Encountered unhandled exception: %s' \
254 log.error('Encountered unhandled exception: %s' \
257 % traceback.format_exc())
255 % traceback.format_exc())
258 json_exc = JSONRPCError('Internal server error')
256 json_exc = JSONRPCError('Internal server error')
259 self._error = str(json_exc)
257 self._error = str(json_exc)
260
258
261 if self._error is not None:
259 if self._error is not None:
262 raw_response = None
260 raw_response = None
263
261
264 response = dict(id=self._req_id, result=raw_response,
262 response = dict(id=self._req_id, result=raw_response,
265 error=self._error)
263 error=self._error)
266
264
267 try:
265 try:
268 return json.dumps(response)
266 return json.dumps(response)
269 except TypeError, e:
267 except TypeError, e:
270 log.error('API FAILED. Error encoding response: %s' % e)
268 log.error('API FAILED. Error encoding response: %s' % e)
271 return json.dumps(
269 return json.dumps(
272 dict(
270 dict(
273 id=self._req_id,
271 id=self._req_id,
274 result=None,
272 result=None,
275 error="Error encoding response"
273 error="Error encoding response"
276 )
274 )
277 )
275 )
278
276
279 def _find_method(self):
277 def _find_method(self):
280 """
278 """
281 Return method named by `self._req_method` in controller if able
279 Return method named by `self._req_method` in controller if able
282 """
280 """
283 log.debug('Trying to find JSON-RPC method: %s' % self._req_method)
281 log.debug('Trying to find JSON-RPC method: %s' % self._req_method)
284 if self._req_method.startswith('_'):
282 if self._req_method.startswith('_'):
285 raise AttributeError("Method not allowed")
283 raise AttributeError("Method not allowed")
286
284
287 try:
285 try:
288 func = getattr(self, self._req_method, None)
286 func = getattr(self, self._req_method, None)
289 except UnicodeEncodeError:
287 except UnicodeEncodeError:
290 raise AttributeError("Problem decoding unicode in requested "
288 raise AttributeError("Problem decoding unicode in requested "
291 "method name.")
289 "method name.")
292
290
293 if isinstance(func, types.MethodType):
291 if isinstance(func, types.MethodType):
294 return func
292 return func
295 else:
293 else:
296 raise AttributeError("No such method: %s" % self._req_method)
294 raise AttributeError("No such method: %s" % self._req_method)
@@ -1,196 +1,195 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.login
3 rhodecode.controllers.login
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Login controller for rhodeocode
6 Login controller for rhodeocode
7
7
8 :created_on: Apr 22, 2010
8 :created_on: Apr 22, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import logging
26 import logging
27 import formencode
27 import formencode
28 import datetime
28 import datetime
29 import urlparse
29 import urlparse
30
30
31 from formencode import htmlfill
31 from formencode import htmlfill
32 from webob.exc import HTTPFound
32 from webob.exc import HTTPFound
33 from pylons.i18n.translation import _
33 from pylons.i18n.translation import _
34 from pylons.controllers.util import abort, redirect
34 from pylons.controllers.util import abort, redirect
35 from pylons import request, response, session, tmpl_context as c, url
35 from pylons import request, response, session, tmpl_context as c, url
36
36
37 import rhodecode.lib.helpers as h
37 import rhodecode.lib.helpers as h
38 from rhodecode.lib.auth import AuthUser, HasPermissionAnyDecorator
38 from rhodecode.lib.auth import AuthUser, HasPermissionAnyDecorator
39 from rhodecode.lib.base import BaseController, render
39 from rhodecode.lib.base import BaseController, render
40 from rhodecode.model.db import User
40 from rhodecode.model.db import User
41 from rhodecode.model.forms import LoginForm, RegisterForm, PasswordResetForm
41 from rhodecode.model.forms import LoginForm, RegisterForm, PasswordResetForm
42 from rhodecode.model.user import UserModel
42 from rhodecode.model.user import UserModel
43 from rhodecode.model.meta import Session
43 from rhodecode.model.meta import Session
44
44
45
45
46 log = logging.getLogger(__name__)
46 log = logging.getLogger(__name__)
47
47
48
48
49 class LoginController(BaseController):
49 class LoginController(BaseController):
50
50
51 def __before__(self):
51 def __before__(self):
52 super(LoginController, self).__before__()
52 super(LoginController, self).__before__()
53
53
54 def index(self):
54 def index(self):
55 # redirect if already logged in
55 # redirect if already logged in
56 c.came_from = request.GET.get('came_from')
56 c.came_from = request.GET.get('came_from')
57
57 not_default = self.rhodecode_user.username != 'default'
58 if self.rhodecode_user.is_authenticated \
58 ip_allowed = self.rhodecode_user.ip_allowed
59 and self.rhodecode_user.username != 'default':
59 if self.rhodecode_user.is_authenticated and not_default and ip_allowed:
60
61 return redirect(url('home'))
60 return redirect(url('home'))
62
61
63 if request.POST:
62 if request.POST:
64 # import Login Form validator class
63 # import Login Form validator class
65 login_form = LoginForm()
64 login_form = LoginForm()
66 try:
65 try:
67 session.invalidate()
66 session.invalidate()
68 c.form_result = login_form.to_python(dict(request.POST))
67 c.form_result = login_form.to_python(dict(request.POST))
69 # form checks for username/password, now we're authenticated
68 # form checks for username/password, now we're authenticated
70 username = c.form_result['username']
69 username = c.form_result['username']
71 user = User.get_by_username(username, case_insensitive=True)
70 user = User.get_by_username(username, case_insensitive=True)
72 auth_user = AuthUser(user.user_id)
71 auth_user = AuthUser(user.user_id)
73 auth_user.set_authenticated()
72 auth_user.set_authenticated()
74 cs = auth_user.get_cookie_store()
73 cs = auth_user.get_cookie_store()
75 session['rhodecode_user'] = cs
74 session['rhodecode_user'] = cs
76 user.update_lastlogin()
75 user.update_lastlogin()
77 Session().commit()
76 Session().commit()
78
77
79 # If they want to be remembered, update the cookie
78 # If they want to be remembered, update the cookie
80 if c.form_result['remember'] is not False:
79 if c.form_result['remember'] is not False:
81 _year = (datetime.datetime.now() +
80 _year = (datetime.datetime.now() +
82 datetime.timedelta(seconds=60 * 60 * 24 * 365))
81 datetime.timedelta(seconds=60 * 60 * 24 * 365))
83 session._set_cookie_expires(_year)
82 session._set_cookie_expires(_year)
84
83
85 session.save()
84 session.save()
86
85
87 log.info('user %s is now authenticated and stored in '
86 log.info('user %s is now authenticated and stored in '
88 'session, session attrs %s' % (username, cs))
87 'session, session attrs %s' % (username, cs))
89
88
90 # dumps session attrs back to cookie
89 # dumps session attrs back to cookie
91 session._update_cookie_out()
90 session._update_cookie_out()
92
91
93 # we set new cookie
92 # we set new cookie
94 headers = None
93 headers = None
95 if session.request['set_cookie']:
94 if session.request['set_cookie']:
96 # send set-cookie headers back to response to update cookie
95 # send set-cookie headers back to response to update cookie
97 headers = [('Set-Cookie', session.request['cookie_out'])]
96 headers = [('Set-Cookie', session.request['cookie_out'])]
98
97
99 allowed_schemes = ['http', 'https']
98 allowed_schemes = ['http', 'https']
100 if c.came_from:
99 if c.came_from:
101 parsed = urlparse.urlparse(c.came_from)
100 parsed = urlparse.urlparse(c.came_from)
102 server_parsed = urlparse.urlparse(url.current())
101 server_parsed = urlparse.urlparse(url.current())
103 if parsed.scheme and parsed.scheme not in allowed_schemes:
102 if parsed.scheme and parsed.scheme not in allowed_schemes:
104 log.error(
103 log.error(
105 'Suspicious URL scheme detected %s for url %s' %
104 'Suspicious URL scheme detected %s for url %s' %
106 (parsed.scheme, parsed))
105 (parsed.scheme, parsed))
107 c.came_from = url('home')
106 c.came_from = url('home')
108 elif server_parsed.netloc != parsed.netloc:
107 elif server_parsed.netloc != parsed.netloc:
109 log.error('Suspicious NETLOC detected %s for url %s'
108 log.error('Suspicious NETLOC detected %s for url %s'
110 'server url is: %s' %
109 'server url is: %s' %
111 (parsed.netloc, parsed, server_parsed))
110 (parsed.netloc, parsed, server_parsed))
112 c.came_from = url('home')
111 c.came_from = url('home')
113 raise HTTPFound(location=c.came_from, headers=headers)
112 raise HTTPFound(location=c.came_from, headers=headers)
114 else:
113 else:
115 raise HTTPFound(location=url('home'), headers=headers)
114 raise HTTPFound(location=url('home'), headers=headers)
116
115
117 except formencode.Invalid, errors:
116 except formencode.Invalid, errors:
118 return htmlfill.render(
117 return htmlfill.render(
119 render('/login.html'),
118 render('/login.html'),
120 defaults=errors.value,
119 defaults=errors.value,
121 errors=errors.error_dict or {},
120 errors=errors.error_dict or {},
122 prefix_error=False,
121 prefix_error=False,
123 encoding="UTF-8")
122 encoding="UTF-8")
124
123
125 return render('/login.html')
124 return render('/login.html')
126
125
127 @HasPermissionAnyDecorator('hg.admin', 'hg.register.auto_activate',
126 @HasPermissionAnyDecorator('hg.admin', 'hg.register.auto_activate',
128 'hg.register.manual_activate')
127 'hg.register.manual_activate')
129 def register(self):
128 def register(self):
130 c.auto_active = False
129 c.auto_active = False
131 for perm in User.get_by_username('default').user_perms:
130 for perm in User.get_by_username('default').user_perms:
132 if perm.permission.permission_name == 'hg.register.auto_activate':
131 if perm.permission.permission_name == 'hg.register.auto_activate':
133 c.auto_active = True
132 c.auto_active = True
134 break
133 break
135
134
136 if request.POST:
135 if request.POST:
137
136
138 register_form = RegisterForm()()
137 register_form = RegisterForm()()
139 try:
138 try:
140 form_result = register_form.to_python(dict(request.POST))
139 form_result = register_form.to_python(dict(request.POST))
141 form_result['active'] = c.auto_active
140 form_result['active'] = c.auto_active
142 UserModel().create_registration(form_result)
141 UserModel().create_registration(form_result)
143 h.flash(_('You have successfully registered into rhodecode'),
142 h.flash(_('You have successfully registered into rhodecode'),
144 category='success')
143 category='success')
145 Session().commit()
144 Session().commit()
146 return redirect(url('login_home'))
145 return redirect(url('login_home'))
147
146
148 except formencode.Invalid, errors:
147 except formencode.Invalid, errors:
149 return htmlfill.render(
148 return htmlfill.render(
150 render('/register.html'),
149 render('/register.html'),
151 defaults=errors.value,
150 defaults=errors.value,
152 errors=errors.error_dict or {},
151 errors=errors.error_dict or {},
153 prefix_error=False,
152 prefix_error=False,
154 encoding="UTF-8")
153 encoding="UTF-8")
155
154
156 return render('/register.html')
155 return render('/register.html')
157
156
158 def password_reset(self):
157 def password_reset(self):
159 if request.POST:
158 if request.POST:
160 password_reset_form = PasswordResetForm()()
159 password_reset_form = PasswordResetForm()()
161 try:
160 try:
162 form_result = password_reset_form.to_python(dict(request.POST))
161 form_result = password_reset_form.to_python(dict(request.POST))
163 UserModel().reset_password_link(form_result)
162 UserModel().reset_password_link(form_result)
164 h.flash(_('Your password reset link was sent'),
163 h.flash(_('Your password reset link was sent'),
165 category='success')
164 category='success')
166 return redirect(url('login_home'))
165 return redirect(url('login_home'))
167
166
168 except formencode.Invalid, errors:
167 except formencode.Invalid, errors:
169 return htmlfill.render(
168 return htmlfill.render(
170 render('/password_reset.html'),
169 render('/password_reset.html'),
171 defaults=errors.value,
170 defaults=errors.value,
172 errors=errors.error_dict or {},
171 errors=errors.error_dict or {},
173 prefix_error=False,
172 prefix_error=False,
174 encoding="UTF-8")
173 encoding="UTF-8")
175
174
176 return render('/password_reset.html')
175 return render('/password_reset.html')
177
176
178 def password_reset_confirmation(self):
177 def password_reset_confirmation(self):
179 if request.GET and request.GET.get('key'):
178 if request.GET and request.GET.get('key'):
180 try:
179 try:
181 user = User.get_by_api_key(request.GET.get('key'))
180 user = User.get_by_api_key(request.GET.get('key'))
182 data = dict(email=user.email)
181 data = dict(email=user.email)
183 UserModel().reset_password(data)
182 UserModel().reset_password(data)
184 h.flash(_('Your password reset was successful, '
183 h.flash(_('Your password reset was successful, '
185 'new password has been sent to your email'),
184 'new password has been sent to your email'),
186 category='success')
185 category='success')
187 except Exception, e:
186 except Exception, e:
188 log.error(e)
187 log.error(e)
189 return redirect(url('reset_password'))
188 return redirect(url('reset_password'))
190
189
191 return redirect(url('login_home'))
190 return redirect(url('login_home'))
192
191
193 def logout(self):
192 def logout(self):
194 session.delete()
193 session.delete()
195 log.info('Logging out and deleting session for user')
194 log.info('Logging out and deleting session for user')
196 redirect(url('home'))
195 redirect(url('home'))
@@ -1,851 +1,879 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.auth
3 rhodecode.lib.auth
4 ~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~
5
5
6 authentication and permission libraries
6 authentication and permission libraries
7
7
8 :created_on: Apr 4, 2010
8 :created_on: Apr 4, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import random
26 import random
27 import logging
27 import logging
28 import traceback
28 import traceback
29 import hashlib
29 import hashlib
30
30
31 from tempfile import _RandomNameSequence
31 from tempfile import _RandomNameSequence
32 from decorator import decorator
32 from decorator import decorator
33
33
34 from pylons import config, url, request
34 from pylons import config, url, request
35 from pylons.controllers.util import abort, redirect
35 from pylons.controllers.util import abort, redirect
36 from pylons.i18n.translation import _
36 from pylons.i18n.translation import _
37
37
38 from rhodecode import __platform__, is_windows, is_unix
38 from rhodecode import __platform__, is_windows, is_unix
39 from rhodecode.model.meta import Session
39 from rhodecode.model.meta import Session
40
40
41 from rhodecode.lib.utils2 import str2bool, safe_unicode
41 from rhodecode.lib.utils2 import str2bool, safe_unicode
42 from rhodecode.lib.exceptions import LdapPasswordError, LdapUsernameError
42 from rhodecode.lib.exceptions import LdapPasswordError, LdapUsernameError
43 from rhodecode.lib.utils import get_repo_slug, get_repos_group_slug
43 from rhodecode.lib.utils import get_repo_slug, get_repos_group_slug
44 from rhodecode.lib.auth_ldap import AuthLdap
44 from rhodecode.lib.auth_ldap import AuthLdap
45
45
46 from rhodecode.model import meta
46 from rhodecode.model import meta
47 from rhodecode.model.user import UserModel
47 from rhodecode.model.user import UserModel
48 from rhodecode.model.db import Permission, RhodeCodeSetting, User, UserIpMap
48 from rhodecode.model.db import Permission, RhodeCodeSetting, User, UserIpMap
49 from rhodecode.lib.caching_query import FromCache
49
50
50 log = logging.getLogger(__name__)
51 log = logging.getLogger(__name__)
51
52
52
53
53 class PasswordGenerator(object):
54 class PasswordGenerator(object):
54 """
55 """
55 This is a simple class for generating password from different sets of
56 This is a simple class for generating password from different sets of
56 characters
57 characters
57 usage::
58 usage::
58
59
59 passwd_gen = PasswordGenerator()
60 passwd_gen = PasswordGenerator()
60 #print 8-letter password containing only big and small letters
61 #print 8-letter password containing only big and small letters
61 of alphabet
62 of alphabet
62 passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
63 passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
63 """
64 """
64 ALPHABETS_NUM = r'''1234567890'''
65 ALPHABETS_NUM = r'''1234567890'''
65 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''
66 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''
66 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''
67 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''
67 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?'''
68 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?'''
68 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL \
69 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL \
69 + ALPHABETS_NUM + ALPHABETS_SPECIAL
70 + ALPHABETS_NUM + ALPHABETS_SPECIAL
70 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM
71 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM
71 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
72 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
72 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM
73 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM
73 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM
74 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM
74
75
75 def __init__(self, passwd=''):
76 def __init__(self, passwd=''):
76 self.passwd = passwd
77 self.passwd = passwd
77
78
78 def gen_password(self, length, type_=None):
79 def gen_password(self, length, type_=None):
79 if type_ is None:
80 if type_ is None:
80 type_ = self.ALPHABETS_FULL
81 type_ = self.ALPHABETS_FULL
81 self.passwd = ''.join([random.choice(type_) for _ in xrange(length)])
82 self.passwd = ''.join([random.choice(type_) for _ in xrange(length)])
82 return self.passwd
83 return self.passwd
83
84
84
85
85 class RhodeCodeCrypto(object):
86 class RhodeCodeCrypto(object):
86
87
87 @classmethod
88 @classmethod
88 def hash_string(cls, str_):
89 def hash_string(cls, str_):
89 """
90 """
90 Cryptographic function used for password hashing based on pybcrypt
91 Cryptographic function used for password hashing based on pybcrypt
91 or pycrypto in windows
92 or pycrypto in windows
92
93
93 :param password: password to hash
94 :param password: password to hash
94 """
95 """
95 if is_windows:
96 if is_windows:
96 from hashlib import sha256
97 from hashlib import sha256
97 return sha256(str_).hexdigest()
98 return sha256(str_).hexdigest()
98 elif is_unix:
99 elif is_unix:
99 import bcrypt
100 import bcrypt
100 return bcrypt.hashpw(str_, bcrypt.gensalt(10))
101 return bcrypt.hashpw(str_, bcrypt.gensalt(10))
101 else:
102 else:
102 raise Exception('Unknown or unsupported platform %s' \
103 raise Exception('Unknown or unsupported platform %s' \
103 % __platform__)
104 % __platform__)
104
105
105 @classmethod
106 @classmethod
106 def hash_check(cls, password, hashed):
107 def hash_check(cls, password, hashed):
107 """
108 """
108 Checks matching password with it's hashed value, runs different
109 Checks matching password with it's hashed value, runs different
109 implementation based on platform it runs on
110 implementation based on platform it runs on
110
111
111 :param password: password
112 :param password: password
112 :param hashed: password in hashed form
113 :param hashed: password in hashed form
113 """
114 """
114
115
115 if is_windows:
116 if is_windows:
116 from hashlib import sha256
117 from hashlib import sha256
117 return sha256(password).hexdigest() == hashed
118 return sha256(password).hexdigest() == hashed
118 elif is_unix:
119 elif is_unix:
119 import bcrypt
120 import bcrypt
120 return bcrypt.hashpw(password, hashed) == hashed
121 return bcrypt.hashpw(password, hashed) == hashed
121 else:
122 else:
122 raise Exception('Unknown or unsupported platform %s' \
123 raise Exception('Unknown or unsupported platform %s' \
123 % __platform__)
124 % __platform__)
124
125
125
126
126 def get_crypt_password(password):
127 def get_crypt_password(password):
127 return RhodeCodeCrypto.hash_string(password)
128 return RhodeCodeCrypto.hash_string(password)
128
129
129
130
130 def check_password(password, hashed):
131 def check_password(password, hashed):
131 return RhodeCodeCrypto.hash_check(password, hashed)
132 return RhodeCodeCrypto.hash_check(password, hashed)
132
133
133
134
134 def generate_api_key(str_, salt=None):
135 def generate_api_key(str_, salt=None):
135 """
136 """
136 Generates API KEY from given string
137 Generates API KEY from given string
137
138
138 :param str_:
139 :param str_:
139 :param salt:
140 :param salt:
140 """
141 """
141
142
142 if salt is None:
143 if salt is None:
143 salt = _RandomNameSequence().next()
144 salt = _RandomNameSequence().next()
144
145
145 return hashlib.sha1(str_ + salt).hexdigest()
146 return hashlib.sha1(str_ + salt).hexdigest()
146
147
147
148
148 def authfunc(environ, username, password):
149 def authfunc(environ, username, password):
149 """
150 """
150 Dummy authentication wrapper function used in Mercurial and Git for
151 Dummy authentication wrapper function used in Mercurial and Git for
151 access control.
152 access control.
152
153
153 :param environ: needed only for using in Basic auth
154 :param environ: needed only for using in Basic auth
154 """
155 """
155 return authenticate(username, password)
156 return authenticate(username, password)
156
157
157
158
158 def authenticate(username, password):
159 def authenticate(username, password):
159 """
160 """
160 Authentication function used for access control,
161 Authentication function used for access control,
161 firstly checks for db authentication then if ldap is enabled for ldap
162 firstly checks for db authentication then if ldap is enabled for ldap
162 authentication, also creates ldap user if not in database
163 authentication, also creates ldap user if not in database
163
164
164 :param username: username
165 :param username: username
165 :param password: password
166 :param password: password
166 """
167 """
167
168
168 user_model = UserModel()
169 user_model = UserModel()
169 user = User.get_by_username(username)
170 user = User.get_by_username(username)
170
171
171 log.debug('Authenticating user using RhodeCode account')
172 log.debug('Authenticating user using RhodeCode account')
172 if user is not None and not user.ldap_dn:
173 if user is not None and not user.ldap_dn:
173 if user.active:
174 if user.active:
174 if user.username == 'default' and user.active:
175 if user.username == 'default' and user.active:
175 log.info('user %s authenticated correctly as anonymous user' %
176 log.info('user %s authenticated correctly as anonymous user' %
176 username)
177 username)
177 return True
178 return True
178
179
179 elif user.username == username and check_password(password,
180 elif user.username == username and check_password(password,
180 user.password):
181 user.password):
181 log.info('user %s authenticated correctly' % username)
182 log.info('user %s authenticated correctly' % username)
182 return True
183 return True
183 else:
184 else:
184 log.warning('user %s tried auth but is disabled' % username)
185 log.warning('user %s tried auth but is disabled' % username)
185
186
186 else:
187 else:
187 log.debug('Regular authentication failed')
188 log.debug('Regular authentication failed')
188 user_obj = User.get_by_username(username, case_insensitive=True)
189 user_obj = User.get_by_username(username, case_insensitive=True)
189
190
190 if user_obj is not None and not user_obj.ldap_dn:
191 if user_obj is not None and not user_obj.ldap_dn:
191 log.debug('this user already exists as non ldap')
192 log.debug('this user already exists as non ldap')
192 return False
193 return False
193
194
194 ldap_settings = RhodeCodeSetting.get_ldap_settings()
195 ldap_settings = RhodeCodeSetting.get_ldap_settings()
195 #======================================================================
196 #======================================================================
196 # FALLBACK TO LDAP AUTH IF ENABLE
197 # FALLBACK TO LDAP AUTH IF ENABLE
197 #======================================================================
198 #======================================================================
198 if str2bool(ldap_settings.get('ldap_active')):
199 if str2bool(ldap_settings.get('ldap_active')):
199 log.debug("Authenticating user using ldap")
200 log.debug("Authenticating user using ldap")
200 kwargs = {
201 kwargs = {
201 'server': ldap_settings.get('ldap_host', ''),
202 'server': ldap_settings.get('ldap_host', ''),
202 'base_dn': ldap_settings.get('ldap_base_dn', ''),
203 'base_dn': ldap_settings.get('ldap_base_dn', ''),
203 'port': ldap_settings.get('ldap_port'),
204 'port': ldap_settings.get('ldap_port'),
204 'bind_dn': ldap_settings.get('ldap_dn_user'),
205 'bind_dn': ldap_settings.get('ldap_dn_user'),
205 'bind_pass': ldap_settings.get('ldap_dn_pass'),
206 'bind_pass': ldap_settings.get('ldap_dn_pass'),
206 'tls_kind': ldap_settings.get('ldap_tls_kind'),
207 'tls_kind': ldap_settings.get('ldap_tls_kind'),
207 'tls_reqcert': ldap_settings.get('ldap_tls_reqcert'),
208 'tls_reqcert': ldap_settings.get('ldap_tls_reqcert'),
208 'ldap_filter': ldap_settings.get('ldap_filter'),
209 'ldap_filter': ldap_settings.get('ldap_filter'),
209 'search_scope': ldap_settings.get('ldap_search_scope'),
210 'search_scope': ldap_settings.get('ldap_search_scope'),
210 'attr_login': ldap_settings.get('ldap_attr_login'),
211 'attr_login': ldap_settings.get('ldap_attr_login'),
211 'ldap_version': 3,
212 'ldap_version': 3,
212 }
213 }
213 log.debug('Checking for ldap authentication')
214 log.debug('Checking for ldap authentication')
214 try:
215 try:
215 aldap = AuthLdap(**kwargs)
216 aldap = AuthLdap(**kwargs)
216 (user_dn, ldap_attrs) = aldap.authenticate_ldap(username,
217 (user_dn, ldap_attrs) = aldap.authenticate_ldap(username,
217 password)
218 password)
218 log.debug('Got ldap DN response %s' % user_dn)
219 log.debug('Got ldap DN response %s' % user_dn)
219
220
220 get_ldap_attr = lambda k: ldap_attrs.get(ldap_settings\
221 get_ldap_attr = lambda k: ldap_attrs.get(ldap_settings\
221 .get(k), [''])[0]
222 .get(k), [''])[0]
222
223
223 user_attrs = {
224 user_attrs = {
224 'name': safe_unicode(get_ldap_attr('ldap_attr_firstname')),
225 'name': safe_unicode(get_ldap_attr('ldap_attr_firstname')),
225 'lastname': safe_unicode(get_ldap_attr('ldap_attr_lastname')),
226 'lastname': safe_unicode(get_ldap_attr('ldap_attr_lastname')),
226 'email': get_ldap_attr('ldap_attr_email'),
227 'email': get_ldap_attr('ldap_attr_email'),
227 }
228 }
228
229
229 # don't store LDAP password since we don't need it. Override
230 # don't store LDAP password since we don't need it. Override
230 # with some random generated password
231 # with some random generated password
231 _password = PasswordGenerator().gen_password(length=8)
232 _password = PasswordGenerator().gen_password(length=8)
232 # create this user on the fly if it doesn't exist in rhodecode
233 # create this user on the fly if it doesn't exist in rhodecode
233 # database
234 # database
234 if user_model.create_ldap(username, _password, user_dn,
235 if user_model.create_ldap(username, _password, user_dn,
235 user_attrs):
236 user_attrs):
236 log.info('created new ldap user %s' % username)
237 log.info('created new ldap user %s' % username)
237
238
238 Session().commit()
239 Session().commit()
239 return True
240 return True
240 except (LdapUsernameError, LdapPasswordError,):
241 except (LdapUsernameError, LdapPasswordError,):
241 pass
242 pass
242 except (Exception,):
243 except (Exception,):
243 log.error(traceback.format_exc())
244 log.error(traceback.format_exc())
244 pass
245 pass
245 return False
246 return False
246
247
247
248
248 def login_container_auth(username):
249 def login_container_auth(username):
249 user = User.get_by_username(username)
250 user = User.get_by_username(username)
250 if user is None:
251 if user is None:
251 user_attrs = {
252 user_attrs = {
252 'name': username,
253 'name': username,
253 'lastname': None,
254 'lastname': None,
254 'email': None,
255 'email': None,
255 }
256 }
256 user = UserModel().create_for_container_auth(username, user_attrs)
257 user = UserModel().create_for_container_auth(username, user_attrs)
257 if not user:
258 if not user:
258 return None
259 return None
259 log.info('User %s was created by container authentication' % username)
260 log.info('User %s was created by container authentication' % username)
260
261
261 if not user.active:
262 if not user.active:
262 return None
263 return None
263
264
264 user.update_lastlogin()
265 user.update_lastlogin()
265 Session().commit()
266 Session().commit()
266
267
267 log.debug('User %s is now logged in by container authentication',
268 log.debug('User %s is now logged in by container authentication',
268 user.username)
269 user.username)
269 return user
270 return user
270
271
271
272
272 def get_container_username(environ, config):
273 def get_container_username(environ, config):
273 username = None
274 username = None
274
275
275 if str2bool(config.get('container_auth_enabled', False)):
276 if str2bool(config.get('container_auth_enabled', False)):
276 from paste.httpheaders import REMOTE_USER
277 from paste.httpheaders import REMOTE_USER
277 username = REMOTE_USER(environ)
278 username = REMOTE_USER(environ)
278
279
279 if not username and str2bool(config.get('proxypass_auth_enabled', False)):
280 if not username and str2bool(config.get('proxypass_auth_enabled', False)):
280 username = environ.get('HTTP_X_FORWARDED_USER')
281 username = environ.get('HTTP_X_FORWARDED_USER')
281
282
282 if username:
283 if username:
283 # Removing realm and domain from username
284 # Removing realm and domain from username
284 username = username.partition('@')[0]
285 username = username.partition('@')[0]
285 username = username.rpartition('\\')[2]
286 username = username.rpartition('\\')[2]
286 log.debug('Received username %s from container' % username)
287 log.debug('Received username %s from container' % username)
287
288
288 return username
289 return username
289
290
290
291
291 class CookieStoreWrapper(object):
292 class CookieStoreWrapper(object):
292
293
293 def __init__(self, cookie_store):
294 def __init__(self, cookie_store):
294 self.cookie_store = cookie_store
295 self.cookie_store = cookie_store
295
296
296 def __repr__(self):
297 def __repr__(self):
297 return 'CookieStore<%s>' % (self.cookie_store)
298 return 'CookieStore<%s>' % (self.cookie_store)
298
299
299 def get(self, key, other=None):
300 def get(self, key, other=None):
300 if isinstance(self.cookie_store, dict):
301 if isinstance(self.cookie_store, dict):
301 return self.cookie_store.get(key, other)
302 return self.cookie_store.get(key, other)
302 elif isinstance(self.cookie_store, AuthUser):
303 elif isinstance(self.cookie_store, AuthUser):
303 return self.cookie_store.__dict__.get(key, other)
304 return self.cookie_store.__dict__.get(key, other)
304
305
305
306
306 class AuthUser(object):
307 class AuthUser(object):
307 """
308 """
308 A simple object that handles all attributes of user in RhodeCode
309 A simple object that handles all attributes of user in RhodeCode
309
310
310 It does lookup based on API key,given user, or user present in session
311 It does lookup based on API key,given user, or user present in session
311 Then it fills all required information for such user. It also checks if
312 Then it fills all required information for such user. It also checks if
312 anonymous access is enabled and if so, it returns default user as logged
313 anonymous access is enabled and if so, it returns default user as logged
313 in
314 in
314 """
315 """
315
316
316 def __init__(self, user_id=None, api_key=None, username=None, ip_addr=None):
317 def __init__(self, user_id=None, api_key=None, username=None, ip_addr=None):
317
318
318 self.user_id = user_id
319 self.user_id = user_id
319 self.api_key = None
320 self.api_key = None
320 self.username = username
321 self.username = username
321 self.ip_addr = ip_addr
322 self.ip_addr = ip_addr
322
323
323 self.name = ''
324 self.name = ''
324 self.lastname = ''
325 self.lastname = ''
325 self.email = ''
326 self.email = ''
326 self.is_authenticated = False
327 self.is_authenticated = False
327 self.admin = False
328 self.admin = False
328 self.inherit_default_permissions = False
329 self.inherit_default_permissions = False
329 self.permissions = {}
330 self.permissions = {}
330 self.allowed_ips = set()
331 self._api_key = api_key
331 self._api_key = api_key
332 self.propagate_data()
332 self.propagate_data()
333 self._instance = None
333 self._instance = None
334
334
335 def propagate_data(self):
335 def propagate_data(self):
336 user_model = UserModel()
336 user_model = UserModel()
337 self.anonymous_user = User.get_by_username('default', cache=True)
337 self.anonymous_user = User.get_by_username('default', cache=True)
338 is_user_loaded = False
338 is_user_loaded = False
339
339
340 # try go get user by api key
340 # try go get user by api key
341 if self._api_key and self._api_key != self.anonymous_user.api_key:
341 if self._api_key and self._api_key != self.anonymous_user.api_key:
342 log.debug('Auth User lookup by API KEY %s' % self._api_key)
342 log.debug('Auth User lookup by API KEY %s' % self._api_key)
343 is_user_loaded = user_model.fill_data(self, api_key=self._api_key)
343 is_user_loaded = user_model.fill_data(self, api_key=self._api_key)
344 # lookup by userid
344 # lookup by userid
345 elif (self.user_id is not None and
345 elif (self.user_id is not None and
346 self.user_id != self.anonymous_user.user_id):
346 self.user_id != self.anonymous_user.user_id):
347 log.debug('Auth User lookup by USER ID %s' % self.user_id)
347 log.debug('Auth User lookup by USER ID %s' % self.user_id)
348 is_user_loaded = user_model.fill_data(self, user_id=self.user_id)
348 is_user_loaded = user_model.fill_data(self, user_id=self.user_id)
349 # lookup by username
349 # lookup by username
350 elif self.username and \
350 elif self.username and \
351 str2bool(config.get('container_auth_enabled', False)):
351 str2bool(config.get('container_auth_enabled', False)):
352
352
353 log.debug('Auth User lookup by USER NAME %s' % self.username)
353 log.debug('Auth User lookup by USER NAME %s' % self.username)
354 dbuser = login_container_auth(self.username)
354 dbuser = login_container_auth(self.username)
355 if dbuser is not None:
355 if dbuser is not None:
356 log.debug('filling all attributes to object')
356 log.debug('filling all attributes to object')
357 for k, v in dbuser.get_dict().items():
357 for k, v in dbuser.get_dict().items():
358 setattr(self, k, v)
358 setattr(self, k, v)
359 self.set_authenticated()
359 self.set_authenticated()
360 is_user_loaded = True
360 is_user_loaded = True
361 else:
361 else:
362 log.debug('No data in %s that could been used to log in' % self)
362 log.debug('No data in %s that could been used to log in' % self)
363
363
364 if not is_user_loaded:
364 if not is_user_loaded:
365 # if we cannot authenticate user try anonymous
365 # if we cannot authenticate user try anonymous
366 if self.anonymous_user.active is True:
366 if self.anonymous_user.active is True:
367 user_model.fill_data(self, user_id=self.anonymous_user.user_id)
367 user_model.fill_data(self, user_id=self.anonymous_user.user_id)
368 # then we set this user is logged in
368 # then we set this user is logged in
369 self.is_authenticated = True
369 self.is_authenticated = True
370 else:
370 else:
371 self.user_id = None
371 self.user_id = None
372 self.username = None
372 self.username = None
373 self.is_authenticated = False
373 self.is_authenticated = False
374
374
375 if not self.username:
375 if not self.username:
376 self.username = 'None'
376 self.username = 'None'
377
377
378 log.debug('Auth User is now %s' % self)
378 log.debug('Auth User is now %s' % self)
379 user_model.fill_perms(self)
379 user_model.fill_perms(self)
380 log.debug('Filling Allowed IPs')
381 self.allowed_ips = AuthUser.get_allowed_ips(self.user_id)
382
380
383 @property
381 @property
384 def is_admin(self):
382 def is_admin(self):
385 return self.admin
383 return self.admin
386
384
385 @property
386 def ip_allowed(self):
387 """
388 Checks if ip_addr used in constructor is allowed from defined list of
389 allowed ip_addresses for user
390
391 :returns: boolean, True if ip is in allowed ip range
392 """
393 #check IP
394 allowed_ips = AuthUser.get_allowed_ips(self.user_id, cache=True)
395 if check_ip_access(source_ip=self.ip_addr, allowed_ips=allowed_ips):
396 log.debug('IP:%s is in range of %s' % (self.ip_addr, allowed_ips))
397 return True
398 else:
399 log.info('Access for IP:%s forbidden, '
400 'not in %s' % (self.ip_addr, allowed_ips))
401 return False
402
387 def __repr__(self):
403 def __repr__(self):
388 return "<AuthUser('id:%s:%s|%s')>" % (self.user_id, self.username,
404 return "<AuthUser('id:%s:%s|%s')>" % (self.user_id, self.username,
389 self.is_authenticated)
405 self.is_authenticated)
390
406
391 def set_authenticated(self, authenticated=True):
407 def set_authenticated(self, authenticated=True):
392 if self.user_id != self.anonymous_user.user_id:
408 if self.user_id != self.anonymous_user.user_id:
393 self.is_authenticated = authenticated
409 self.is_authenticated = authenticated
394
410
395 def get_cookie_store(self):
411 def get_cookie_store(self):
396 return {'username': self.username,
412 return {'username': self.username,
397 'user_id': self.user_id,
413 'user_id': self.user_id,
398 'is_authenticated': self.is_authenticated}
414 'is_authenticated': self.is_authenticated}
399
415
400 @classmethod
416 @classmethod
401 def from_cookie_store(cls, cookie_store):
417 def from_cookie_store(cls, cookie_store):
402 """
418 """
403 Creates AuthUser from a cookie store
419 Creates AuthUser from a cookie store
404
420
405 :param cls:
421 :param cls:
406 :param cookie_store:
422 :param cookie_store:
407 """
423 """
408 user_id = cookie_store.get('user_id')
424 user_id = cookie_store.get('user_id')
409 username = cookie_store.get('username')
425 username = cookie_store.get('username')
410 api_key = cookie_store.get('api_key')
426 api_key = cookie_store.get('api_key')
411 return AuthUser(user_id, api_key, username)
427 return AuthUser(user_id, api_key, username)
412
428
413 @classmethod
429 @classmethod
414 def get_allowed_ips(cls, user_id):
430 def get_allowed_ips(cls, user_id, cache=False):
415 _set = set()
431 _set = set()
416 user_ips = UserIpMap.query().filter(UserIpMap.user_id == user_id).all()
432 user_ips = UserIpMap.query().filter(UserIpMap.user_id == user_id)
433 if cache:
434 user_ips = user_ips.options(FromCache("sql_cache_short",
435 "get_user_ips_%s" % user_id))
417 for ip in user_ips:
436 for ip in user_ips:
418 _set.add(ip.ip_addr)
437 _set.add(ip.ip_addr)
419 return _set or set(['0.0.0.0/0'])
438 return _set or set(['0.0.0.0/0'])
420
439
421
440
422 def set_available_permissions(config):
441 def set_available_permissions(config):
423 """
442 """
424 This function will propagate pylons globals with all available defined
443 This function will propagate pylons globals with all available defined
425 permission given in db. We don't want to check each time from db for new
444 permission given in db. We don't want to check each time from db for new
426 permissions since adding a new permission also requires application restart
445 permissions since adding a new permission also requires application restart
427 ie. to decorate new views with the newly created permission
446 ie. to decorate new views with the newly created permission
428
447
429 :param config: current pylons config instance
448 :param config: current pylons config instance
430
449
431 """
450 """
432 log.info('getting information about all available permissions')
451 log.info('getting information about all available permissions')
433 try:
452 try:
434 sa = meta.Session
453 sa = meta.Session
435 all_perms = sa.query(Permission).all()
454 all_perms = sa.query(Permission).all()
436 except Exception:
455 except Exception:
437 pass
456 pass
438 finally:
457 finally:
439 meta.Session.remove()
458 meta.Session.remove()
440
459
441 config['available_permissions'] = [x.permission_name for x in all_perms]
460 config['available_permissions'] = [x.permission_name for x in all_perms]
442
461
443
462
444 #==============================================================================
463 #==============================================================================
445 # CHECK DECORATORS
464 # CHECK DECORATORS
446 #==============================================================================
465 #==============================================================================
447 class LoginRequired(object):
466 class LoginRequired(object):
448 """
467 """
449 Must be logged in to execute this function else
468 Must be logged in to execute this function else
450 redirect to login page
469 redirect to login page
451
470
452 :param api_access: if enabled this checks only for valid auth token
471 :param api_access: if enabled this checks only for valid auth token
453 and grants access based on valid token
472 and grants access based on valid token
454 """
473 """
455
474
456 def __init__(self, api_access=False):
475 def __init__(self, api_access=False):
457 self.api_access = api_access
476 self.api_access = api_access
458
477
459 def __call__(self, func):
478 def __call__(self, func):
460 return decorator(self.__wrapper, func)
479 return decorator(self.__wrapper, func)
461
480
462 def __wrapper(self, func, *fargs, **fkwargs):
481 def __wrapper(self, func, *fargs, **fkwargs):
463 cls = fargs[0]
482 cls = fargs[0]
464 user = cls.rhodecode_user
483 user = cls.rhodecode_user
484 loc = "%s:%s" % (cls.__class__.__name__, func.__name__)
485
486 #check IP
487 ip_access_ok = True
488 if not user.ip_allowed:
489 from rhodecode.lib import helpers as h
490 h.flash(h.literal(_('IP %s not allowed' % (user.ip_addr))),
491 category='warning')
492 ip_access_ok = False
465
493
466 api_access_ok = False
494 api_access_ok = False
467 if self.api_access:
495 if self.api_access:
468 log.debug('Checking API KEY access for %s' % cls)
496 log.debug('Checking API KEY access for %s' % cls)
469 if user.api_key == request.GET.get('api_key'):
497 if user.api_key == request.GET.get('api_key'):
470 api_access_ok = True
498 api_access_ok = True
471 else:
499 else:
472 log.debug("API KEY token not valid")
500 log.debug("API KEY token not valid")
473 loc = "%s:%s" % (cls.__class__.__name__, func.__name__)
501
474 log.debug('Checking if %s is authenticated @ %s' % (user.username, loc))
502 log.debug('Checking if %s is authenticated @ %s' % (user.username, loc))
475 if user.is_authenticated or api_access_ok:
503 if (user.is_authenticated or api_access_ok) and ip_access_ok:
476 reason = 'RegularAuth' if user.is_authenticated else 'APIAuth'
504 reason = 'RegularAuth' if user.is_authenticated else 'APIAuth'
477 log.info('user %s is authenticated and granted access to %s '
505 log.info('user %s is authenticated and granted access to %s '
478 'using %s' % (user.username, loc, reason)
506 'using %s' % (user.username, loc, reason)
479 )
507 )
480 return func(*fargs, **fkwargs)
508 return func(*fargs, **fkwargs)
481 else:
509 else:
482 log.warn('user %s NOT authenticated on func: %s' % (
510 log.warn('user %s NOT authenticated on func: %s' % (
483 user, loc)
511 user, loc)
484 )
512 )
485 p = url.current()
513 p = url.current()
486
514
487 log.debug('redirecting to login page with %s' % p)
515 log.debug('redirecting to login page with %s' % p)
488 return redirect(url('login_home', came_from=p))
516 return redirect(url('login_home', came_from=p))
489
517
490
518
491 class NotAnonymous(object):
519 class NotAnonymous(object):
492 """
520 """
493 Must be logged in to execute this function else
521 Must be logged in to execute this function else
494 redirect to login page"""
522 redirect to login page"""
495
523
496 def __call__(self, func):
524 def __call__(self, func):
497 return decorator(self.__wrapper, func)
525 return decorator(self.__wrapper, func)
498
526
499 def __wrapper(self, func, *fargs, **fkwargs):
527 def __wrapper(self, func, *fargs, **fkwargs):
500 cls = fargs[0]
528 cls = fargs[0]
501 self.user = cls.rhodecode_user
529 self.user = cls.rhodecode_user
502
530
503 log.debug('Checking if user is not anonymous @%s' % cls)
531 log.debug('Checking if user is not anonymous @%s' % cls)
504
532
505 anonymous = self.user.username == 'default'
533 anonymous = self.user.username == 'default'
506
534
507 if anonymous:
535 if anonymous:
508 p = url.current()
536 p = url.current()
509
537
510 import rhodecode.lib.helpers as h
538 import rhodecode.lib.helpers as h
511 h.flash(_('You need to be a registered user to '
539 h.flash(_('You need to be a registered user to '
512 'perform this action'),
540 'perform this action'),
513 category='warning')
541 category='warning')
514 return redirect(url('login_home', came_from=p))
542 return redirect(url('login_home', came_from=p))
515 else:
543 else:
516 return func(*fargs, **fkwargs)
544 return func(*fargs, **fkwargs)
517
545
518
546
519 class PermsDecorator(object):
547 class PermsDecorator(object):
520 """Base class for controller decorators"""
548 """Base class for controller decorators"""
521
549
522 def __init__(self, *required_perms):
550 def __init__(self, *required_perms):
523 available_perms = config['available_permissions']
551 available_perms = config['available_permissions']
524 for perm in required_perms:
552 for perm in required_perms:
525 if perm not in available_perms:
553 if perm not in available_perms:
526 raise Exception("'%s' permission is not defined" % perm)
554 raise Exception("'%s' permission is not defined" % perm)
527 self.required_perms = set(required_perms)
555 self.required_perms = set(required_perms)
528 self.user_perms = None
556 self.user_perms = None
529
557
530 def __call__(self, func):
558 def __call__(self, func):
531 return decorator(self.__wrapper, func)
559 return decorator(self.__wrapper, func)
532
560
533 def __wrapper(self, func, *fargs, **fkwargs):
561 def __wrapper(self, func, *fargs, **fkwargs):
534 cls = fargs[0]
562 cls = fargs[0]
535 self.user = cls.rhodecode_user
563 self.user = cls.rhodecode_user
536 self.user_perms = self.user.permissions
564 self.user_perms = self.user.permissions
537 log.debug('checking %s permissions %s for %s %s',
565 log.debug('checking %s permissions %s for %s %s',
538 self.__class__.__name__, self.required_perms, cls, self.user)
566 self.__class__.__name__, self.required_perms, cls, self.user)
539
567
540 if self.check_permissions():
568 if self.check_permissions():
541 log.debug('Permission granted for %s %s' % (cls, self.user))
569 log.debug('Permission granted for %s %s' % (cls, self.user))
542 return func(*fargs, **fkwargs)
570 return func(*fargs, **fkwargs)
543
571
544 else:
572 else:
545 log.debug('Permission denied for %s %s' % (cls, self.user))
573 log.debug('Permission denied for %s %s' % (cls, self.user))
546 anonymous = self.user.username == 'default'
574 anonymous = self.user.username == 'default'
547
575
548 if anonymous:
576 if anonymous:
549 p = url.current()
577 p = url.current()
550
578
551 import rhodecode.lib.helpers as h
579 import rhodecode.lib.helpers as h
552 h.flash(_('You need to be a signed in to '
580 h.flash(_('You need to be a signed in to '
553 'view this page'),
581 'view this page'),
554 category='warning')
582 category='warning')
555 return redirect(url('login_home', came_from=p))
583 return redirect(url('login_home', came_from=p))
556
584
557 else:
585 else:
558 # redirect with forbidden ret code
586 # redirect with forbidden ret code
559 return abort(403)
587 return abort(403)
560
588
561 def check_permissions(self):
589 def check_permissions(self):
562 """Dummy function for overriding"""
590 """Dummy function for overriding"""
563 raise Exception('You have to write this function in child class')
591 raise Exception('You have to write this function in child class')
564
592
565
593
566 class HasPermissionAllDecorator(PermsDecorator):
594 class HasPermissionAllDecorator(PermsDecorator):
567 """
595 """
568 Checks for access permission for all given predicates. All of them
596 Checks for access permission for all given predicates. All of them
569 have to be meet in order to fulfill the request
597 have to be meet in order to fulfill the request
570 """
598 """
571
599
572 def check_permissions(self):
600 def check_permissions(self):
573 if self.required_perms.issubset(self.user_perms.get('global')):
601 if self.required_perms.issubset(self.user_perms.get('global')):
574 return True
602 return True
575 return False
603 return False
576
604
577
605
578 class HasPermissionAnyDecorator(PermsDecorator):
606 class HasPermissionAnyDecorator(PermsDecorator):
579 """
607 """
580 Checks for access permission for any of given predicates. In order to
608 Checks for access permission for any of given predicates. In order to
581 fulfill the request any of predicates must be meet
609 fulfill the request any of predicates must be meet
582 """
610 """
583
611
584 def check_permissions(self):
612 def check_permissions(self):
585 if self.required_perms.intersection(self.user_perms.get('global')):
613 if self.required_perms.intersection(self.user_perms.get('global')):
586 return True
614 return True
587 return False
615 return False
588
616
589
617
590 class HasRepoPermissionAllDecorator(PermsDecorator):
618 class HasRepoPermissionAllDecorator(PermsDecorator):
591 """
619 """
592 Checks for access permission for all given predicates for specific
620 Checks for access permission for all given predicates for specific
593 repository. All of them have to be meet in order to fulfill the request
621 repository. All of them have to be meet in order to fulfill the request
594 """
622 """
595
623
596 def check_permissions(self):
624 def check_permissions(self):
597 repo_name = get_repo_slug(request)
625 repo_name = get_repo_slug(request)
598 try:
626 try:
599 user_perms = set([self.user_perms['repositories'][repo_name]])
627 user_perms = set([self.user_perms['repositories'][repo_name]])
600 except KeyError:
628 except KeyError:
601 return False
629 return False
602 if self.required_perms.issubset(user_perms):
630 if self.required_perms.issubset(user_perms):
603 return True
631 return True
604 return False
632 return False
605
633
606
634
607 class HasRepoPermissionAnyDecorator(PermsDecorator):
635 class HasRepoPermissionAnyDecorator(PermsDecorator):
608 """
636 """
609 Checks for access permission for any of given predicates for specific
637 Checks for access permission for any of given predicates for specific
610 repository. In order to fulfill the request any of predicates must be meet
638 repository. In order to fulfill the request any of predicates must be meet
611 """
639 """
612
640
613 def check_permissions(self):
641 def check_permissions(self):
614 repo_name = get_repo_slug(request)
642 repo_name = get_repo_slug(request)
615
643
616 try:
644 try:
617 user_perms = set([self.user_perms['repositories'][repo_name]])
645 user_perms = set([self.user_perms['repositories'][repo_name]])
618 except KeyError:
646 except KeyError:
619 return False
647 return False
620
648
621 if self.required_perms.intersection(user_perms):
649 if self.required_perms.intersection(user_perms):
622 return True
650 return True
623 return False
651 return False
624
652
625
653
626 class HasReposGroupPermissionAllDecorator(PermsDecorator):
654 class HasReposGroupPermissionAllDecorator(PermsDecorator):
627 """
655 """
628 Checks for access permission for all given predicates for specific
656 Checks for access permission for all given predicates for specific
629 repository. All of them have to be meet in order to fulfill the request
657 repository. All of them have to be meet in order to fulfill the request
630 """
658 """
631
659
632 def check_permissions(self):
660 def check_permissions(self):
633 group_name = get_repos_group_slug(request)
661 group_name = get_repos_group_slug(request)
634 try:
662 try:
635 user_perms = set([self.user_perms['repositories_groups'][group_name]])
663 user_perms = set([self.user_perms['repositories_groups'][group_name]])
636 except KeyError:
664 except KeyError:
637 return False
665 return False
638 if self.required_perms.issubset(user_perms):
666 if self.required_perms.issubset(user_perms):
639 return True
667 return True
640 return False
668 return False
641
669
642
670
643 class HasReposGroupPermissionAnyDecorator(PermsDecorator):
671 class HasReposGroupPermissionAnyDecorator(PermsDecorator):
644 """
672 """
645 Checks for access permission for any of given predicates for specific
673 Checks for access permission for any of given predicates for specific
646 repository. In order to fulfill the request any of predicates must be meet
674 repository. In order to fulfill the request any of predicates must be meet
647 """
675 """
648
676
649 def check_permissions(self):
677 def check_permissions(self):
650 group_name = get_repos_group_slug(request)
678 group_name = get_repos_group_slug(request)
651
679
652 try:
680 try:
653 user_perms = set([self.user_perms['repositories_groups'][group_name]])
681 user_perms = set([self.user_perms['repositories_groups'][group_name]])
654 except KeyError:
682 except KeyError:
655 return False
683 return False
656 if self.required_perms.intersection(user_perms):
684 if self.required_perms.intersection(user_perms):
657 return True
685 return True
658 return False
686 return False
659
687
660
688
661 #==============================================================================
689 #==============================================================================
662 # CHECK FUNCTIONS
690 # CHECK FUNCTIONS
663 #==============================================================================
691 #==============================================================================
664 class PermsFunction(object):
692 class PermsFunction(object):
665 """Base function for other check functions"""
693 """Base function for other check functions"""
666
694
667 def __init__(self, *perms):
695 def __init__(self, *perms):
668 available_perms = config['available_permissions']
696 available_perms = config['available_permissions']
669
697
670 for perm in perms:
698 for perm in perms:
671 if perm not in available_perms:
699 if perm not in available_perms:
672 raise Exception("'%s' permission is not defined" % perm)
700 raise Exception("'%s' permission is not defined" % perm)
673 self.required_perms = set(perms)
701 self.required_perms = set(perms)
674 self.user_perms = None
702 self.user_perms = None
675 self.repo_name = None
703 self.repo_name = None
676 self.group_name = None
704 self.group_name = None
677
705
678 def __call__(self, check_Location=''):
706 def __call__(self, check_Location=''):
679 user = request.user
707 user = request.user
680 cls_name = self.__class__.__name__
708 cls_name = self.__class__.__name__
681 check_scope = {
709 check_scope = {
682 'HasPermissionAll': '',
710 'HasPermissionAll': '',
683 'HasPermissionAny': '',
711 'HasPermissionAny': '',
684 'HasRepoPermissionAll': 'repo:%s' % self.repo_name,
712 'HasRepoPermissionAll': 'repo:%s' % self.repo_name,
685 'HasRepoPermissionAny': 'repo:%s' % self.repo_name,
713 'HasRepoPermissionAny': 'repo:%s' % self.repo_name,
686 'HasReposGroupPermissionAll': 'group:%s' % self.group_name,
714 'HasReposGroupPermissionAll': 'group:%s' % self.group_name,
687 'HasReposGroupPermissionAny': 'group:%s' % self.group_name,
715 'HasReposGroupPermissionAny': 'group:%s' % self.group_name,
688 }.get(cls_name, '?')
716 }.get(cls_name, '?')
689 log.debug('checking cls:%s %s usr:%s %s @ %s', cls_name,
717 log.debug('checking cls:%s %s usr:%s %s @ %s', cls_name,
690 self.required_perms, user, check_scope,
718 self.required_perms, user, check_scope,
691 check_Location or 'unspecified location')
719 check_Location or 'unspecified location')
692 if not user:
720 if not user:
693 log.debug('Empty request user')
721 log.debug('Empty request user')
694 return False
722 return False
695 self.user_perms = user.permissions
723 self.user_perms = user.permissions
696 if self.check_permissions():
724 if self.check_permissions():
697 log.debug('Permission to %s granted for user: %s @ %s', self.repo_name, user,
725 log.debug('Permission to %s granted for user: %s @ %s', self.repo_name, user,
698 check_Location or 'unspecified location')
726 check_Location or 'unspecified location')
699 return True
727 return True
700
728
701 else:
729 else:
702 log.debug('Permission to %s denied for user: %s @ %s', self.repo_name, user,
730 log.debug('Permission to %s denied for user: %s @ %s', self.repo_name, user,
703 check_Location or 'unspecified location')
731 check_Location or 'unspecified location')
704 return False
732 return False
705
733
706 def check_permissions(self):
734 def check_permissions(self):
707 """Dummy function for overriding"""
735 """Dummy function for overriding"""
708 raise Exception('You have to write this function in child class')
736 raise Exception('You have to write this function in child class')
709
737
710
738
711 class HasPermissionAll(PermsFunction):
739 class HasPermissionAll(PermsFunction):
712 def check_permissions(self):
740 def check_permissions(self):
713 if self.required_perms.issubset(self.user_perms.get('global')):
741 if self.required_perms.issubset(self.user_perms.get('global')):
714 return True
742 return True
715 return False
743 return False
716
744
717
745
718 class HasPermissionAny(PermsFunction):
746 class HasPermissionAny(PermsFunction):
719 def check_permissions(self):
747 def check_permissions(self):
720 if self.required_perms.intersection(self.user_perms.get('global')):
748 if self.required_perms.intersection(self.user_perms.get('global')):
721 return True
749 return True
722 return False
750 return False
723
751
724
752
725 class HasRepoPermissionAll(PermsFunction):
753 class HasRepoPermissionAll(PermsFunction):
726 def __call__(self, repo_name=None, check_Location=''):
754 def __call__(self, repo_name=None, check_Location=''):
727 self.repo_name = repo_name
755 self.repo_name = repo_name
728 return super(HasRepoPermissionAll, self).__call__(check_Location)
756 return super(HasRepoPermissionAll, self).__call__(check_Location)
729
757
730 def check_permissions(self):
758 def check_permissions(self):
731 if not self.repo_name:
759 if not self.repo_name:
732 self.repo_name = get_repo_slug(request)
760 self.repo_name = get_repo_slug(request)
733
761
734 try:
762 try:
735 self._user_perms = set(
763 self._user_perms = set(
736 [self.user_perms['repositories'][self.repo_name]]
764 [self.user_perms['repositories'][self.repo_name]]
737 )
765 )
738 except KeyError:
766 except KeyError:
739 return False
767 return False
740 if self.required_perms.issubset(self._user_perms):
768 if self.required_perms.issubset(self._user_perms):
741 return True
769 return True
742 return False
770 return False
743
771
744
772
745 class HasRepoPermissionAny(PermsFunction):
773 class HasRepoPermissionAny(PermsFunction):
746 def __call__(self, repo_name=None, check_Location=''):
774 def __call__(self, repo_name=None, check_Location=''):
747 self.repo_name = repo_name
775 self.repo_name = repo_name
748 return super(HasRepoPermissionAny, self).__call__(check_Location)
776 return super(HasRepoPermissionAny, self).__call__(check_Location)
749
777
750 def check_permissions(self):
778 def check_permissions(self):
751 if not self.repo_name:
779 if not self.repo_name:
752 self.repo_name = get_repo_slug(request)
780 self.repo_name = get_repo_slug(request)
753
781
754 try:
782 try:
755 self._user_perms = set(
783 self._user_perms = set(
756 [self.user_perms['repositories'][self.repo_name]]
784 [self.user_perms['repositories'][self.repo_name]]
757 )
785 )
758 except KeyError:
786 except KeyError:
759 return False
787 return False
760 if self.required_perms.intersection(self._user_perms):
788 if self.required_perms.intersection(self._user_perms):
761 return True
789 return True
762 return False
790 return False
763
791
764
792
765 class HasReposGroupPermissionAny(PermsFunction):
793 class HasReposGroupPermissionAny(PermsFunction):
766 def __call__(self, group_name=None, check_Location=''):
794 def __call__(self, group_name=None, check_Location=''):
767 self.group_name = group_name
795 self.group_name = group_name
768 return super(HasReposGroupPermissionAny, self).__call__(check_Location)
796 return super(HasReposGroupPermissionAny, self).__call__(check_Location)
769
797
770 def check_permissions(self):
798 def check_permissions(self):
771 try:
799 try:
772 self._user_perms = set(
800 self._user_perms = set(
773 [self.user_perms['repositories_groups'][self.group_name]]
801 [self.user_perms['repositories_groups'][self.group_name]]
774 )
802 )
775 except KeyError:
803 except KeyError:
776 return False
804 return False
777 if self.required_perms.intersection(self._user_perms):
805 if self.required_perms.intersection(self._user_perms):
778 return True
806 return True
779 return False
807 return False
780
808
781
809
782 class HasReposGroupPermissionAll(PermsFunction):
810 class HasReposGroupPermissionAll(PermsFunction):
783 def __call__(self, group_name=None, check_Location=''):
811 def __call__(self, group_name=None, check_Location=''):
784 self.group_name = group_name
812 self.group_name = group_name
785 return super(HasReposGroupPermissionAll, self).__call__(check_Location)
813 return super(HasReposGroupPermissionAll, self).__call__(check_Location)
786
814
787 def check_permissions(self):
815 def check_permissions(self):
788 try:
816 try:
789 self._user_perms = set(
817 self._user_perms = set(
790 [self.user_perms['repositories_groups'][self.group_name]]
818 [self.user_perms['repositories_groups'][self.group_name]]
791 )
819 )
792 except KeyError:
820 except KeyError:
793 return False
821 return False
794 if self.required_perms.issubset(self._user_perms):
822 if self.required_perms.issubset(self._user_perms):
795 return True
823 return True
796 return False
824 return False
797
825
798
826
799 #==============================================================================
827 #==============================================================================
800 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
828 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
801 #==============================================================================
829 #==============================================================================
802 class HasPermissionAnyMiddleware(object):
830 class HasPermissionAnyMiddleware(object):
803 def __init__(self, *perms):
831 def __init__(self, *perms):
804 self.required_perms = set(perms)
832 self.required_perms = set(perms)
805
833
806 def __call__(self, user, repo_name):
834 def __call__(self, user, repo_name):
807 # repo_name MUST be unicode, since we handle keys in permission
835 # repo_name MUST be unicode, since we handle keys in permission
808 # dict by unicode
836 # dict by unicode
809 repo_name = safe_unicode(repo_name)
837 repo_name = safe_unicode(repo_name)
810 usr = AuthUser(user.user_id)
838 usr = AuthUser(user.user_id)
811 try:
839 try:
812 self.user_perms = set([usr.permissions['repositories'][repo_name]])
840 self.user_perms = set([usr.permissions['repositories'][repo_name]])
813 except Exception:
841 except Exception:
814 log.error('Exception while accessing permissions %s' %
842 log.error('Exception while accessing permissions %s' %
815 traceback.format_exc())
843 traceback.format_exc())
816 self.user_perms = set()
844 self.user_perms = set()
817 self.username = user.username
845 self.username = user.username
818 self.repo_name = repo_name
846 self.repo_name = repo_name
819 return self.check_permissions()
847 return self.check_permissions()
820
848
821 def check_permissions(self):
849 def check_permissions(self):
822 log.debug('checking VCS protocol '
850 log.debug('checking VCS protocol '
823 'permissions %s for user:%s repository:%s', self.user_perms,
851 'permissions %s for user:%s repository:%s', self.user_perms,
824 self.username, self.repo_name)
852 self.username, self.repo_name)
825 if self.required_perms.intersection(self.user_perms):
853 if self.required_perms.intersection(self.user_perms):
826 log.debug('permission granted for user:%s on repo:%s' % (
854 log.debug('permission granted for user:%s on repo:%s' % (
827 self.username, self.repo_name
855 self.username, self.repo_name
828 )
856 )
829 )
857 )
830 return True
858 return True
831 log.debug('permission denied for user:%s on repo:%s' % (
859 log.debug('permission denied for user:%s on repo:%s' % (
832 self.username, self.repo_name
860 self.username, self.repo_name
833 )
861 )
834 )
862 )
835 return False
863 return False
836
864
837
865
838 def check_ip_access(source_ip, allowed_ips=None):
866 def check_ip_access(source_ip, allowed_ips=None):
839 """
867 """
840 Checks if source_ip is a subnet of any of allowed_ips.
868 Checks if source_ip is a subnet of any of allowed_ips.
841
869
842 :param source_ip:
870 :param source_ip:
843 :param allowed_ips: list of allowed ips together with mask
871 :param allowed_ips: list of allowed ips together with mask
844 """
872 """
845 from rhodecode.lib import ipaddr
873 from rhodecode.lib import ipaddr
846 log.debug('checking if ip:%s is subnet of %s' % (source_ip, allowed_ips))
874 log.debug('checking if ip:%s is subnet of %s' % (source_ip, allowed_ips))
847 if isinstance(allowed_ips, (tuple, list, set)):
875 if isinstance(allowed_ips, (tuple, list, set)):
848 for ip in allowed_ips:
876 for ip in allowed_ips:
849 if ipaddr.IPAddress(source_ip) in ipaddr.IPNetwork(ip):
877 if ipaddr.IPAddress(source_ip) in ipaddr.IPNetwork(ip):
850 return True
878 return True
851 return False
879 return False
@@ -1,334 +1,332 b''
1 """The base Controller API
1 """The base Controller API
2
2
3 Provides the BaseController class for subclassing.
3 Provides the BaseController class for subclassing.
4 """
4 """
5 import logging
5 import logging
6 import time
6 import time
7 import traceback
7 import traceback
8
8
9 from paste.auth.basic import AuthBasicAuthenticator
9 from paste.auth.basic import AuthBasicAuthenticator
10 from paste.httpexceptions import HTTPUnauthorized, HTTPForbidden
10 from paste.httpexceptions import HTTPUnauthorized, HTTPForbidden
11 from paste.httpheaders import WWW_AUTHENTICATE, AUTHORIZATION
11 from paste.httpheaders import WWW_AUTHENTICATE, AUTHORIZATION
12
12
13 from pylons import config, tmpl_context as c, request, session, url
13 from pylons import config, tmpl_context as c, request, session, url
14 from pylons.controllers import WSGIController
14 from pylons.controllers import WSGIController
15 from pylons.controllers.util import redirect
15 from pylons.controllers.util import redirect
16 from pylons.templating import render_mako as render
16 from pylons.templating import render_mako as render
17
17
18 from rhodecode import __version__, BACKENDS
18 from rhodecode import __version__, BACKENDS
19
19
20 from rhodecode.lib.utils2 import str2bool, safe_unicode, AttributeDict,\
20 from rhodecode.lib.utils2 import str2bool, safe_unicode, AttributeDict,\
21 safe_str, safe_int
21 safe_str, safe_int
22 from rhodecode.lib.auth import AuthUser, get_container_username, authfunc,\
22 from rhodecode.lib.auth import AuthUser, get_container_username, authfunc,\
23 HasPermissionAnyMiddleware, CookieStoreWrapper, check_ip_access
23 HasPermissionAnyMiddleware, CookieStoreWrapper
24 from rhodecode.lib.utils import get_repo_slug, invalidate_cache
24 from rhodecode.lib.utils import get_repo_slug, invalidate_cache
25 from rhodecode.model import meta
25 from rhodecode.model import meta
26
26
27 from rhodecode.model.db import Repository, RhodeCodeUi, User, RhodeCodeSetting
27 from rhodecode.model.db import Repository, RhodeCodeUi, User, RhodeCodeSetting
28 from rhodecode.model.notification import NotificationModel
28 from rhodecode.model.notification import NotificationModel
29 from rhodecode.model.scm import ScmModel
29 from rhodecode.model.scm import ScmModel
30 from rhodecode.model.meta import Session
30 from rhodecode.model.meta import Session
31
31
32 log = logging.getLogger(__name__)
32 log = logging.getLogger(__name__)
33
33
34
34
35 def _get_ip_addr(environ):
35 def _get_ip_addr(environ):
36 proxy_key = 'HTTP_X_REAL_IP'
36 proxy_key = 'HTTP_X_REAL_IP'
37 proxy_key2 = 'HTTP_X_FORWARDED_FOR'
37 proxy_key2 = 'HTTP_X_FORWARDED_FOR'
38 def_key = 'REMOTE_ADDR'
38 def_key = 'REMOTE_ADDR'
39
39
40 ip = environ.get(proxy_key2)
40 ip = environ.get(proxy_key2)
41 if ip:
41 if ip:
42 return ip
42 return ip
43
43
44 ip = environ.get(proxy_key)
44 ip = environ.get(proxy_key)
45
45
46 if ip:
46 if ip:
47 return ip
47 return ip
48
48
49 ip = environ.get(def_key, '0.0.0.0')
49 ip = environ.get(def_key, '0.0.0.0')
50 return ip
50 return ip
51
51
52
52
53 def _get_access_path(environ):
53 def _get_access_path(environ):
54 path = environ.get('PATH_INFO')
54 path = environ.get('PATH_INFO')
55 org_req = environ.get('pylons.original_request')
55 org_req = environ.get('pylons.original_request')
56 if org_req:
56 if org_req:
57 path = org_req.environ.get('PATH_INFO')
57 path = org_req.environ.get('PATH_INFO')
58 return path
58 return path
59
59
60
60
61 class BasicAuth(AuthBasicAuthenticator):
61 class BasicAuth(AuthBasicAuthenticator):
62
62
63 def __init__(self, realm, authfunc, auth_http_code=None):
63 def __init__(self, realm, authfunc, auth_http_code=None):
64 self.realm = realm
64 self.realm = realm
65 self.authfunc = authfunc
65 self.authfunc = authfunc
66 self._rc_auth_http_code = auth_http_code
66 self._rc_auth_http_code = auth_http_code
67
67
68 def build_authentication(self):
68 def build_authentication(self):
69 head = WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm)
69 head = WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm)
70 if self._rc_auth_http_code and self._rc_auth_http_code == '403':
70 if self._rc_auth_http_code and self._rc_auth_http_code == '403':
71 # return 403 if alternative http return code is specified in
71 # return 403 if alternative http return code is specified in
72 # RhodeCode config
72 # RhodeCode config
73 return HTTPForbidden(headers=head)
73 return HTTPForbidden(headers=head)
74 return HTTPUnauthorized(headers=head)
74 return HTTPUnauthorized(headers=head)
75
75
76 def authenticate(self, environ):
76 def authenticate(self, environ):
77 authorization = AUTHORIZATION(environ)
77 authorization = AUTHORIZATION(environ)
78 if not authorization:
78 if not authorization:
79 return self.build_authentication()
79 return self.build_authentication()
80 (authmeth, auth) = authorization.split(' ', 1)
80 (authmeth, auth) = authorization.split(' ', 1)
81 if 'basic' != authmeth.lower():
81 if 'basic' != authmeth.lower():
82 return self.build_authentication()
82 return self.build_authentication()
83 auth = auth.strip().decode('base64')
83 auth = auth.strip().decode('base64')
84 _parts = auth.split(':', 1)
84 _parts = auth.split(':', 1)
85 if len(_parts) == 2:
85 if len(_parts) == 2:
86 username, password = _parts
86 username, password = _parts
87 if self.authfunc(environ, username, password):
87 if self.authfunc(environ, username, password):
88 return username
88 return username
89 return self.build_authentication()
89 return self.build_authentication()
90
90
91 __call__ = authenticate
91 __call__ = authenticate
92
92
93
93
94 class BaseVCSController(object):
94 class BaseVCSController(object):
95
95
96 def __init__(self, application, config):
96 def __init__(self, application, config):
97 self.application = application
97 self.application = application
98 self.config = config
98 self.config = config
99 # base path of repo locations
99 # base path of repo locations
100 self.basepath = self.config['base_path']
100 self.basepath = self.config['base_path']
101 #authenticate this mercurial request using authfunc
101 #authenticate this mercurial request using authfunc
102 self.authenticate = BasicAuth('', authfunc,
102 self.authenticate = BasicAuth('', authfunc,
103 config.get('auth_ret_code'))
103 config.get('auth_ret_code'))
104 self.ip_addr = '0.0.0.0'
104 self.ip_addr = '0.0.0.0'
105
105
106 def _handle_request(self, environ, start_response):
106 def _handle_request(self, environ, start_response):
107 raise NotImplementedError()
107 raise NotImplementedError()
108
108
109 def _get_by_id(self, repo_name):
109 def _get_by_id(self, repo_name):
110 """
110 """
111 Get's a special pattern _<ID> from clone url and tries to replace it
111 Get's a special pattern _<ID> from clone url and tries to replace it
112 with a repository_name for support of _<ID> non changable urls
112 with a repository_name for support of _<ID> non changable urls
113
113
114 :param repo_name:
114 :param repo_name:
115 """
115 """
116 try:
116 try:
117 data = repo_name.split('/')
117 data = repo_name.split('/')
118 if len(data) >= 2:
118 if len(data) >= 2:
119 by_id = data[1].split('_')
119 by_id = data[1].split('_')
120 if len(by_id) == 2 and by_id[1].isdigit():
120 if len(by_id) == 2 and by_id[1].isdigit():
121 _repo_name = Repository.get(by_id[1]).repo_name
121 _repo_name = Repository.get(by_id[1]).repo_name
122 data[1] = _repo_name
122 data[1] = _repo_name
123 except:
123 except:
124 log.debug('Failed to extract repo_name from id %s' % (
124 log.debug('Failed to extract repo_name from id %s' % (
125 traceback.format_exc()
125 traceback.format_exc()
126 )
126 )
127 )
127 )
128
128
129 return '/'.join(data)
129 return '/'.join(data)
130
130
131 def _invalidate_cache(self, repo_name):
131 def _invalidate_cache(self, repo_name):
132 """
132 """
133 Set's cache for this repository for invalidation on next access
133 Set's cache for this repository for invalidation on next access
134
134
135 :param repo_name: full repo name, also a cache key
135 :param repo_name: full repo name, also a cache key
136 """
136 """
137 invalidate_cache('get_repo_cached_%s' % repo_name)
137 invalidate_cache('get_repo_cached_%s' % repo_name)
138
138
139 def _check_permission(self, action, user, repo_name, ip_addr=None):
139 def _check_permission(self, action, user, repo_name, ip_addr=None):
140 """
140 """
141 Checks permissions using action (push/pull) user and repository
141 Checks permissions using action (push/pull) user and repository
142 name
142 name
143
143
144 :param action: push or pull action
144 :param action: push or pull action
145 :param user: user instance
145 :param user: user instance
146 :param repo_name: repository name
146 :param repo_name: repository name
147 """
147 """
148 #check IP
148 #check IP
149 allowed_ips = AuthUser.get_allowed_ips(user.user_id)
149 authuser = AuthUser(user_id=user.user_id, ip_addr=ip_addr)
150 if check_ip_access(source_ip=ip_addr, allowed_ips=allowed_ips) is False:
150 if not authuser.ip_allowed:
151 log.info('Access for IP:%s forbidden, '
152 'not in %s' % (ip_addr, allowed_ips))
153 return False
151 return False
154 else:
152 else:
155 log.info('Access for IP:%s allowed' % (ip_addr))
153 log.info('Access for IP:%s allowed' % (ip_addr))
156 if action == 'push':
154 if action == 'push':
157 if not HasPermissionAnyMiddleware('repository.write',
155 if not HasPermissionAnyMiddleware('repository.write',
158 'repository.admin')(user,
156 'repository.admin')(user,
159 repo_name):
157 repo_name):
160 return False
158 return False
161
159
162 else:
160 else:
163 #any other action need at least read permission
161 #any other action need at least read permission
164 if not HasPermissionAnyMiddleware('repository.read',
162 if not HasPermissionAnyMiddleware('repository.read',
165 'repository.write',
163 'repository.write',
166 'repository.admin')(user,
164 'repository.admin')(user,
167 repo_name):
165 repo_name):
168 return False
166 return False
169
167
170 return True
168 return True
171
169
172 def _get_ip_addr(self, environ):
170 def _get_ip_addr(self, environ):
173 return _get_ip_addr(environ)
171 return _get_ip_addr(environ)
174
172
175 def _check_ssl(self, environ, start_response):
173 def _check_ssl(self, environ, start_response):
176 """
174 """
177 Checks the SSL check flag and returns False if SSL is not present
175 Checks the SSL check flag and returns False if SSL is not present
178 and required True otherwise
176 and required True otherwise
179 """
177 """
180 org_proto = environ['wsgi._org_proto']
178 org_proto = environ['wsgi._org_proto']
181 #check if we have SSL required ! if not it's a bad request !
179 #check if we have SSL required ! if not it's a bad request !
182 require_ssl = str2bool(RhodeCodeUi.get_by_key('push_ssl').ui_value)
180 require_ssl = str2bool(RhodeCodeUi.get_by_key('push_ssl').ui_value)
183 if require_ssl and org_proto == 'http':
181 if require_ssl and org_proto == 'http':
184 log.debug('proto is %s and SSL is required BAD REQUEST !'
182 log.debug('proto is %s and SSL is required BAD REQUEST !'
185 % org_proto)
183 % org_proto)
186 return False
184 return False
187 return True
185 return True
188
186
189 def _check_locking_state(self, environ, action, repo, user_id):
187 def _check_locking_state(self, environ, action, repo, user_id):
190 """
188 """
191 Checks locking on this repository, if locking is enabled and lock is
189 Checks locking on this repository, if locking is enabled and lock is
192 present returns a tuple of make_lock, locked, locked_by.
190 present returns a tuple of make_lock, locked, locked_by.
193 make_lock can have 3 states None (do nothing) True, make lock
191 make_lock can have 3 states None (do nothing) True, make lock
194 False release lock, This value is later propagated to hooks, which
192 False release lock, This value is later propagated to hooks, which
195 do the locking. Think about this as signals passed to hooks what to do.
193 do the locking. Think about this as signals passed to hooks what to do.
196
194
197 """
195 """
198 locked = False # defines that locked error should be thrown to user
196 locked = False # defines that locked error should be thrown to user
199 make_lock = None
197 make_lock = None
200 repo = Repository.get_by_repo_name(repo)
198 repo = Repository.get_by_repo_name(repo)
201 user = User.get(user_id)
199 user = User.get(user_id)
202
200
203 # this is kind of hacky, but due to how mercurial handles client-server
201 # this is kind of hacky, but due to how mercurial handles client-server
204 # server see all operation on changeset; bookmarks, phases and
202 # server see all operation on changeset; bookmarks, phases and
205 # obsolescence marker in different transaction, we don't want to check
203 # obsolescence marker in different transaction, we don't want to check
206 # locking on those
204 # locking on those
207 obsolete_call = environ['QUERY_STRING'] in ['cmd=listkeys',]
205 obsolete_call = environ['QUERY_STRING'] in ['cmd=listkeys',]
208 locked_by = repo.locked
206 locked_by = repo.locked
209 if repo and repo.enable_locking and not obsolete_call:
207 if repo and repo.enable_locking and not obsolete_call:
210 if action == 'push':
208 if action == 'push':
211 #check if it's already locked !, if it is compare users
209 #check if it's already locked !, if it is compare users
212 user_id, _date = repo.locked
210 user_id, _date = repo.locked
213 if user.user_id == user_id:
211 if user.user_id == user_id:
214 log.debug('Got push from user %s, now unlocking' % (user))
212 log.debug('Got push from user %s, now unlocking' % (user))
215 # unlock if we have push from user who locked
213 # unlock if we have push from user who locked
216 make_lock = False
214 make_lock = False
217 else:
215 else:
218 # we're not the same user who locked, ban with 423 !
216 # we're not the same user who locked, ban with 423 !
219 locked = True
217 locked = True
220 if action == 'pull':
218 if action == 'pull':
221 if repo.locked[0] and repo.locked[1]:
219 if repo.locked[0] and repo.locked[1]:
222 locked = True
220 locked = True
223 else:
221 else:
224 log.debug('Setting lock on repo %s by %s' % (repo, user))
222 log.debug('Setting lock on repo %s by %s' % (repo, user))
225 make_lock = True
223 make_lock = True
226
224
227 else:
225 else:
228 log.debug('Repository %s do not have locking enabled' % (repo))
226 log.debug('Repository %s do not have locking enabled' % (repo))
229 log.debug('FINAL locking values make_lock:%s,locked:%s,locked_by:%s'
227 log.debug('FINAL locking values make_lock:%s,locked:%s,locked_by:%s'
230 % (make_lock, locked, locked_by))
228 % (make_lock, locked, locked_by))
231 return make_lock, locked, locked_by
229 return make_lock, locked, locked_by
232
230
233 def __call__(self, environ, start_response):
231 def __call__(self, environ, start_response):
234 start = time.time()
232 start = time.time()
235 try:
233 try:
236 return self._handle_request(environ, start_response)
234 return self._handle_request(environ, start_response)
237 finally:
235 finally:
238 log = logging.getLogger('rhodecode.' + self.__class__.__name__)
236 log = logging.getLogger('rhodecode.' + self.__class__.__name__)
239 log.debug('Request time: %.3fs' % (time.time() - start))
237 log.debug('Request time: %.3fs' % (time.time() - start))
240 meta.Session.remove()
238 meta.Session.remove()
241
239
242
240
243 class BaseController(WSGIController):
241 class BaseController(WSGIController):
244
242
245 def __before__(self):
243 def __before__(self):
246 """
244 """
247 __before__ is called before controller methods and after __call__
245 __before__ is called before controller methods and after __call__
248 """
246 """
249 c.rhodecode_version = __version__
247 c.rhodecode_version = __version__
250 c.rhodecode_instanceid = config.get('instance_id')
248 c.rhodecode_instanceid = config.get('instance_id')
251 c.rhodecode_name = config.get('rhodecode_title')
249 c.rhodecode_name = config.get('rhodecode_title')
252 c.use_gravatar = str2bool(config.get('use_gravatar'))
250 c.use_gravatar = str2bool(config.get('use_gravatar'))
253 c.ga_code = config.get('rhodecode_ga_code')
251 c.ga_code = config.get('rhodecode_ga_code')
254 # Visual options
252 # Visual options
255 c.visual = AttributeDict({})
253 c.visual = AttributeDict({})
256 rc_config = RhodeCodeSetting.get_app_settings()
254 rc_config = RhodeCodeSetting.get_app_settings()
257
255
258 c.visual.show_public_icon = str2bool(rc_config.get('rhodecode_show_public_icon'))
256 c.visual.show_public_icon = str2bool(rc_config.get('rhodecode_show_public_icon'))
259 c.visual.show_private_icon = str2bool(rc_config.get('rhodecode_show_private_icon'))
257 c.visual.show_private_icon = str2bool(rc_config.get('rhodecode_show_private_icon'))
260 c.visual.stylify_metatags = str2bool(rc_config.get('rhodecode_stylify_metatags'))
258 c.visual.stylify_metatags = str2bool(rc_config.get('rhodecode_stylify_metatags'))
261 c.visual.lightweight_dashboard = str2bool(rc_config.get('rhodecode_lightweight_dashboard'))
259 c.visual.lightweight_dashboard = str2bool(rc_config.get('rhodecode_lightweight_dashboard'))
262 c.visual.lightweight_dashboard_items = safe_int(config.get('dashboard_items', 100))
260 c.visual.lightweight_dashboard_items = safe_int(config.get('dashboard_items', 100))
263
261
264 c.repo_name = get_repo_slug(request)
262 c.repo_name = get_repo_slug(request)
265 c.backends = BACKENDS.keys()
263 c.backends = BACKENDS.keys()
266 c.unread_notifications = NotificationModel()\
264 c.unread_notifications = NotificationModel()\
267 .get_unread_cnt_for_user(c.rhodecode_user.user_id)
265 .get_unread_cnt_for_user(c.rhodecode_user.user_id)
268 self.cut_off_limit = int(config.get('cut_off_limit'))
266 self.cut_off_limit = int(config.get('cut_off_limit'))
269
267
270 self.sa = meta.Session
268 self.sa = meta.Session
271 self.scm_model = ScmModel(self.sa)
269 self.scm_model = ScmModel(self.sa)
272
270
273 def __call__(self, environ, start_response):
271 def __call__(self, environ, start_response):
274 """Invoke the Controller"""
272 """Invoke the Controller"""
275 # WSGIController.__call__ dispatches to the Controller method
273 # WSGIController.__call__ dispatches to the Controller method
276 # the request is routed to. This routing information is
274 # the request is routed to. This routing information is
277 # available in environ['pylons.routes_dict']
275 # available in environ['pylons.routes_dict']
278 start = time.time()
276 start = time.time()
279 try:
277 try:
280 self.ip_addr = _get_ip_addr(environ)
278 self.ip_addr = _get_ip_addr(environ)
281 # make sure that we update permissions each time we call controller
279 # make sure that we update permissions each time we call controller
282 api_key = request.GET.get('api_key')
280 api_key = request.GET.get('api_key')
283 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
281 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
284 user_id = cookie_store.get('user_id', None)
282 user_id = cookie_store.get('user_id', None)
285 username = get_container_username(environ, config)
283 username = get_container_username(environ, config)
286 auth_user = AuthUser(user_id, api_key, username, self.ip_addr)
284 auth_user = AuthUser(user_id, api_key, username, self.ip_addr)
287 request.user = auth_user
285 request.user = auth_user
288 self.rhodecode_user = c.rhodecode_user = auth_user
286 self.rhodecode_user = c.rhodecode_user = auth_user
289 if not self.rhodecode_user.is_authenticated and \
287 if not self.rhodecode_user.is_authenticated and \
290 self.rhodecode_user.user_id is not None:
288 self.rhodecode_user.user_id is not None:
291 self.rhodecode_user.set_authenticated(
289 self.rhodecode_user.set_authenticated(
292 cookie_store.get('is_authenticated')
290 cookie_store.get('is_authenticated')
293 )
291 )
294 log.info('IP: %s User: %s accessed %s' % (
292 log.info('IP: %s User: %s accessed %s' % (
295 self.ip_addr, auth_user, safe_unicode(_get_access_path(environ)))
293 self.ip_addr, auth_user, safe_unicode(_get_access_path(environ)))
296 )
294 )
297 return WSGIController.__call__(self, environ, start_response)
295 return WSGIController.__call__(self, environ, start_response)
298 finally:
296 finally:
299 log.info('IP: %s Request to %s time: %.3fs' % (
297 log.info('IP: %s Request to %s time: %.3fs' % (
300 _get_ip_addr(environ),
298 _get_ip_addr(environ),
301 safe_unicode(_get_access_path(environ)), time.time() - start)
299 safe_unicode(_get_access_path(environ)), time.time() - start)
302 )
300 )
303 meta.Session.remove()
301 meta.Session.remove()
304
302
305
303
306 class BaseRepoController(BaseController):
304 class BaseRepoController(BaseController):
307 """
305 """
308 Base class for controllers responsible for loading all needed data for
306 Base class for controllers responsible for loading all needed data for
309 repository loaded items are
307 repository loaded items are
310
308
311 c.rhodecode_repo: instance of scm repository
309 c.rhodecode_repo: instance of scm repository
312 c.rhodecode_db_repo: instance of db
310 c.rhodecode_db_repo: instance of db
313 c.repository_followers: number of followers
311 c.repository_followers: number of followers
314 c.repository_forks: number of forks
312 c.repository_forks: number of forks
315 """
313 """
316
314
317 def __before__(self):
315 def __before__(self):
318 super(BaseRepoController, self).__before__()
316 super(BaseRepoController, self).__before__()
319 if c.repo_name:
317 if c.repo_name:
320
318
321 dbr = c.rhodecode_db_repo = Repository.get_by_repo_name(c.repo_name)
319 dbr = c.rhodecode_db_repo = Repository.get_by_repo_name(c.repo_name)
322 c.rhodecode_repo = c.rhodecode_db_repo.scm_instance
320 c.rhodecode_repo = c.rhodecode_db_repo.scm_instance
323 # update last change according to VCS data
321 # update last change according to VCS data
324 dbr.update_last_change(c.rhodecode_repo.last_change)
322 dbr.update_last_change(c.rhodecode_repo.last_change)
325 if c.rhodecode_repo is None:
323 if c.rhodecode_repo is None:
326 log.error('%s this repository is present in database but it '
324 log.error('%s this repository is present in database but it '
327 'cannot be created as an scm instance', c.repo_name)
325 'cannot be created as an scm instance', c.repo_name)
328
326
329 redirect(url('home'))
327 redirect(url('home'))
330
328
331 # some globals counter for menu
329 # some globals counter for menu
332 c.repository_followers = self.scm_model.get_followers(dbr)
330 c.repository_followers = self.scm_model.get_followers(dbr)
333 c.repository_forks = self.scm_model.get_forks(dbr)
331 c.repository_forks = self.scm_model.get_forks(dbr)
334 c.repository_pull_requests = self.scm_model.get_pull_requests(dbr)
332 c.repository_pull_requests = self.scm_model.get_pull_requests(dbr)
@@ -1,1871 +1,1872 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.db
3 rhodecode.model.db
4 ~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~
5
5
6 Database Models for RhodeCode
6 Database Models for RhodeCode
7
7
8 :created_on: Apr 08, 2010
8 :created_on: Apr 08, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import os
26 import os
27 import logging
27 import logging
28 import datetime
28 import datetime
29 import traceback
29 import traceback
30 import hashlib
30 import hashlib
31 import time
31 import time
32 from collections import defaultdict
32 from collections import defaultdict
33
33
34 from sqlalchemy import *
34 from sqlalchemy import *
35 from sqlalchemy.ext.hybrid import hybrid_property
35 from sqlalchemy.ext.hybrid import hybrid_property
36 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
36 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
37 from sqlalchemy.exc import DatabaseError
37 from sqlalchemy.exc import DatabaseError
38 from beaker.cache import cache_region, region_invalidate
38 from beaker.cache import cache_region, region_invalidate
39 from webob.exc import HTTPNotFound
39 from webob.exc import HTTPNotFound
40
40
41 from pylons.i18n.translation import lazy_ugettext as _
41 from pylons.i18n.translation import lazy_ugettext as _
42
42
43 from rhodecode.lib.vcs import get_backend
43 from rhodecode.lib.vcs import get_backend
44 from rhodecode.lib.vcs.utils.helpers import get_scm
44 from rhodecode.lib.vcs.utils.helpers import get_scm
45 from rhodecode.lib.vcs.exceptions import VCSError
45 from rhodecode.lib.vcs.exceptions import VCSError
46 from rhodecode.lib.vcs.utils.lazy import LazyProperty
46 from rhodecode.lib.vcs.utils.lazy import LazyProperty
47
47
48 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
48 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
49 safe_unicode, remove_suffix, remove_prefix
49 safe_unicode, remove_suffix, remove_prefix
50 from rhodecode.lib.compat import json
50 from rhodecode.lib.compat import json
51 from rhodecode.lib.caching_query import FromCache
51 from rhodecode.lib.caching_query import FromCache
52
52
53 from rhodecode.model.meta import Base, Session
53 from rhodecode.model.meta import Base, Session
54
54
55 URL_SEP = '/'
55 URL_SEP = '/'
56 log = logging.getLogger(__name__)
56 log = logging.getLogger(__name__)
57
57
58 #==============================================================================
58 #==============================================================================
59 # BASE CLASSES
59 # BASE CLASSES
60 #==============================================================================
60 #==============================================================================
61
61
62 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
62 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
63
63
64
64
65 class BaseModel(object):
65 class BaseModel(object):
66 """
66 """
67 Base Model for all classess
67 Base Model for all classess
68 """
68 """
69
69
70 @classmethod
70 @classmethod
71 def _get_keys(cls):
71 def _get_keys(cls):
72 """return column names for this model """
72 """return column names for this model """
73 return class_mapper(cls).c.keys()
73 return class_mapper(cls).c.keys()
74
74
75 def get_dict(self):
75 def get_dict(self):
76 """
76 """
77 return dict with keys and values corresponding
77 return dict with keys and values corresponding
78 to this model data """
78 to this model data """
79
79
80 d = {}
80 d = {}
81 for k in self._get_keys():
81 for k in self._get_keys():
82 d[k] = getattr(self, k)
82 d[k] = getattr(self, k)
83
83
84 # also use __json__() if present to get additional fields
84 # also use __json__() if present to get additional fields
85 _json_attr = getattr(self, '__json__', None)
85 _json_attr = getattr(self, '__json__', None)
86 if _json_attr:
86 if _json_attr:
87 # update with attributes from __json__
87 # update with attributes from __json__
88 if callable(_json_attr):
88 if callable(_json_attr):
89 _json_attr = _json_attr()
89 _json_attr = _json_attr()
90 for k, val in _json_attr.iteritems():
90 for k, val in _json_attr.iteritems():
91 d[k] = val
91 d[k] = val
92 return d
92 return d
93
93
94 def get_appstruct(self):
94 def get_appstruct(self):
95 """return list with keys and values tupples corresponding
95 """return list with keys and values tupples corresponding
96 to this model data """
96 to this model data """
97
97
98 l = []
98 l = []
99 for k in self._get_keys():
99 for k in self._get_keys():
100 l.append((k, getattr(self, k),))
100 l.append((k, getattr(self, k),))
101 return l
101 return l
102
102
103 def populate_obj(self, populate_dict):
103 def populate_obj(self, populate_dict):
104 """populate model with data from given populate_dict"""
104 """populate model with data from given populate_dict"""
105
105
106 for k in self._get_keys():
106 for k in self._get_keys():
107 if k in populate_dict:
107 if k in populate_dict:
108 setattr(self, k, populate_dict[k])
108 setattr(self, k, populate_dict[k])
109
109
110 @classmethod
110 @classmethod
111 def query(cls):
111 def query(cls):
112 return Session().query(cls)
112 return Session().query(cls)
113
113
114 @classmethod
114 @classmethod
115 def get(cls, id_):
115 def get(cls, id_):
116 if id_:
116 if id_:
117 return cls.query().get(id_)
117 return cls.query().get(id_)
118
118
119 @classmethod
119 @classmethod
120 def get_or_404(cls, id_):
120 def get_or_404(cls, id_):
121 try:
121 try:
122 id_ = int(id_)
122 id_ = int(id_)
123 except (TypeError, ValueError):
123 except (TypeError, ValueError):
124 raise HTTPNotFound
124 raise HTTPNotFound
125
125
126 res = cls.query().get(id_)
126 res = cls.query().get(id_)
127 if not res:
127 if not res:
128 raise HTTPNotFound
128 raise HTTPNotFound
129 return res
129 return res
130
130
131 @classmethod
131 @classmethod
132 def getAll(cls):
132 def getAll(cls):
133 return cls.query().all()
133 return cls.query().all()
134
134
135 @classmethod
135 @classmethod
136 def delete(cls, id_):
136 def delete(cls, id_):
137 obj = cls.query().get(id_)
137 obj = cls.query().get(id_)
138 Session().delete(obj)
138 Session().delete(obj)
139
139
140 def __repr__(self):
140 def __repr__(self):
141 if hasattr(self, '__unicode__'):
141 if hasattr(self, '__unicode__'):
142 # python repr needs to return str
142 # python repr needs to return str
143 return safe_str(self.__unicode__())
143 return safe_str(self.__unicode__())
144 return '<DB:%s>' % (self.__class__.__name__)
144 return '<DB:%s>' % (self.__class__.__name__)
145
145
146
146
147 class RhodeCodeSetting(Base, BaseModel):
147 class RhodeCodeSetting(Base, BaseModel):
148 __tablename__ = 'rhodecode_settings'
148 __tablename__ = 'rhodecode_settings'
149 __table_args__ = (
149 __table_args__ = (
150 UniqueConstraint('app_settings_name'),
150 UniqueConstraint('app_settings_name'),
151 {'extend_existing': True, 'mysql_engine': 'InnoDB',
151 {'extend_existing': True, 'mysql_engine': 'InnoDB',
152 'mysql_charset': 'utf8'}
152 'mysql_charset': 'utf8'}
153 )
153 )
154 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
154 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
155 app_settings_name = Column("app_settings_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
155 app_settings_name = Column("app_settings_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
156 _app_settings_value = Column("app_settings_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
156 _app_settings_value = Column("app_settings_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
157
157
158 def __init__(self, k='', v=''):
158 def __init__(self, k='', v=''):
159 self.app_settings_name = k
159 self.app_settings_name = k
160 self.app_settings_value = v
160 self.app_settings_value = v
161
161
162 @validates('_app_settings_value')
162 @validates('_app_settings_value')
163 def validate_settings_value(self, key, val):
163 def validate_settings_value(self, key, val):
164 assert type(val) == unicode
164 assert type(val) == unicode
165 return val
165 return val
166
166
167 @hybrid_property
167 @hybrid_property
168 def app_settings_value(self):
168 def app_settings_value(self):
169 v = self._app_settings_value
169 v = self._app_settings_value
170 if self.app_settings_name in ["ldap_active",
170 if self.app_settings_name in ["ldap_active",
171 "default_repo_enable_statistics",
171 "default_repo_enable_statistics",
172 "default_repo_enable_locking",
172 "default_repo_enable_locking",
173 "default_repo_private",
173 "default_repo_private",
174 "default_repo_enable_downloads"]:
174 "default_repo_enable_downloads"]:
175 v = str2bool(v)
175 v = str2bool(v)
176 return v
176 return v
177
177
178 @app_settings_value.setter
178 @app_settings_value.setter
179 def app_settings_value(self, val):
179 def app_settings_value(self, val):
180 """
180 """
181 Setter that will always make sure we use unicode in app_settings_value
181 Setter that will always make sure we use unicode in app_settings_value
182
182
183 :param val:
183 :param val:
184 """
184 """
185 self._app_settings_value = safe_unicode(val)
185 self._app_settings_value = safe_unicode(val)
186
186
187 def __unicode__(self):
187 def __unicode__(self):
188 return u"<%s('%s:%s')>" % (
188 return u"<%s('%s:%s')>" % (
189 self.__class__.__name__,
189 self.__class__.__name__,
190 self.app_settings_name, self.app_settings_value
190 self.app_settings_name, self.app_settings_value
191 )
191 )
192
192
193 @classmethod
193 @classmethod
194 def get_by_name(cls, key):
194 def get_by_name(cls, key):
195 return cls.query()\
195 return cls.query()\
196 .filter(cls.app_settings_name == key).scalar()
196 .filter(cls.app_settings_name == key).scalar()
197
197
198 @classmethod
198 @classmethod
199 def get_by_name_or_create(cls, key):
199 def get_by_name_or_create(cls, key):
200 res = cls.get_by_name(key)
200 res = cls.get_by_name(key)
201 if not res:
201 if not res:
202 res = cls(key)
202 res = cls(key)
203 return res
203 return res
204
204
205 @classmethod
205 @classmethod
206 def get_app_settings(cls, cache=False):
206 def get_app_settings(cls, cache=False):
207
207
208 ret = cls.query()
208 ret = cls.query()
209
209
210 if cache:
210 if cache:
211 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
211 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
212
212
213 if not ret:
213 if not ret:
214 raise Exception('Could not get application settings !')
214 raise Exception('Could not get application settings !')
215 settings = {}
215 settings = {}
216 for each in ret:
216 for each in ret:
217 settings['rhodecode_' + each.app_settings_name] = \
217 settings['rhodecode_' + each.app_settings_name] = \
218 each.app_settings_value
218 each.app_settings_value
219
219
220 return settings
220 return settings
221
221
222 @classmethod
222 @classmethod
223 def get_ldap_settings(cls, cache=False):
223 def get_ldap_settings(cls, cache=False):
224 ret = cls.query()\
224 ret = cls.query()\
225 .filter(cls.app_settings_name.startswith('ldap_')).all()
225 .filter(cls.app_settings_name.startswith('ldap_')).all()
226 fd = {}
226 fd = {}
227 for row in ret:
227 for row in ret:
228 fd.update({row.app_settings_name: row.app_settings_value})
228 fd.update({row.app_settings_name: row.app_settings_value})
229
229
230 return fd
230 return fd
231
231
232 @classmethod
232 @classmethod
233 def get_default_repo_settings(cls, cache=False, strip_prefix=False):
233 def get_default_repo_settings(cls, cache=False, strip_prefix=False):
234 ret = cls.query()\
234 ret = cls.query()\
235 .filter(cls.app_settings_name.startswith('default_')).all()
235 .filter(cls.app_settings_name.startswith('default_')).all()
236 fd = {}
236 fd = {}
237 for row in ret:
237 for row in ret:
238 key = row.app_settings_name
238 key = row.app_settings_name
239 if strip_prefix:
239 if strip_prefix:
240 key = remove_prefix(key, prefix='default_')
240 key = remove_prefix(key, prefix='default_')
241 fd.update({key: row.app_settings_value})
241 fd.update({key: row.app_settings_value})
242
242
243 return fd
243 return fd
244
244
245
245
246 class RhodeCodeUi(Base, BaseModel):
246 class RhodeCodeUi(Base, BaseModel):
247 __tablename__ = 'rhodecode_ui'
247 __tablename__ = 'rhodecode_ui'
248 __table_args__ = (
248 __table_args__ = (
249 UniqueConstraint('ui_key'),
249 UniqueConstraint('ui_key'),
250 {'extend_existing': True, 'mysql_engine': 'InnoDB',
250 {'extend_existing': True, 'mysql_engine': 'InnoDB',
251 'mysql_charset': 'utf8'}
251 'mysql_charset': 'utf8'}
252 )
252 )
253
253
254 HOOK_UPDATE = 'changegroup.update'
254 HOOK_UPDATE = 'changegroup.update'
255 HOOK_REPO_SIZE = 'changegroup.repo_size'
255 HOOK_REPO_SIZE = 'changegroup.repo_size'
256 HOOK_PUSH = 'changegroup.push_logger'
256 HOOK_PUSH = 'changegroup.push_logger'
257 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
257 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
258 HOOK_PULL = 'outgoing.pull_logger'
258 HOOK_PULL = 'outgoing.pull_logger'
259 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
259 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
260
260
261 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
261 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
262 ui_section = Column("ui_section", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
262 ui_section = Column("ui_section", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
263 ui_key = Column("ui_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
263 ui_key = Column("ui_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
264 ui_value = Column("ui_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
264 ui_value = Column("ui_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
265 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
265 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
266
266
267 @classmethod
267 @classmethod
268 def get_by_key(cls, key):
268 def get_by_key(cls, key):
269 return cls.query().filter(cls.ui_key == key).scalar()
269 return cls.query().filter(cls.ui_key == key).scalar()
270
270
271 @classmethod
271 @classmethod
272 def get_builtin_hooks(cls):
272 def get_builtin_hooks(cls):
273 q = cls.query()
273 q = cls.query()
274 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
274 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
275 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
275 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
276 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
276 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
277 return q.all()
277 return q.all()
278
278
279 @classmethod
279 @classmethod
280 def get_custom_hooks(cls):
280 def get_custom_hooks(cls):
281 q = cls.query()
281 q = cls.query()
282 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
282 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
283 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
283 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
284 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
284 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
285 q = q.filter(cls.ui_section == 'hooks')
285 q = q.filter(cls.ui_section == 'hooks')
286 return q.all()
286 return q.all()
287
287
288 @classmethod
288 @classmethod
289 def get_repos_location(cls):
289 def get_repos_location(cls):
290 return cls.get_by_key('/').ui_value
290 return cls.get_by_key('/').ui_value
291
291
292 @classmethod
292 @classmethod
293 def create_or_update_hook(cls, key, val):
293 def create_or_update_hook(cls, key, val):
294 new_ui = cls.get_by_key(key) or cls()
294 new_ui = cls.get_by_key(key) or cls()
295 new_ui.ui_section = 'hooks'
295 new_ui.ui_section = 'hooks'
296 new_ui.ui_active = True
296 new_ui.ui_active = True
297 new_ui.ui_key = key
297 new_ui.ui_key = key
298 new_ui.ui_value = val
298 new_ui.ui_value = val
299
299
300 Session().add(new_ui)
300 Session().add(new_ui)
301
301
302 def __repr__(self):
302 def __repr__(self):
303 return '<DB:%s[%s:%s]>' % (self.__class__.__name__, self.ui_key,
303 return '<DB:%s[%s:%s]>' % (self.__class__.__name__, self.ui_key,
304 self.ui_value)
304 self.ui_value)
305
305
306
306
307 class User(Base, BaseModel):
307 class User(Base, BaseModel):
308 __tablename__ = 'users'
308 __tablename__ = 'users'
309 __table_args__ = (
309 __table_args__ = (
310 UniqueConstraint('username'), UniqueConstraint('email'),
310 UniqueConstraint('username'), UniqueConstraint('email'),
311 Index('u_username_idx', 'username'),
311 Index('u_username_idx', 'username'),
312 Index('u_email_idx', 'email'),
312 Index('u_email_idx', 'email'),
313 {'extend_existing': True, 'mysql_engine': 'InnoDB',
313 {'extend_existing': True, 'mysql_engine': 'InnoDB',
314 'mysql_charset': 'utf8'}
314 'mysql_charset': 'utf8'}
315 )
315 )
316 DEFAULT_USER = 'default'
316 DEFAULT_USER = 'default'
317 DEFAULT_PERMISSIONS = [
317 DEFAULT_PERMISSIONS = [
318 'hg.register.manual_activate', 'hg.create.repository',
318 'hg.register.manual_activate', 'hg.create.repository',
319 'hg.fork.repository', 'repository.read', 'group.read'
319 'hg.fork.repository', 'repository.read', 'group.read'
320 ]
320 ]
321 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
321 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
322 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
322 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
323 password = Column("password", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
323 password = Column("password", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
324 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
324 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
325 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
325 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
326 name = Column("firstname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
326 name = Column("firstname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
327 lastname = Column("lastname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
327 lastname = Column("lastname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
328 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
328 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
329 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
329 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
330 ldap_dn = Column("ldap_dn", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
330 ldap_dn = Column("ldap_dn", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
331 api_key = Column("api_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
331 api_key = Column("api_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
332 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
332 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
333
333
334 user_log = relationship('UserLog')
334 user_log = relationship('UserLog')
335 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
335 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
336
336
337 repositories = relationship('Repository')
337 repositories = relationship('Repository')
338 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
338 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
339 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
339 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
340
340
341 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
341 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
342 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
342 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
343
343
344 group_member = relationship('UsersGroupMember', cascade='all')
344 group_member = relationship('UsersGroupMember', cascade='all')
345
345
346 notifications = relationship('UserNotification', cascade='all')
346 notifications = relationship('UserNotification', cascade='all')
347 # notifications assigned to this user
347 # notifications assigned to this user
348 user_created_notifications = relationship('Notification', cascade='all')
348 user_created_notifications = relationship('Notification', cascade='all')
349 # comments created by this user
349 # comments created by this user
350 user_comments = relationship('ChangesetComment', cascade='all')
350 user_comments = relationship('ChangesetComment', cascade='all')
351 #extra emails for this user
351 #extra emails for this user
352 user_emails = relationship('UserEmailMap', cascade='all')
352 user_emails = relationship('UserEmailMap', cascade='all')
353
353
354 @hybrid_property
354 @hybrid_property
355 def email(self):
355 def email(self):
356 return self._email
356 return self._email
357
357
358 @email.setter
358 @email.setter
359 def email(self, val):
359 def email(self, val):
360 self._email = val.lower() if val else None
360 self._email = val.lower() if val else None
361
361
362 @property
362 @property
363 def firstname(self):
363 def firstname(self):
364 # alias for future
364 # alias for future
365 return self.name
365 return self.name
366
366
367 @property
367 @property
368 def emails(self):
368 def emails(self):
369 other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
369 other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
370 return [self.email] + [x.email for x in other]
370 return [self.email] + [x.email for x in other]
371
371
372 @property
372 @property
373 def ip_addresses(self):
373 def ip_addresses(self):
374 ret = UserIpMap.query().filter(UserIpMap.user == self).all()
374 ret = UserIpMap.query().filter(UserIpMap.user == self).all()
375 return [x.ip_addr for x in ret]
375 return [x.ip_addr for x in ret]
376
376
377 @property
377 @property
378 def username_and_name(self):
378 def username_and_name(self):
379 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
379 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
380
380
381 @property
381 @property
382 def full_name(self):
382 def full_name(self):
383 return '%s %s' % (self.firstname, self.lastname)
383 return '%s %s' % (self.firstname, self.lastname)
384
384
385 @property
385 @property
386 def full_name_or_username(self):
386 def full_name_or_username(self):
387 return ('%s %s' % (self.firstname, self.lastname)
387 return ('%s %s' % (self.firstname, self.lastname)
388 if (self.firstname and self.lastname) else self.username)
388 if (self.firstname and self.lastname) else self.username)
389
389
390 @property
390 @property
391 def full_contact(self):
391 def full_contact(self):
392 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
392 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
393
393
394 @property
394 @property
395 def short_contact(self):
395 def short_contact(self):
396 return '%s %s' % (self.firstname, self.lastname)
396 return '%s %s' % (self.firstname, self.lastname)
397
397
398 @property
398 @property
399 def is_admin(self):
399 def is_admin(self):
400 return self.admin
400 return self.admin
401
401
402 def __unicode__(self):
402 def __unicode__(self):
403 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
403 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
404 self.user_id, self.username)
404 self.user_id, self.username)
405
405
406 @classmethod
406 @classmethod
407 def get_by_username(cls, username, case_insensitive=False, cache=False):
407 def get_by_username(cls, username, case_insensitive=False, cache=False):
408 if case_insensitive:
408 if case_insensitive:
409 q = cls.query().filter(cls.username.ilike(username))
409 q = cls.query().filter(cls.username.ilike(username))
410 else:
410 else:
411 q = cls.query().filter(cls.username == username)
411 q = cls.query().filter(cls.username == username)
412
412
413 if cache:
413 if cache:
414 q = q.options(FromCache(
414 q = q.options(FromCache(
415 "sql_cache_short",
415 "sql_cache_short",
416 "get_user_%s" % _hash_key(username)
416 "get_user_%s" % _hash_key(username)
417 )
417 )
418 )
418 )
419 return q.scalar()
419 return q.scalar()
420
420
421 @classmethod
421 @classmethod
422 def get_by_api_key(cls, api_key, cache=False):
422 def get_by_api_key(cls, api_key, cache=False):
423 q = cls.query().filter(cls.api_key == api_key)
423 q = cls.query().filter(cls.api_key == api_key)
424
424
425 if cache:
425 if cache:
426 q = q.options(FromCache("sql_cache_short",
426 q = q.options(FromCache("sql_cache_short",
427 "get_api_key_%s" % api_key))
427 "get_api_key_%s" % api_key))
428 return q.scalar()
428 return q.scalar()
429
429
430 @classmethod
430 @classmethod
431 def get_by_email(cls, email, case_insensitive=False, cache=False):
431 def get_by_email(cls, email, case_insensitive=False, cache=False):
432 if case_insensitive:
432 if case_insensitive:
433 q = cls.query().filter(cls.email.ilike(email))
433 q = cls.query().filter(cls.email.ilike(email))
434 else:
434 else:
435 q = cls.query().filter(cls.email == email)
435 q = cls.query().filter(cls.email == email)
436
436
437 if cache:
437 if cache:
438 q = q.options(FromCache("sql_cache_short",
438 q = q.options(FromCache("sql_cache_short",
439 "get_email_key_%s" % email))
439 "get_email_key_%s" % email))
440
440
441 ret = q.scalar()
441 ret = q.scalar()
442 if ret is None:
442 if ret is None:
443 q = UserEmailMap.query()
443 q = UserEmailMap.query()
444 # try fetching in alternate email map
444 # try fetching in alternate email map
445 if case_insensitive:
445 if case_insensitive:
446 q = q.filter(UserEmailMap.email.ilike(email))
446 q = q.filter(UserEmailMap.email.ilike(email))
447 else:
447 else:
448 q = q.filter(UserEmailMap.email == email)
448 q = q.filter(UserEmailMap.email == email)
449 q = q.options(joinedload(UserEmailMap.user))
449 q = q.options(joinedload(UserEmailMap.user))
450 if cache:
450 if cache:
451 q = q.options(FromCache("sql_cache_short",
451 q = q.options(FromCache("sql_cache_short",
452 "get_email_map_key_%s" % email))
452 "get_email_map_key_%s" % email))
453 ret = getattr(q.scalar(), 'user', None)
453 ret = getattr(q.scalar(), 'user', None)
454
454
455 return ret
455 return ret
456
456
457 def update_lastlogin(self):
457 def update_lastlogin(self):
458 """Update user lastlogin"""
458 """Update user lastlogin"""
459 self.last_login = datetime.datetime.now()
459 self.last_login = datetime.datetime.now()
460 Session().add(self)
460 Session().add(self)
461 log.debug('updated user %s lastlogin' % self.username)
461 log.debug('updated user %s lastlogin' % self.username)
462
462
463 def get_api_data(self):
463 def get_api_data(self):
464 """
464 """
465 Common function for generating user related data for API
465 Common function for generating user related data for API
466 """
466 """
467 user = self
467 user = self
468 data = dict(
468 data = dict(
469 user_id=user.user_id,
469 user_id=user.user_id,
470 username=user.username,
470 username=user.username,
471 firstname=user.name,
471 firstname=user.name,
472 lastname=user.lastname,
472 lastname=user.lastname,
473 email=user.email,
473 email=user.email,
474 emails=user.emails,
474 emails=user.emails,
475 api_key=user.api_key,
475 api_key=user.api_key,
476 active=user.active,
476 active=user.active,
477 admin=user.admin,
477 admin=user.admin,
478 ldap_dn=user.ldap_dn,
478 ldap_dn=user.ldap_dn,
479 last_login=user.last_login,
479 last_login=user.last_login,
480 ip_addresses=user.ip_addresses
480 ip_addresses=user.ip_addresses
481 )
481 )
482 return data
482 return data
483
483
484 def __json__(self):
484 def __json__(self):
485 data = dict(
485 data = dict(
486 full_name=self.full_name,
486 full_name=self.full_name,
487 full_name_or_username=self.full_name_or_username,
487 full_name_or_username=self.full_name_or_username,
488 short_contact=self.short_contact,
488 short_contact=self.short_contact,
489 full_contact=self.full_contact
489 full_contact=self.full_contact
490 )
490 )
491 data.update(self.get_api_data())
491 data.update(self.get_api_data())
492 return data
492 return data
493
493
494
494
495 class UserEmailMap(Base, BaseModel):
495 class UserEmailMap(Base, BaseModel):
496 __tablename__ = 'user_email_map'
496 __tablename__ = 'user_email_map'
497 __table_args__ = (
497 __table_args__ = (
498 Index('uem_email_idx', 'email'),
498 Index('uem_email_idx', 'email'),
499 UniqueConstraint('email'),
499 UniqueConstraint('email'),
500 {'extend_existing': True, 'mysql_engine': 'InnoDB',
500 {'extend_existing': True, 'mysql_engine': 'InnoDB',
501 'mysql_charset': 'utf8'}
501 'mysql_charset': 'utf8'}
502 )
502 )
503 __mapper_args__ = {}
503 __mapper_args__ = {}
504
504
505 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
505 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
506 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
506 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
507 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
507 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
508 user = relationship('User', lazy='joined')
508 user = relationship('User', lazy='joined')
509
509
510 @validates('_email')
510 @validates('_email')
511 def validate_email(self, key, email):
511 def validate_email(self, key, email):
512 # check if this email is not main one
512 # check if this email is not main one
513 main_email = Session().query(User).filter(User.email == email).scalar()
513 main_email = Session().query(User).filter(User.email == email).scalar()
514 if main_email is not None:
514 if main_email is not None:
515 raise AttributeError('email %s is present is user table' % email)
515 raise AttributeError('email %s is present is user table' % email)
516 return email
516 return email
517
517
518 @hybrid_property
518 @hybrid_property
519 def email(self):
519 def email(self):
520 return self._email
520 return self._email
521
521
522 @email.setter
522 @email.setter
523 def email(self, val):
523 def email(self, val):
524 self._email = val.lower() if val else None
524 self._email = val.lower() if val else None
525
525
526
526
527 class UserIpMap(Base, BaseModel):
527 class UserIpMap(Base, BaseModel):
528 __tablename__ = 'user_ip_map'
528 __tablename__ = 'user_ip_map'
529 __table_args__ = (
529 __table_args__ = (
530 UniqueConstraint('user_id', 'ip_addr'),
530 UniqueConstraint('user_id', 'ip_addr'),
531 {'extend_existing': True, 'mysql_engine': 'InnoDB',
531 {'extend_existing': True, 'mysql_engine': 'InnoDB',
532 'mysql_charset': 'utf8'}
532 'mysql_charset': 'utf8'}
533 )
533 )
534 __mapper_args__ = {}
534 __mapper_args__ = {}
535
535
536 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
536 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
537 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
537 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
538 ip_addr = Column("ip_addr", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
538 ip_addr = Column("ip_addr", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
539 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
539 user = relationship('User', lazy='joined')
540 user = relationship('User', lazy='joined')
540
541
541 @classmethod
542 @classmethod
542 def _get_ip_range(cls, ip_addr):
543 def _get_ip_range(cls, ip_addr):
543 from rhodecode.lib import ipaddr
544 from rhodecode.lib import ipaddr
544 net = ipaddr.IPv4Network(ip_addr)
545 net = ipaddr.IPv4Network(ip_addr)
545 return [str(net.network), str(net.broadcast)]
546 return [str(net.network), str(net.broadcast)]
546
547
547 def __json__(self):
548 def __json__(self):
548 return dict(
549 return dict(
549 ip_addr=self.ip_addr,
550 ip_addr=self.ip_addr,
550 ip_range=self._get_ip_range(self.ip_addr)
551 ip_range=self._get_ip_range(self.ip_addr)
551 )
552 )
552
553
553
554
554 class UserLog(Base, BaseModel):
555 class UserLog(Base, BaseModel):
555 __tablename__ = 'user_logs'
556 __tablename__ = 'user_logs'
556 __table_args__ = (
557 __table_args__ = (
557 {'extend_existing': True, 'mysql_engine': 'InnoDB',
558 {'extend_existing': True, 'mysql_engine': 'InnoDB',
558 'mysql_charset': 'utf8'},
559 'mysql_charset': 'utf8'},
559 )
560 )
560 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
561 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
561 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
562 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
562 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
563 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
563 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
564 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
564 repository_name = Column("repository_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
565 repository_name = Column("repository_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
565 user_ip = Column("user_ip", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
566 user_ip = Column("user_ip", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
566 action = Column("action", UnicodeText(1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
567 action = Column("action", UnicodeText(1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
567 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
568 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
568
569
569 @property
570 @property
570 def action_as_day(self):
571 def action_as_day(self):
571 return datetime.date(*self.action_date.timetuple()[:3])
572 return datetime.date(*self.action_date.timetuple()[:3])
572
573
573 user = relationship('User')
574 user = relationship('User')
574 repository = relationship('Repository', cascade='')
575 repository = relationship('Repository', cascade='')
575
576
576
577
577 class UsersGroup(Base, BaseModel):
578 class UsersGroup(Base, BaseModel):
578 __tablename__ = 'users_groups'
579 __tablename__ = 'users_groups'
579 __table_args__ = (
580 __table_args__ = (
580 {'extend_existing': True, 'mysql_engine': 'InnoDB',
581 {'extend_existing': True, 'mysql_engine': 'InnoDB',
581 'mysql_charset': 'utf8'},
582 'mysql_charset': 'utf8'},
582 )
583 )
583
584
584 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
585 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
585 users_group_name = Column("users_group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
586 users_group_name = Column("users_group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
586 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
587 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
587 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
588 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
588
589
589 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
590 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
590 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
591 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
591 users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
592 users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
592
593
593 def __unicode__(self):
594 def __unicode__(self):
594 return u'<userGroup(%s)>' % (self.users_group_name)
595 return u'<userGroup(%s)>' % (self.users_group_name)
595
596
596 @classmethod
597 @classmethod
597 def get_by_group_name(cls, group_name, cache=False,
598 def get_by_group_name(cls, group_name, cache=False,
598 case_insensitive=False):
599 case_insensitive=False):
599 if case_insensitive:
600 if case_insensitive:
600 q = cls.query().filter(cls.users_group_name.ilike(group_name))
601 q = cls.query().filter(cls.users_group_name.ilike(group_name))
601 else:
602 else:
602 q = cls.query().filter(cls.users_group_name == group_name)
603 q = cls.query().filter(cls.users_group_name == group_name)
603 if cache:
604 if cache:
604 q = q.options(FromCache(
605 q = q.options(FromCache(
605 "sql_cache_short",
606 "sql_cache_short",
606 "get_user_%s" % _hash_key(group_name)
607 "get_user_%s" % _hash_key(group_name)
607 )
608 )
608 )
609 )
609 return q.scalar()
610 return q.scalar()
610
611
611 @classmethod
612 @classmethod
612 def get(cls, users_group_id, cache=False):
613 def get(cls, users_group_id, cache=False):
613 users_group = cls.query()
614 users_group = cls.query()
614 if cache:
615 if cache:
615 users_group = users_group.options(FromCache("sql_cache_short",
616 users_group = users_group.options(FromCache("sql_cache_short",
616 "get_users_group_%s" % users_group_id))
617 "get_users_group_%s" % users_group_id))
617 return users_group.get(users_group_id)
618 return users_group.get(users_group_id)
618
619
619 def get_api_data(self):
620 def get_api_data(self):
620 users_group = self
621 users_group = self
621
622
622 data = dict(
623 data = dict(
623 users_group_id=users_group.users_group_id,
624 users_group_id=users_group.users_group_id,
624 group_name=users_group.users_group_name,
625 group_name=users_group.users_group_name,
625 active=users_group.users_group_active,
626 active=users_group.users_group_active,
626 )
627 )
627
628
628 return data
629 return data
629
630
630
631
631 class UsersGroupMember(Base, BaseModel):
632 class UsersGroupMember(Base, BaseModel):
632 __tablename__ = 'users_groups_members'
633 __tablename__ = 'users_groups_members'
633 __table_args__ = (
634 __table_args__ = (
634 {'extend_existing': True, 'mysql_engine': 'InnoDB',
635 {'extend_existing': True, 'mysql_engine': 'InnoDB',
635 'mysql_charset': 'utf8'},
636 'mysql_charset': 'utf8'},
636 )
637 )
637
638
638 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
639 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
639 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
640 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
640 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
641 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
641
642
642 user = relationship('User', lazy='joined')
643 user = relationship('User', lazy='joined')
643 users_group = relationship('UsersGroup')
644 users_group = relationship('UsersGroup')
644
645
645 def __init__(self, gr_id='', u_id=''):
646 def __init__(self, gr_id='', u_id=''):
646 self.users_group_id = gr_id
647 self.users_group_id = gr_id
647 self.user_id = u_id
648 self.user_id = u_id
648
649
649
650
650 class Repository(Base, BaseModel):
651 class Repository(Base, BaseModel):
651 __tablename__ = 'repositories'
652 __tablename__ = 'repositories'
652 __table_args__ = (
653 __table_args__ = (
653 UniqueConstraint('repo_name'),
654 UniqueConstraint('repo_name'),
654 Index('r_repo_name_idx', 'repo_name'),
655 Index('r_repo_name_idx', 'repo_name'),
655 {'extend_existing': True, 'mysql_engine': 'InnoDB',
656 {'extend_existing': True, 'mysql_engine': 'InnoDB',
656 'mysql_charset': 'utf8'},
657 'mysql_charset': 'utf8'},
657 )
658 )
658
659
659 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
660 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
660 repo_name = Column("repo_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
661 repo_name = Column("repo_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
661 clone_uri = Column("clone_uri", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
662 clone_uri = Column("clone_uri", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
662 repo_type = Column("repo_type", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
663 repo_type = Column("repo_type", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
663 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
664 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
664 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
665 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
665 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
666 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
666 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
667 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
667 description = Column("description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
668 description = Column("description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
668 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
669 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
669 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
670 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
670 landing_rev = Column("landing_revision", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
671 landing_rev = Column("landing_revision", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
671 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
672 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
672 _locked = Column("locked", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
673 _locked = Column("locked", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
673 #changeset_cache = Column("changeset_cache", LargeBinary(), nullable=False) #JSON data
674 #changeset_cache = Column("changeset_cache", LargeBinary(), nullable=False) #JSON data
674
675
675 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
676 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
676 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
677 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
677
678
678 user = relationship('User')
679 user = relationship('User')
679 fork = relationship('Repository', remote_side=repo_id)
680 fork = relationship('Repository', remote_side=repo_id)
680 group = relationship('RepoGroup')
681 group = relationship('RepoGroup')
681 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
682 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
682 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
683 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
683 stats = relationship('Statistics', cascade='all', uselist=False)
684 stats = relationship('Statistics', cascade='all', uselist=False)
684
685
685 followers = relationship('UserFollowing',
686 followers = relationship('UserFollowing',
686 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
687 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
687 cascade='all')
688 cascade='all')
688
689
689 logs = relationship('UserLog')
690 logs = relationship('UserLog')
690 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
691 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
691
692
692 pull_requests_org = relationship('PullRequest',
693 pull_requests_org = relationship('PullRequest',
693 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
694 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
694 cascade="all, delete, delete-orphan")
695 cascade="all, delete, delete-orphan")
695
696
696 pull_requests_other = relationship('PullRequest',
697 pull_requests_other = relationship('PullRequest',
697 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
698 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
698 cascade="all, delete, delete-orphan")
699 cascade="all, delete, delete-orphan")
699
700
700 def __unicode__(self):
701 def __unicode__(self):
701 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
702 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
702 self.repo_name)
703 self.repo_name)
703
704
704 @hybrid_property
705 @hybrid_property
705 def locked(self):
706 def locked(self):
706 # always should return [user_id, timelocked]
707 # always should return [user_id, timelocked]
707 if self._locked:
708 if self._locked:
708 _lock_info = self._locked.split(':')
709 _lock_info = self._locked.split(':')
709 return int(_lock_info[0]), _lock_info[1]
710 return int(_lock_info[0]), _lock_info[1]
710 return [None, None]
711 return [None, None]
711
712
712 @locked.setter
713 @locked.setter
713 def locked(self, val):
714 def locked(self, val):
714 if val and isinstance(val, (list, tuple)):
715 if val and isinstance(val, (list, tuple)):
715 self._locked = ':'.join(map(str, val))
716 self._locked = ':'.join(map(str, val))
716 else:
717 else:
717 self._locked = None
718 self._locked = None
718
719
719 @classmethod
720 @classmethod
720 def url_sep(cls):
721 def url_sep(cls):
721 return URL_SEP
722 return URL_SEP
722
723
723 @classmethod
724 @classmethod
724 def get_by_repo_name(cls, repo_name):
725 def get_by_repo_name(cls, repo_name):
725 q = Session().query(cls).filter(cls.repo_name == repo_name)
726 q = Session().query(cls).filter(cls.repo_name == repo_name)
726 q = q.options(joinedload(Repository.fork))\
727 q = q.options(joinedload(Repository.fork))\
727 .options(joinedload(Repository.user))\
728 .options(joinedload(Repository.user))\
728 .options(joinedload(Repository.group))
729 .options(joinedload(Repository.group))
729 return q.scalar()
730 return q.scalar()
730
731
731 @classmethod
732 @classmethod
732 def get_by_full_path(cls, repo_full_path):
733 def get_by_full_path(cls, repo_full_path):
733 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
734 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
734 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
735 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
735
736
736 @classmethod
737 @classmethod
737 def get_repo_forks(cls, repo_id):
738 def get_repo_forks(cls, repo_id):
738 return cls.query().filter(Repository.fork_id == repo_id)
739 return cls.query().filter(Repository.fork_id == repo_id)
739
740
740 @classmethod
741 @classmethod
741 def base_path(cls):
742 def base_path(cls):
742 """
743 """
743 Returns base path when all repos are stored
744 Returns base path when all repos are stored
744
745
745 :param cls:
746 :param cls:
746 """
747 """
747 q = Session().query(RhodeCodeUi)\
748 q = Session().query(RhodeCodeUi)\
748 .filter(RhodeCodeUi.ui_key == cls.url_sep())
749 .filter(RhodeCodeUi.ui_key == cls.url_sep())
749 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
750 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
750 return q.one().ui_value
751 return q.one().ui_value
751
752
752 @property
753 @property
753 def forks(self):
754 def forks(self):
754 """
755 """
755 Return forks of this repo
756 Return forks of this repo
756 """
757 """
757 return Repository.get_repo_forks(self.repo_id)
758 return Repository.get_repo_forks(self.repo_id)
758
759
759 @property
760 @property
760 def parent(self):
761 def parent(self):
761 """
762 """
762 Returns fork parent
763 Returns fork parent
763 """
764 """
764 return self.fork
765 return self.fork
765
766
766 @property
767 @property
767 def just_name(self):
768 def just_name(self):
768 return self.repo_name.split(Repository.url_sep())[-1]
769 return self.repo_name.split(Repository.url_sep())[-1]
769
770
770 @property
771 @property
771 def groups_with_parents(self):
772 def groups_with_parents(self):
772 groups = []
773 groups = []
773 if self.group is None:
774 if self.group is None:
774 return groups
775 return groups
775
776
776 cur_gr = self.group
777 cur_gr = self.group
777 groups.insert(0, cur_gr)
778 groups.insert(0, cur_gr)
778 while 1:
779 while 1:
779 gr = getattr(cur_gr, 'parent_group', None)
780 gr = getattr(cur_gr, 'parent_group', None)
780 cur_gr = cur_gr.parent_group
781 cur_gr = cur_gr.parent_group
781 if gr is None:
782 if gr is None:
782 break
783 break
783 groups.insert(0, gr)
784 groups.insert(0, gr)
784
785
785 return groups
786 return groups
786
787
787 @property
788 @property
788 def groups_and_repo(self):
789 def groups_and_repo(self):
789 return self.groups_with_parents, self.just_name
790 return self.groups_with_parents, self.just_name
790
791
791 @LazyProperty
792 @LazyProperty
792 def repo_path(self):
793 def repo_path(self):
793 """
794 """
794 Returns base full path for that repository means where it actually
795 Returns base full path for that repository means where it actually
795 exists on a filesystem
796 exists on a filesystem
796 """
797 """
797 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
798 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
798 Repository.url_sep())
799 Repository.url_sep())
799 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
800 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
800 return q.one().ui_value
801 return q.one().ui_value
801
802
802 @property
803 @property
803 def repo_full_path(self):
804 def repo_full_path(self):
804 p = [self.repo_path]
805 p = [self.repo_path]
805 # we need to split the name by / since this is how we store the
806 # we need to split the name by / since this is how we store the
806 # names in the database, but that eventually needs to be converted
807 # names in the database, but that eventually needs to be converted
807 # into a valid system path
808 # into a valid system path
808 p += self.repo_name.split(Repository.url_sep())
809 p += self.repo_name.split(Repository.url_sep())
809 return os.path.join(*p)
810 return os.path.join(*p)
810
811
811 @property
812 @property
812 def cache_keys(self):
813 def cache_keys(self):
813 """
814 """
814 Returns associated cache keys for that repo
815 Returns associated cache keys for that repo
815 """
816 """
816 return CacheInvalidation.query()\
817 return CacheInvalidation.query()\
817 .filter(CacheInvalidation.cache_args == self.repo_name)\
818 .filter(CacheInvalidation.cache_args == self.repo_name)\
818 .order_by(CacheInvalidation.cache_key)\
819 .order_by(CacheInvalidation.cache_key)\
819 .all()
820 .all()
820
821
821 def get_new_name(self, repo_name):
822 def get_new_name(self, repo_name):
822 """
823 """
823 returns new full repository name based on assigned group and new new
824 returns new full repository name based on assigned group and new new
824
825
825 :param group_name:
826 :param group_name:
826 """
827 """
827 path_prefix = self.group.full_path_splitted if self.group else []
828 path_prefix = self.group.full_path_splitted if self.group else []
828 return Repository.url_sep().join(path_prefix + [repo_name])
829 return Repository.url_sep().join(path_prefix + [repo_name])
829
830
830 @property
831 @property
831 def _ui(self):
832 def _ui(self):
832 """
833 """
833 Creates an db based ui object for this repository
834 Creates an db based ui object for this repository
834 """
835 """
835 from rhodecode.lib.utils import make_ui
836 from rhodecode.lib.utils import make_ui
836 return make_ui('db', clear_session=False)
837 return make_ui('db', clear_session=False)
837
838
838 @classmethod
839 @classmethod
839 def inject_ui(cls, repo, extras={}):
840 def inject_ui(cls, repo, extras={}):
840 from rhodecode.lib.vcs.backends.hg import MercurialRepository
841 from rhodecode.lib.vcs.backends.hg import MercurialRepository
841 from rhodecode.lib.vcs.backends.git import GitRepository
842 from rhodecode.lib.vcs.backends.git import GitRepository
842 required = (MercurialRepository, GitRepository)
843 required = (MercurialRepository, GitRepository)
843 if not isinstance(repo, required):
844 if not isinstance(repo, required):
844 raise Exception('repo must be instance of %s' % required)
845 raise Exception('repo must be instance of %s' % required)
845
846
846 # inject ui extra param to log this action via push logger
847 # inject ui extra param to log this action via push logger
847 for k, v in extras.items():
848 for k, v in extras.items():
848 repo._repo.ui.setconfig('rhodecode_extras', k, v)
849 repo._repo.ui.setconfig('rhodecode_extras', k, v)
849
850
850 @classmethod
851 @classmethod
851 def is_valid(cls, repo_name):
852 def is_valid(cls, repo_name):
852 """
853 """
853 returns True if given repo name is a valid filesystem repository
854 returns True if given repo name is a valid filesystem repository
854
855
855 :param cls:
856 :param cls:
856 :param repo_name:
857 :param repo_name:
857 """
858 """
858 from rhodecode.lib.utils import is_valid_repo
859 from rhodecode.lib.utils import is_valid_repo
859
860
860 return is_valid_repo(repo_name, cls.base_path())
861 return is_valid_repo(repo_name, cls.base_path())
861
862
862 def get_api_data(self):
863 def get_api_data(self):
863 """
864 """
864 Common function for generating repo api data
865 Common function for generating repo api data
865
866
866 """
867 """
867 repo = self
868 repo = self
868 data = dict(
869 data = dict(
869 repo_id=repo.repo_id,
870 repo_id=repo.repo_id,
870 repo_name=repo.repo_name,
871 repo_name=repo.repo_name,
871 repo_type=repo.repo_type,
872 repo_type=repo.repo_type,
872 clone_uri=repo.clone_uri,
873 clone_uri=repo.clone_uri,
873 private=repo.private,
874 private=repo.private,
874 created_on=repo.created_on,
875 created_on=repo.created_on,
875 description=repo.description,
876 description=repo.description,
876 landing_rev=repo.landing_rev,
877 landing_rev=repo.landing_rev,
877 owner=repo.user.username,
878 owner=repo.user.username,
878 fork_of=repo.fork.repo_name if repo.fork else None,
879 fork_of=repo.fork.repo_name if repo.fork else None,
879 enable_statistics=repo.enable_statistics,
880 enable_statistics=repo.enable_statistics,
880 enable_locking=repo.enable_locking,
881 enable_locking=repo.enable_locking,
881 enable_downloads=repo.enable_downloads
882 enable_downloads=repo.enable_downloads
882 )
883 )
883
884
884 return data
885 return data
885
886
886 @classmethod
887 @classmethod
887 def lock(cls, repo, user_id):
888 def lock(cls, repo, user_id):
888 repo.locked = [user_id, time.time()]
889 repo.locked = [user_id, time.time()]
889 Session().add(repo)
890 Session().add(repo)
890 Session().commit()
891 Session().commit()
891
892
892 @classmethod
893 @classmethod
893 def unlock(cls, repo):
894 def unlock(cls, repo):
894 repo.locked = None
895 repo.locked = None
895 Session().add(repo)
896 Session().add(repo)
896 Session().commit()
897 Session().commit()
897
898
898 @property
899 @property
899 def last_db_change(self):
900 def last_db_change(self):
900 return self.updated_on
901 return self.updated_on
901
902
902 #==========================================================================
903 #==========================================================================
903 # SCM PROPERTIES
904 # SCM PROPERTIES
904 #==========================================================================
905 #==========================================================================
905
906
906 def get_changeset(self, rev=None):
907 def get_changeset(self, rev=None):
907 return get_changeset_safe(self.scm_instance, rev)
908 return get_changeset_safe(self.scm_instance, rev)
908
909
909 def get_landing_changeset(self):
910 def get_landing_changeset(self):
910 """
911 """
911 Returns landing changeset, or if that doesn't exist returns the tip
912 Returns landing changeset, or if that doesn't exist returns the tip
912 """
913 """
913 cs = self.get_changeset(self.landing_rev) or self.get_changeset()
914 cs = self.get_changeset(self.landing_rev) or self.get_changeset()
914 return cs
915 return cs
915
916
916 def update_last_change(self, last_change=None):
917 def update_last_change(self, last_change=None):
917 if last_change is None:
918 if last_change is None:
918 last_change = datetime.datetime.now()
919 last_change = datetime.datetime.now()
919 if self.updated_on is None or self.updated_on != last_change:
920 if self.updated_on is None or self.updated_on != last_change:
920 log.debug('updated repo %s with new date %s' % (self, last_change))
921 log.debug('updated repo %s with new date %s' % (self, last_change))
921 self.updated_on = last_change
922 self.updated_on = last_change
922 Session().add(self)
923 Session().add(self)
923 Session().commit()
924 Session().commit()
924
925
925 @property
926 @property
926 def tip(self):
927 def tip(self):
927 return self.get_changeset('tip')
928 return self.get_changeset('tip')
928
929
929 @property
930 @property
930 def author(self):
931 def author(self):
931 return self.tip.author
932 return self.tip.author
932
933
933 @property
934 @property
934 def last_change(self):
935 def last_change(self):
935 return self.scm_instance.last_change
936 return self.scm_instance.last_change
936
937
937 def get_comments(self, revisions=None):
938 def get_comments(self, revisions=None):
938 """
939 """
939 Returns comments for this repository grouped by revisions
940 Returns comments for this repository grouped by revisions
940
941
941 :param revisions: filter query by revisions only
942 :param revisions: filter query by revisions only
942 """
943 """
943 cmts = ChangesetComment.query()\
944 cmts = ChangesetComment.query()\
944 .filter(ChangesetComment.repo == self)
945 .filter(ChangesetComment.repo == self)
945 if revisions:
946 if revisions:
946 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
947 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
947 grouped = defaultdict(list)
948 grouped = defaultdict(list)
948 for cmt in cmts.all():
949 for cmt in cmts.all():
949 grouped[cmt.revision].append(cmt)
950 grouped[cmt.revision].append(cmt)
950 return grouped
951 return grouped
951
952
952 def statuses(self, revisions=None):
953 def statuses(self, revisions=None):
953 """
954 """
954 Returns statuses for this repository
955 Returns statuses for this repository
955
956
956 :param revisions: list of revisions to get statuses for
957 :param revisions: list of revisions to get statuses for
957 :type revisions: list
958 :type revisions: list
958 """
959 """
959
960
960 statuses = ChangesetStatus.query()\
961 statuses = ChangesetStatus.query()\
961 .filter(ChangesetStatus.repo == self)\
962 .filter(ChangesetStatus.repo == self)\
962 .filter(ChangesetStatus.version == 0)
963 .filter(ChangesetStatus.version == 0)
963 if revisions:
964 if revisions:
964 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
965 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
965 grouped = {}
966 grouped = {}
966
967
967 #maybe we have open new pullrequest without a status ?
968 #maybe we have open new pullrequest without a status ?
968 stat = ChangesetStatus.STATUS_UNDER_REVIEW
969 stat = ChangesetStatus.STATUS_UNDER_REVIEW
969 status_lbl = ChangesetStatus.get_status_lbl(stat)
970 status_lbl = ChangesetStatus.get_status_lbl(stat)
970 for pr in PullRequest.query().filter(PullRequest.org_repo == self).all():
971 for pr in PullRequest.query().filter(PullRequest.org_repo == self).all():
971 for rev in pr.revisions:
972 for rev in pr.revisions:
972 pr_id = pr.pull_request_id
973 pr_id = pr.pull_request_id
973 pr_repo = pr.other_repo.repo_name
974 pr_repo = pr.other_repo.repo_name
974 grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
975 grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
975
976
976 for stat in statuses.all():
977 for stat in statuses.all():
977 pr_id = pr_repo = None
978 pr_id = pr_repo = None
978 if stat.pull_request:
979 if stat.pull_request:
979 pr_id = stat.pull_request.pull_request_id
980 pr_id = stat.pull_request.pull_request_id
980 pr_repo = stat.pull_request.other_repo.repo_name
981 pr_repo = stat.pull_request.other_repo.repo_name
981 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
982 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
982 pr_id, pr_repo]
983 pr_id, pr_repo]
983 return grouped
984 return grouped
984
985
985 #==========================================================================
986 #==========================================================================
986 # SCM CACHE INSTANCE
987 # SCM CACHE INSTANCE
987 #==========================================================================
988 #==========================================================================
988
989
989 @property
990 @property
990 def invalidate(self):
991 def invalidate(self):
991 return CacheInvalidation.invalidate(self.repo_name)
992 return CacheInvalidation.invalidate(self.repo_name)
992
993
993 def set_invalidate(self):
994 def set_invalidate(self):
994 """
995 """
995 set a cache for invalidation for this instance
996 set a cache for invalidation for this instance
996 """
997 """
997 CacheInvalidation.set_invalidate(repo_name=self.repo_name)
998 CacheInvalidation.set_invalidate(repo_name=self.repo_name)
998
999
999 @LazyProperty
1000 @LazyProperty
1000 def scm_instance(self):
1001 def scm_instance(self):
1001 import rhodecode
1002 import rhodecode
1002 full_cache = str2bool(rhodecode.CONFIG.get('vcs_full_cache'))
1003 full_cache = str2bool(rhodecode.CONFIG.get('vcs_full_cache'))
1003 if full_cache:
1004 if full_cache:
1004 return self.scm_instance_cached()
1005 return self.scm_instance_cached()
1005 return self.__get_instance()
1006 return self.__get_instance()
1006
1007
1007 def scm_instance_cached(self, cache_map=None):
1008 def scm_instance_cached(self, cache_map=None):
1008 @cache_region('long_term')
1009 @cache_region('long_term')
1009 def _c(repo_name):
1010 def _c(repo_name):
1010 return self.__get_instance()
1011 return self.__get_instance()
1011 rn = self.repo_name
1012 rn = self.repo_name
1012 log.debug('Getting cached instance of repo')
1013 log.debug('Getting cached instance of repo')
1013
1014
1014 if cache_map:
1015 if cache_map:
1015 # get using prefilled cache_map
1016 # get using prefilled cache_map
1016 invalidate_repo = cache_map[self.repo_name]
1017 invalidate_repo = cache_map[self.repo_name]
1017 if invalidate_repo:
1018 if invalidate_repo:
1018 invalidate_repo = (None if invalidate_repo.cache_active
1019 invalidate_repo = (None if invalidate_repo.cache_active
1019 else invalidate_repo)
1020 else invalidate_repo)
1020 else:
1021 else:
1021 # get from invalidate
1022 # get from invalidate
1022 invalidate_repo = self.invalidate
1023 invalidate_repo = self.invalidate
1023
1024
1024 if invalidate_repo is not None:
1025 if invalidate_repo is not None:
1025 region_invalidate(_c, None, rn)
1026 region_invalidate(_c, None, rn)
1026 # update our cache
1027 # update our cache
1027 CacheInvalidation.set_valid(invalidate_repo.cache_key)
1028 CacheInvalidation.set_valid(invalidate_repo.cache_key)
1028 return _c(rn)
1029 return _c(rn)
1029
1030
1030 def __get_instance(self):
1031 def __get_instance(self):
1031 repo_full_path = self.repo_full_path
1032 repo_full_path = self.repo_full_path
1032 try:
1033 try:
1033 alias = get_scm(repo_full_path)[0]
1034 alias = get_scm(repo_full_path)[0]
1034 log.debug('Creating instance of %s repository' % alias)
1035 log.debug('Creating instance of %s repository' % alias)
1035 backend = get_backend(alias)
1036 backend = get_backend(alias)
1036 except VCSError:
1037 except VCSError:
1037 log.error(traceback.format_exc())
1038 log.error(traceback.format_exc())
1038 log.error('Perhaps this repository is in db and not in '
1039 log.error('Perhaps this repository is in db and not in '
1039 'filesystem run rescan repositories with '
1040 'filesystem run rescan repositories with '
1040 '"destroy old data " option from admin panel')
1041 '"destroy old data " option from admin panel')
1041 return
1042 return
1042
1043
1043 if alias == 'hg':
1044 if alias == 'hg':
1044
1045
1045 repo = backend(safe_str(repo_full_path), create=False,
1046 repo = backend(safe_str(repo_full_path), create=False,
1046 baseui=self._ui)
1047 baseui=self._ui)
1047 # skip hidden web repository
1048 # skip hidden web repository
1048 if repo._get_hidden():
1049 if repo._get_hidden():
1049 return
1050 return
1050 else:
1051 else:
1051 repo = backend(repo_full_path, create=False)
1052 repo = backend(repo_full_path, create=False)
1052
1053
1053 return repo
1054 return repo
1054
1055
1055
1056
1056 class RepoGroup(Base, BaseModel):
1057 class RepoGroup(Base, BaseModel):
1057 __tablename__ = 'groups'
1058 __tablename__ = 'groups'
1058 __table_args__ = (
1059 __table_args__ = (
1059 UniqueConstraint('group_name', 'group_parent_id'),
1060 UniqueConstraint('group_name', 'group_parent_id'),
1060 CheckConstraint('group_id != group_parent_id'),
1061 CheckConstraint('group_id != group_parent_id'),
1061 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1062 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1062 'mysql_charset': 'utf8'},
1063 'mysql_charset': 'utf8'},
1063 )
1064 )
1064 __mapper_args__ = {'order_by': 'group_name'}
1065 __mapper_args__ = {'order_by': 'group_name'}
1065
1066
1066 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1067 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1067 group_name = Column("group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
1068 group_name = Column("group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
1068 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
1069 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
1069 group_description = Column("group_description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1070 group_description = Column("group_description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1070 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
1071 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
1071
1072
1072 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
1073 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
1073 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
1074 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
1074
1075
1075 parent_group = relationship('RepoGroup', remote_side=group_id)
1076 parent_group = relationship('RepoGroup', remote_side=group_id)
1076
1077
1077 def __init__(self, group_name='', parent_group=None):
1078 def __init__(self, group_name='', parent_group=None):
1078 self.group_name = group_name
1079 self.group_name = group_name
1079 self.parent_group = parent_group
1080 self.parent_group = parent_group
1080
1081
1081 def __unicode__(self):
1082 def __unicode__(self):
1082 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
1083 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
1083 self.group_name)
1084 self.group_name)
1084
1085
1085 @classmethod
1086 @classmethod
1086 def groups_choices(cls, check_perms=False):
1087 def groups_choices(cls, check_perms=False):
1087 from webhelpers.html import literal as _literal
1088 from webhelpers.html import literal as _literal
1088 from rhodecode.model.scm import ScmModel
1089 from rhodecode.model.scm import ScmModel
1089 groups = cls.query().all()
1090 groups = cls.query().all()
1090 if check_perms:
1091 if check_perms:
1091 #filter group user have access to, it's done
1092 #filter group user have access to, it's done
1092 #magically inside ScmModel based on current user
1093 #magically inside ScmModel based on current user
1093 groups = ScmModel().get_repos_groups(groups)
1094 groups = ScmModel().get_repos_groups(groups)
1094 repo_groups = [('', '')]
1095 repo_groups = [('', '')]
1095 sep = ' &raquo; '
1096 sep = ' &raquo; '
1096 _name = lambda k: _literal(sep.join(k))
1097 _name = lambda k: _literal(sep.join(k))
1097
1098
1098 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
1099 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
1099 for x in groups])
1100 for x in groups])
1100
1101
1101 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
1102 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
1102 return repo_groups
1103 return repo_groups
1103
1104
1104 @classmethod
1105 @classmethod
1105 def url_sep(cls):
1106 def url_sep(cls):
1106 return URL_SEP
1107 return URL_SEP
1107
1108
1108 @classmethod
1109 @classmethod
1109 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
1110 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
1110 if case_insensitive:
1111 if case_insensitive:
1111 gr = cls.query()\
1112 gr = cls.query()\
1112 .filter(cls.group_name.ilike(group_name))
1113 .filter(cls.group_name.ilike(group_name))
1113 else:
1114 else:
1114 gr = cls.query()\
1115 gr = cls.query()\
1115 .filter(cls.group_name == group_name)
1116 .filter(cls.group_name == group_name)
1116 if cache:
1117 if cache:
1117 gr = gr.options(FromCache(
1118 gr = gr.options(FromCache(
1118 "sql_cache_short",
1119 "sql_cache_short",
1119 "get_group_%s" % _hash_key(group_name)
1120 "get_group_%s" % _hash_key(group_name)
1120 )
1121 )
1121 )
1122 )
1122 return gr.scalar()
1123 return gr.scalar()
1123
1124
1124 @property
1125 @property
1125 def parents(self):
1126 def parents(self):
1126 parents_recursion_limit = 5
1127 parents_recursion_limit = 5
1127 groups = []
1128 groups = []
1128 if self.parent_group is None:
1129 if self.parent_group is None:
1129 return groups
1130 return groups
1130 cur_gr = self.parent_group
1131 cur_gr = self.parent_group
1131 groups.insert(0, cur_gr)
1132 groups.insert(0, cur_gr)
1132 cnt = 0
1133 cnt = 0
1133 while 1:
1134 while 1:
1134 cnt += 1
1135 cnt += 1
1135 gr = getattr(cur_gr, 'parent_group', None)
1136 gr = getattr(cur_gr, 'parent_group', None)
1136 cur_gr = cur_gr.parent_group
1137 cur_gr = cur_gr.parent_group
1137 if gr is None:
1138 if gr is None:
1138 break
1139 break
1139 if cnt == parents_recursion_limit:
1140 if cnt == parents_recursion_limit:
1140 # this will prevent accidental infinit loops
1141 # this will prevent accidental infinit loops
1141 log.error('group nested more than %s' %
1142 log.error('group nested more than %s' %
1142 parents_recursion_limit)
1143 parents_recursion_limit)
1143 break
1144 break
1144
1145
1145 groups.insert(0, gr)
1146 groups.insert(0, gr)
1146 return groups
1147 return groups
1147
1148
1148 @property
1149 @property
1149 def children(self):
1150 def children(self):
1150 return RepoGroup.query().filter(RepoGroup.parent_group == self)
1151 return RepoGroup.query().filter(RepoGroup.parent_group == self)
1151
1152
1152 @property
1153 @property
1153 def name(self):
1154 def name(self):
1154 return self.group_name.split(RepoGroup.url_sep())[-1]
1155 return self.group_name.split(RepoGroup.url_sep())[-1]
1155
1156
1156 @property
1157 @property
1157 def full_path(self):
1158 def full_path(self):
1158 return self.group_name
1159 return self.group_name
1159
1160
1160 @property
1161 @property
1161 def full_path_splitted(self):
1162 def full_path_splitted(self):
1162 return self.group_name.split(RepoGroup.url_sep())
1163 return self.group_name.split(RepoGroup.url_sep())
1163
1164
1164 @property
1165 @property
1165 def repositories(self):
1166 def repositories(self):
1166 return Repository.query()\
1167 return Repository.query()\
1167 .filter(Repository.group == self)\
1168 .filter(Repository.group == self)\
1168 .order_by(Repository.repo_name)
1169 .order_by(Repository.repo_name)
1169
1170
1170 @property
1171 @property
1171 def repositories_recursive_count(self):
1172 def repositories_recursive_count(self):
1172 cnt = self.repositories.count()
1173 cnt = self.repositories.count()
1173
1174
1174 def children_count(group):
1175 def children_count(group):
1175 cnt = 0
1176 cnt = 0
1176 for child in group.children:
1177 for child in group.children:
1177 cnt += child.repositories.count()
1178 cnt += child.repositories.count()
1178 cnt += children_count(child)
1179 cnt += children_count(child)
1179 return cnt
1180 return cnt
1180
1181
1181 return cnt + children_count(self)
1182 return cnt + children_count(self)
1182
1183
1183 def recursive_groups_and_repos(self):
1184 def recursive_groups_and_repos(self):
1184 """
1185 """
1185 Recursive return all groups, with repositories in those groups
1186 Recursive return all groups, with repositories in those groups
1186 """
1187 """
1187 all_ = []
1188 all_ = []
1188
1189
1189 def _get_members(root_gr):
1190 def _get_members(root_gr):
1190 for r in root_gr.repositories:
1191 for r in root_gr.repositories:
1191 all_.append(r)
1192 all_.append(r)
1192 childs = root_gr.children.all()
1193 childs = root_gr.children.all()
1193 if childs:
1194 if childs:
1194 for gr in childs:
1195 for gr in childs:
1195 all_.append(gr)
1196 all_.append(gr)
1196 _get_members(gr)
1197 _get_members(gr)
1197
1198
1198 _get_members(self)
1199 _get_members(self)
1199 return [self] + all_
1200 return [self] + all_
1200
1201
1201 def get_new_name(self, group_name):
1202 def get_new_name(self, group_name):
1202 """
1203 """
1203 returns new full group name based on parent and new name
1204 returns new full group name based on parent and new name
1204
1205
1205 :param group_name:
1206 :param group_name:
1206 """
1207 """
1207 path_prefix = (self.parent_group.full_path_splitted if
1208 path_prefix = (self.parent_group.full_path_splitted if
1208 self.parent_group else [])
1209 self.parent_group else [])
1209 return RepoGroup.url_sep().join(path_prefix + [group_name])
1210 return RepoGroup.url_sep().join(path_prefix + [group_name])
1210
1211
1211
1212
1212 class Permission(Base, BaseModel):
1213 class Permission(Base, BaseModel):
1213 __tablename__ = 'permissions'
1214 __tablename__ = 'permissions'
1214 __table_args__ = (
1215 __table_args__ = (
1215 Index('p_perm_name_idx', 'permission_name'),
1216 Index('p_perm_name_idx', 'permission_name'),
1216 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1217 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1217 'mysql_charset': 'utf8'},
1218 'mysql_charset': 'utf8'},
1218 )
1219 )
1219 PERMS = [
1220 PERMS = [
1220 ('repository.none', _('Repository no access')),
1221 ('repository.none', _('Repository no access')),
1221 ('repository.read', _('Repository read access')),
1222 ('repository.read', _('Repository read access')),
1222 ('repository.write', _('Repository write access')),
1223 ('repository.write', _('Repository write access')),
1223 ('repository.admin', _('Repository admin access')),
1224 ('repository.admin', _('Repository admin access')),
1224
1225
1225 ('group.none', _('Repositories Group no access')),
1226 ('group.none', _('Repositories Group no access')),
1226 ('group.read', _('Repositories Group read access')),
1227 ('group.read', _('Repositories Group read access')),
1227 ('group.write', _('Repositories Group write access')),
1228 ('group.write', _('Repositories Group write access')),
1228 ('group.admin', _('Repositories Group admin access')),
1229 ('group.admin', _('Repositories Group admin access')),
1229
1230
1230 ('hg.admin', _('RhodeCode Administrator')),
1231 ('hg.admin', _('RhodeCode Administrator')),
1231 ('hg.create.none', _('Repository creation disabled')),
1232 ('hg.create.none', _('Repository creation disabled')),
1232 ('hg.create.repository', _('Repository creation enabled')),
1233 ('hg.create.repository', _('Repository creation enabled')),
1233 ('hg.fork.none', _('Repository forking disabled')),
1234 ('hg.fork.none', _('Repository forking disabled')),
1234 ('hg.fork.repository', _('Repository forking enabled')),
1235 ('hg.fork.repository', _('Repository forking enabled')),
1235 ('hg.register.none', _('Register disabled')),
1236 ('hg.register.none', _('Register disabled')),
1236 ('hg.register.manual_activate', _('Register new user with RhodeCode '
1237 ('hg.register.manual_activate', _('Register new user with RhodeCode '
1237 'with manual activation')),
1238 'with manual activation')),
1238
1239
1239 ('hg.register.auto_activate', _('Register new user with RhodeCode '
1240 ('hg.register.auto_activate', _('Register new user with RhodeCode '
1240 'with auto activation')),
1241 'with auto activation')),
1241 ]
1242 ]
1242
1243
1243 # defines which permissions are more important higher the more important
1244 # defines which permissions are more important higher the more important
1244 PERM_WEIGHTS = {
1245 PERM_WEIGHTS = {
1245 'repository.none': 0,
1246 'repository.none': 0,
1246 'repository.read': 1,
1247 'repository.read': 1,
1247 'repository.write': 3,
1248 'repository.write': 3,
1248 'repository.admin': 4,
1249 'repository.admin': 4,
1249
1250
1250 'group.none': 0,
1251 'group.none': 0,
1251 'group.read': 1,
1252 'group.read': 1,
1252 'group.write': 3,
1253 'group.write': 3,
1253 'group.admin': 4,
1254 'group.admin': 4,
1254
1255
1255 'hg.fork.none': 0,
1256 'hg.fork.none': 0,
1256 'hg.fork.repository': 1,
1257 'hg.fork.repository': 1,
1257 'hg.create.none': 0,
1258 'hg.create.none': 0,
1258 'hg.create.repository':1
1259 'hg.create.repository':1
1259 }
1260 }
1260
1261
1261 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1262 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1262 permission_name = Column("permission_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1263 permission_name = Column("permission_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1263 permission_longname = Column("permission_longname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1264 permission_longname = Column("permission_longname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1264
1265
1265 def __unicode__(self):
1266 def __unicode__(self):
1266 return u"<%s('%s:%s')>" % (
1267 return u"<%s('%s:%s')>" % (
1267 self.__class__.__name__, self.permission_id, self.permission_name
1268 self.__class__.__name__, self.permission_id, self.permission_name
1268 )
1269 )
1269
1270
1270 @classmethod
1271 @classmethod
1271 def get_by_key(cls, key):
1272 def get_by_key(cls, key):
1272 return cls.query().filter(cls.permission_name == key).scalar()
1273 return cls.query().filter(cls.permission_name == key).scalar()
1273
1274
1274 @classmethod
1275 @classmethod
1275 def get_default_perms(cls, default_user_id):
1276 def get_default_perms(cls, default_user_id):
1276 q = Session().query(UserRepoToPerm, Repository, cls)\
1277 q = Session().query(UserRepoToPerm, Repository, cls)\
1277 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
1278 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
1278 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
1279 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
1279 .filter(UserRepoToPerm.user_id == default_user_id)
1280 .filter(UserRepoToPerm.user_id == default_user_id)
1280
1281
1281 return q.all()
1282 return q.all()
1282
1283
1283 @classmethod
1284 @classmethod
1284 def get_default_group_perms(cls, default_user_id):
1285 def get_default_group_perms(cls, default_user_id):
1285 q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
1286 q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
1286 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
1287 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
1287 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
1288 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
1288 .filter(UserRepoGroupToPerm.user_id == default_user_id)
1289 .filter(UserRepoGroupToPerm.user_id == default_user_id)
1289
1290
1290 return q.all()
1291 return q.all()
1291
1292
1292
1293
1293 class UserRepoToPerm(Base, BaseModel):
1294 class UserRepoToPerm(Base, BaseModel):
1294 __tablename__ = 'repo_to_perm'
1295 __tablename__ = 'repo_to_perm'
1295 __table_args__ = (
1296 __table_args__ = (
1296 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
1297 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
1297 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1298 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1298 'mysql_charset': 'utf8'}
1299 'mysql_charset': 'utf8'}
1299 )
1300 )
1300 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1301 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1301 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1302 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1302 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1303 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1303 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1304 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1304
1305
1305 user = relationship('User')
1306 user = relationship('User')
1306 repository = relationship('Repository')
1307 repository = relationship('Repository')
1307 permission = relationship('Permission')
1308 permission = relationship('Permission')
1308
1309
1309 @classmethod
1310 @classmethod
1310 def create(cls, user, repository, permission):
1311 def create(cls, user, repository, permission):
1311 n = cls()
1312 n = cls()
1312 n.user = user
1313 n.user = user
1313 n.repository = repository
1314 n.repository = repository
1314 n.permission = permission
1315 n.permission = permission
1315 Session().add(n)
1316 Session().add(n)
1316 return n
1317 return n
1317
1318
1318 def __unicode__(self):
1319 def __unicode__(self):
1319 return u'<user:%s => %s >' % (self.user, self.repository)
1320 return u'<user:%s => %s >' % (self.user, self.repository)
1320
1321
1321
1322
1322 class UserToPerm(Base, BaseModel):
1323 class UserToPerm(Base, BaseModel):
1323 __tablename__ = 'user_to_perm'
1324 __tablename__ = 'user_to_perm'
1324 __table_args__ = (
1325 __table_args__ = (
1325 UniqueConstraint('user_id', 'permission_id'),
1326 UniqueConstraint('user_id', 'permission_id'),
1326 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1327 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1327 'mysql_charset': 'utf8'}
1328 'mysql_charset': 'utf8'}
1328 )
1329 )
1329 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1330 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1330 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1331 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1331 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1332 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1332
1333
1333 user = relationship('User')
1334 user = relationship('User')
1334 permission = relationship('Permission', lazy='joined')
1335 permission = relationship('Permission', lazy='joined')
1335
1336
1336
1337
1337 class UsersGroupRepoToPerm(Base, BaseModel):
1338 class UsersGroupRepoToPerm(Base, BaseModel):
1338 __tablename__ = 'users_group_repo_to_perm'
1339 __tablename__ = 'users_group_repo_to_perm'
1339 __table_args__ = (
1340 __table_args__ = (
1340 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1341 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1341 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1342 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1342 'mysql_charset': 'utf8'}
1343 'mysql_charset': 'utf8'}
1343 )
1344 )
1344 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1345 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1345 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1346 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1346 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1347 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1347 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1348 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1348
1349
1349 users_group = relationship('UsersGroup')
1350 users_group = relationship('UsersGroup')
1350 permission = relationship('Permission')
1351 permission = relationship('Permission')
1351 repository = relationship('Repository')
1352 repository = relationship('Repository')
1352
1353
1353 @classmethod
1354 @classmethod
1354 def create(cls, users_group, repository, permission):
1355 def create(cls, users_group, repository, permission):
1355 n = cls()
1356 n = cls()
1356 n.users_group = users_group
1357 n.users_group = users_group
1357 n.repository = repository
1358 n.repository = repository
1358 n.permission = permission
1359 n.permission = permission
1359 Session().add(n)
1360 Session().add(n)
1360 return n
1361 return n
1361
1362
1362 def __unicode__(self):
1363 def __unicode__(self):
1363 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
1364 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
1364
1365
1365
1366
1366 class UsersGroupToPerm(Base, BaseModel):
1367 class UsersGroupToPerm(Base, BaseModel):
1367 __tablename__ = 'users_group_to_perm'
1368 __tablename__ = 'users_group_to_perm'
1368 __table_args__ = (
1369 __table_args__ = (
1369 UniqueConstraint('users_group_id', 'permission_id',),
1370 UniqueConstraint('users_group_id', 'permission_id',),
1370 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1371 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1371 'mysql_charset': 'utf8'}
1372 'mysql_charset': 'utf8'}
1372 )
1373 )
1373 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1374 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1374 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1375 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1375 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1376 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1376
1377
1377 users_group = relationship('UsersGroup')
1378 users_group = relationship('UsersGroup')
1378 permission = relationship('Permission')
1379 permission = relationship('Permission')
1379
1380
1380
1381
1381 class UserRepoGroupToPerm(Base, BaseModel):
1382 class UserRepoGroupToPerm(Base, BaseModel):
1382 __tablename__ = 'user_repo_group_to_perm'
1383 __tablename__ = 'user_repo_group_to_perm'
1383 __table_args__ = (
1384 __table_args__ = (
1384 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1385 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1385 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1386 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1386 'mysql_charset': 'utf8'}
1387 'mysql_charset': 'utf8'}
1387 )
1388 )
1388
1389
1389 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1390 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1390 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1391 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1391 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1392 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1392 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1393 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1393
1394
1394 user = relationship('User')
1395 user = relationship('User')
1395 group = relationship('RepoGroup')
1396 group = relationship('RepoGroup')
1396 permission = relationship('Permission')
1397 permission = relationship('Permission')
1397
1398
1398
1399
1399 class UsersGroupRepoGroupToPerm(Base, BaseModel):
1400 class UsersGroupRepoGroupToPerm(Base, BaseModel):
1400 __tablename__ = 'users_group_repo_group_to_perm'
1401 __tablename__ = 'users_group_repo_group_to_perm'
1401 __table_args__ = (
1402 __table_args__ = (
1402 UniqueConstraint('users_group_id', 'group_id'),
1403 UniqueConstraint('users_group_id', 'group_id'),
1403 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1404 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1404 'mysql_charset': 'utf8'}
1405 'mysql_charset': 'utf8'}
1405 )
1406 )
1406
1407
1407 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1408 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1408 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1409 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1409 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1410 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1410 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1411 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1411
1412
1412 users_group = relationship('UsersGroup')
1413 users_group = relationship('UsersGroup')
1413 permission = relationship('Permission')
1414 permission = relationship('Permission')
1414 group = relationship('RepoGroup')
1415 group = relationship('RepoGroup')
1415
1416
1416
1417
1417 class Statistics(Base, BaseModel):
1418 class Statistics(Base, BaseModel):
1418 __tablename__ = 'statistics'
1419 __tablename__ = 'statistics'
1419 __table_args__ = (
1420 __table_args__ = (
1420 UniqueConstraint('repository_id'),
1421 UniqueConstraint('repository_id'),
1421 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1422 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1422 'mysql_charset': 'utf8'}
1423 'mysql_charset': 'utf8'}
1423 )
1424 )
1424 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1425 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1425 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1426 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1426 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1427 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1427 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1428 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1428 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1429 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1429 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1430 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1430
1431
1431 repository = relationship('Repository', single_parent=True)
1432 repository = relationship('Repository', single_parent=True)
1432
1433
1433
1434
1434 class UserFollowing(Base, BaseModel):
1435 class UserFollowing(Base, BaseModel):
1435 __tablename__ = 'user_followings'
1436 __tablename__ = 'user_followings'
1436 __table_args__ = (
1437 __table_args__ = (
1437 UniqueConstraint('user_id', 'follows_repository_id'),
1438 UniqueConstraint('user_id', 'follows_repository_id'),
1438 UniqueConstraint('user_id', 'follows_user_id'),
1439 UniqueConstraint('user_id', 'follows_user_id'),
1439 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1440 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1440 'mysql_charset': 'utf8'}
1441 'mysql_charset': 'utf8'}
1441 )
1442 )
1442
1443
1443 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1444 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1444 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1445 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1445 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1446 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1446 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1447 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1447 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1448 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1448
1449
1449 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1450 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1450
1451
1451 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1452 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1452 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1453 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1453
1454
1454 @classmethod
1455 @classmethod
1455 def get_repo_followers(cls, repo_id):
1456 def get_repo_followers(cls, repo_id):
1456 return cls.query().filter(cls.follows_repo_id == repo_id)
1457 return cls.query().filter(cls.follows_repo_id == repo_id)
1457
1458
1458
1459
1459 class CacheInvalidation(Base, BaseModel):
1460 class CacheInvalidation(Base, BaseModel):
1460 __tablename__ = 'cache_invalidation'
1461 __tablename__ = 'cache_invalidation'
1461 __table_args__ = (
1462 __table_args__ = (
1462 UniqueConstraint('cache_key'),
1463 UniqueConstraint('cache_key'),
1463 Index('key_idx', 'cache_key'),
1464 Index('key_idx', 'cache_key'),
1464 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1465 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1465 'mysql_charset': 'utf8'},
1466 'mysql_charset': 'utf8'},
1466 )
1467 )
1467 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1468 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1468 cache_key = Column("cache_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1469 cache_key = Column("cache_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1469 cache_args = Column("cache_args", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1470 cache_args = Column("cache_args", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1470 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1471 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1471
1472
1472 def __init__(self, cache_key, cache_args=''):
1473 def __init__(self, cache_key, cache_args=''):
1473 self.cache_key = cache_key
1474 self.cache_key = cache_key
1474 self.cache_args = cache_args
1475 self.cache_args = cache_args
1475 self.cache_active = False
1476 self.cache_active = False
1476
1477
1477 def __unicode__(self):
1478 def __unicode__(self):
1478 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1479 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1479 self.cache_id, self.cache_key)
1480 self.cache_id, self.cache_key)
1480
1481
1481 @property
1482 @property
1482 def prefix(self):
1483 def prefix(self):
1483 _split = self.cache_key.split(self.cache_args, 1)
1484 _split = self.cache_key.split(self.cache_args, 1)
1484 if _split and len(_split) == 2:
1485 if _split and len(_split) == 2:
1485 return _split[0]
1486 return _split[0]
1486 return ''
1487 return ''
1487
1488
1488 @classmethod
1489 @classmethod
1489 def clear_cache(cls):
1490 def clear_cache(cls):
1490 cls.query().delete()
1491 cls.query().delete()
1491
1492
1492 @classmethod
1493 @classmethod
1493 def _get_key(cls, key):
1494 def _get_key(cls, key):
1494 """
1495 """
1495 Wrapper for generating a key, together with a prefix
1496 Wrapper for generating a key, together with a prefix
1496
1497
1497 :param key:
1498 :param key:
1498 """
1499 """
1499 import rhodecode
1500 import rhodecode
1500 prefix = ''
1501 prefix = ''
1501 org_key = key
1502 org_key = key
1502 iid = rhodecode.CONFIG.get('instance_id')
1503 iid = rhodecode.CONFIG.get('instance_id')
1503 if iid:
1504 if iid:
1504 prefix = iid
1505 prefix = iid
1505
1506
1506 return "%s%s" % (prefix, key), prefix, org_key
1507 return "%s%s" % (prefix, key), prefix, org_key
1507
1508
1508 @classmethod
1509 @classmethod
1509 def get_by_key(cls, key):
1510 def get_by_key(cls, key):
1510 return cls.query().filter(cls.cache_key == key).scalar()
1511 return cls.query().filter(cls.cache_key == key).scalar()
1511
1512
1512 @classmethod
1513 @classmethod
1513 def get_by_repo_name(cls, repo_name):
1514 def get_by_repo_name(cls, repo_name):
1514 return cls.query().filter(cls.cache_args == repo_name).all()
1515 return cls.query().filter(cls.cache_args == repo_name).all()
1515
1516
1516 @classmethod
1517 @classmethod
1517 def _get_or_create_key(cls, key, repo_name, commit=True):
1518 def _get_or_create_key(cls, key, repo_name, commit=True):
1518 inv_obj = Session().query(cls).filter(cls.cache_key == key).scalar()
1519 inv_obj = Session().query(cls).filter(cls.cache_key == key).scalar()
1519 if not inv_obj:
1520 if not inv_obj:
1520 try:
1521 try:
1521 inv_obj = CacheInvalidation(key, repo_name)
1522 inv_obj = CacheInvalidation(key, repo_name)
1522 Session().add(inv_obj)
1523 Session().add(inv_obj)
1523 if commit:
1524 if commit:
1524 Session().commit()
1525 Session().commit()
1525 except Exception:
1526 except Exception:
1526 log.error(traceback.format_exc())
1527 log.error(traceback.format_exc())
1527 Session().rollback()
1528 Session().rollback()
1528 return inv_obj
1529 return inv_obj
1529
1530
1530 @classmethod
1531 @classmethod
1531 def invalidate(cls, key):
1532 def invalidate(cls, key):
1532 """
1533 """
1533 Returns Invalidation object if this given key should be invalidated
1534 Returns Invalidation object if this given key should be invalidated
1534 None otherwise. `cache_active = False` means that this cache
1535 None otherwise. `cache_active = False` means that this cache
1535 state is not valid and needs to be invalidated
1536 state is not valid and needs to be invalidated
1536
1537
1537 :param key:
1538 :param key:
1538 """
1539 """
1539 repo_name = key
1540 repo_name = key
1540 repo_name = remove_suffix(repo_name, '_README')
1541 repo_name = remove_suffix(repo_name, '_README')
1541 repo_name = remove_suffix(repo_name, '_RSS')
1542 repo_name = remove_suffix(repo_name, '_RSS')
1542 repo_name = remove_suffix(repo_name, '_ATOM')
1543 repo_name = remove_suffix(repo_name, '_ATOM')
1543
1544
1544 # adds instance prefix
1545 # adds instance prefix
1545 key, _prefix, _org_key = cls._get_key(key)
1546 key, _prefix, _org_key = cls._get_key(key)
1546 inv = cls._get_or_create_key(key, repo_name)
1547 inv = cls._get_or_create_key(key, repo_name)
1547
1548
1548 if inv and inv.cache_active is False:
1549 if inv and inv.cache_active is False:
1549 return inv
1550 return inv
1550
1551
1551 @classmethod
1552 @classmethod
1552 def set_invalidate(cls, key=None, repo_name=None):
1553 def set_invalidate(cls, key=None, repo_name=None):
1553 """
1554 """
1554 Mark this Cache key for invalidation, either by key or whole
1555 Mark this Cache key for invalidation, either by key or whole
1555 cache sets based on repo_name
1556 cache sets based on repo_name
1556
1557
1557 :param key:
1558 :param key:
1558 """
1559 """
1559 if key:
1560 if key:
1560 key, _prefix, _org_key = cls._get_key(key)
1561 key, _prefix, _org_key = cls._get_key(key)
1561 inv_objs = Session().query(cls).filter(cls.cache_key == key).all()
1562 inv_objs = Session().query(cls).filter(cls.cache_key == key).all()
1562 elif repo_name:
1563 elif repo_name:
1563 inv_objs = Session().query(cls).filter(cls.cache_args == repo_name).all()
1564 inv_objs = Session().query(cls).filter(cls.cache_args == repo_name).all()
1564
1565
1565 log.debug('marking %s key[s] for invalidation based on key=%s,repo_name=%s'
1566 log.debug('marking %s key[s] for invalidation based on key=%s,repo_name=%s'
1566 % (len(inv_objs), key, repo_name))
1567 % (len(inv_objs), key, repo_name))
1567 try:
1568 try:
1568 for inv_obj in inv_objs:
1569 for inv_obj in inv_objs:
1569 inv_obj.cache_active = False
1570 inv_obj.cache_active = False
1570 Session().add(inv_obj)
1571 Session().add(inv_obj)
1571 Session().commit()
1572 Session().commit()
1572 except Exception:
1573 except Exception:
1573 log.error(traceback.format_exc())
1574 log.error(traceback.format_exc())
1574 Session().rollback()
1575 Session().rollback()
1575
1576
1576 @classmethod
1577 @classmethod
1577 def set_valid(cls, key):
1578 def set_valid(cls, key):
1578 """
1579 """
1579 Mark this cache key as active and currently cached
1580 Mark this cache key as active and currently cached
1580
1581
1581 :param key:
1582 :param key:
1582 """
1583 """
1583 inv_obj = cls.get_by_key(key)
1584 inv_obj = cls.get_by_key(key)
1584 inv_obj.cache_active = True
1585 inv_obj.cache_active = True
1585 Session().add(inv_obj)
1586 Session().add(inv_obj)
1586 Session().commit()
1587 Session().commit()
1587
1588
1588 @classmethod
1589 @classmethod
1589 def get_cache_map(cls):
1590 def get_cache_map(cls):
1590
1591
1591 class cachemapdict(dict):
1592 class cachemapdict(dict):
1592
1593
1593 def __init__(self, *args, **kwargs):
1594 def __init__(self, *args, **kwargs):
1594 fixkey = kwargs.get('fixkey')
1595 fixkey = kwargs.get('fixkey')
1595 if fixkey:
1596 if fixkey:
1596 del kwargs['fixkey']
1597 del kwargs['fixkey']
1597 self.fixkey = fixkey
1598 self.fixkey = fixkey
1598 super(cachemapdict, self).__init__(*args, **kwargs)
1599 super(cachemapdict, self).__init__(*args, **kwargs)
1599
1600
1600 def __getattr__(self, name):
1601 def __getattr__(self, name):
1601 key = name
1602 key = name
1602 if self.fixkey:
1603 if self.fixkey:
1603 key, _prefix, _org_key = cls._get_key(key)
1604 key, _prefix, _org_key = cls._get_key(key)
1604 if key in self.__dict__:
1605 if key in self.__dict__:
1605 return self.__dict__[key]
1606 return self.__dict__[key]
1606 else:
1607 else:
1607 return self[key]
1608 return self[key]
1608
1609
1609 def __getitem__(self, key):
1610 def __getitem__(self, key):
1610 if self.fixkey:
1611 if self.fixkey:
1611 key, _prefix, _org_key = cls._get_key(key)
1612 key, _prefix, _org_key = cls._get_key(key)
1612 try:
1613 try:
1613 return super(cachemapdict, self).__getitem__(key)
1614 return super(cachemapdict, self).__getitem__(key)
1614 except KeyError:
1615 except KeyError:
1615 return
1616 return
1616
1617
1617 cache_map = cachemapdict(fixkey=True)
1618 cache_map = cachemapdict(fixkey=True)
1618 for obj in cls.query().all():
1619 for obj in cls.query().all():
1619 cache_map[obj.cache_key] = cachemapdict(obj.get_dict())
1620 cache_map[obj.cache_key] = cachemapdict(obj.get_dict())
1620 return cache_map
1621 return cache_map
1621
1622
1622
1623
1623 class ChangesetComment(Base, BaseModel):
1624 class ChangesetComment(Base, BaseModel):
1624 __tablename__ = 'changeset_comments'
1625 __tablename__ = 'changeset_comments'
1625 __table_args__ = (
1626 __table_args__ = (
1626 Index('cc_revision_idx', 'revision'),
1627 Index('cc_revision_idx', 'revision'),
1627 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1628 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1628 'mysql_charset': 'utf8'},
1629 'mysql_charset': 'utf8'},
1629 )
1630 )
1630 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1631 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1631 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1632 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1632 revision = Column('revision', String(40), nullable=True)
1633 revision = Column('revision', String(40), nullable=True)
1633 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1634 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1634 line_no = Column('line_no', Unicode(10), nullable=True)
1635 line_no = Column('line_no', Unicode(10), nullable=True)
1635 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
1636 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
1636 f_path = Column('f_path', Unicode(1000), nullable=True)
1637 f_path = Column('f_path', Unicode(1000), nullable=True)
1637 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1638 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1638 text = Column('text', UnicodeText(25000), nullable=False)
1639 text = Column('text', UnicodeText(25000), nullable=False)
1639 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1640 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1640 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1641 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1641
1642
1642 author = relationship('User', lazy='joined')
1643 author = relationship('User', lazy='joined')
1643 repo = relationship('Repository')
1644 repo = relationship('Repository')
1644 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1645 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1645 pull_request = relationship('PullRequest', lazy='joined')
1646 pull_request = relationship('PullRequest', lazy='joined')
1646
1647
1647 @classmethod
1648 @classmethod
1648 def get_users(cls, revision=None, pull_request_id=None):
1649 def get_users(cls, revision=None, pull_request_id=None):
1649 """
1650 """
1650 Returns user associated with this ChangesetComment. ie those
1651 Returns user associated with this ChangesetComment. ie those
1651 who actually commented
1652 who actually commented
1652
1653
1653 :param cls:
1654 :param cls:
1654 :param revision:
1655 :param revision:
1655 """
1656 """
1656 q = Session().query(User)\
1657 q = Session().query(User)\
1657 .join(ChangesetComment.author)
1658 .join(ChangesetComment.author)
1658 if revision:
1659 if revision:
1659 q = q.filter(cls.revision == revision)
1660 q = q.filter(cls.revision == revision)
1660 elif pull_request_id:
1661 elif pull_request_id:
1661 q = q.filter(cls.pull_request_id == pull_request_id)
1662 q = q.filter(cls.pull_request_id == pull_request_id)
1662 return q.all()
1663 return q.all()
1663
1664
1664
1665
1665 class ChangesetStatus(Base, BaseModel):
1666 class ChangesetStatus(Base, BaseModel):
1666 __tablename__ = 'changeset_statuses'
1667 __tablename__ = 'changeset_statuses'
1667 __table_args__ = (
1668 __table_args__ = (
1668 Index('cs_revision_idx', 'revision'),
1669 Index('cs_revision_idx', 'revision'),
1669 Index('cs_version_idx', 'version'),
1670 Index('cs_version_idx', 'version'),
1670 UniqueConstraint('repo_id', 'revision', 'version'),
1671 UniqueConstraint('repo_id', 'revision', 'version'),
1671 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1672 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1672 'mysql_charset': 'utf8'}
1673 'mysql_charset': 'utf8'}
1673 )
1674 )
1674 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1675 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1675 STATUS_APPROVED = 'approved'
1676 STATUS_APPROVED = 'approved'
1676 STATUS_REJECTED = 'rejected'
1677 STATUS_REJECTED = 'rejected'
1677 STATUS_UNDER_REVIEW = 'under_review'
1678 STATUS_UNDER_REVIEW = 'under_review'
1678
1679
1679 STATUSES = [
1680 STATUSES = [
1680 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1681 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1681 (STATUS_APPROVED, _("Approved")),
1682 (STATUS_APPROVED, _("Approved")),
1682 (STATUS_REJECTED, _("Rejected")),
1683 (STATUS_REJECTED, _("Rejected")),
1683 (STATUS_UNDER_REVIEW, _("Under Review")),
1684 (STATUS_UNDER_REVIEW, _("Under Review")),
1684 ]
1685 ]
1685
1686
1686 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1687 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1687 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1688 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1688 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1689 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1689 revision = Column('revision', String(40), nullable=False)
1690 revision = Column('revision', String(40), nullable=False)
1690 status = Column('status', String(128), nullable=False, default=DEFAULT)
1691 status = Column('status', String(128), nullable=False, default=DEFAULT)
1691 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1692 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1692 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1693 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1693 version = Column('version', Integer(), nullable=False, default=0)
1694 version = Column('version', Integer(), nullable=False, default=0)
1694 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1695 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1695
1696
1696 author = relationship('User', lazy='joined')
1697 author = relationship('User', lazy='joined')
1697 repo = relationship('Repository')
1698 repo = relationship('Repository')
1698 comment = relationship('ChangesetComment', lazy='joined')
1699 comment = relationship('ChangesetComment', lazy='joined')
1699 pull_request = relationship('PullRequest', lazy='joined')
1700 pull_request = relationship('PullRequest', lazy='joined')
1700
1701
1701 def __unicode__(self):
1702 def __unicode__(self):
1702 return u"<%s('%s:%s')>" % (
1703 return u"<%s('%s:%s')>" % (
1703 self.__class__.__name__,
1704 self.__class__.__name__,
1704 self.status, self.author
1705 self.status, self.author
1705 )
1706 )
1706
1707
1707 @classmethod
1708 @classmethod
1708 def get_status_lbl(cls, value):
1709 def get_status_lbl(cls, value):
1709 return dict(cls.STATUSES).get(value)
1710 return dict(cls.STATUSES).get(value)
1710
1711
1711 @property
1712 @property
1712 def status_lbl(self):
1713 def status_lbl(self):
1713 return ChangesetStatus.get_status_lbl(self.status)
1714 return ChangesetStatus.get_status_lbl(self.status)
1714
1715
1715
1716
1716 class PullRequest(Base, BaseModel):
1717 class PullRequest(Base, BaseModel):
1717 __tablename__ = 'pull_requests'
1718 __tablename__ = 'pull_requests'
1718 __table_args__ = (
1719 __table_args__ = (
1719 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1720 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1720 'mysql_charset': 'utf8'},
1721 'mysql_charset': 'utf8'},
1721 )
1722 )
1722
1723
1723 STATUS_NEW = u'new'
1724 STATUS_NEW = u'new'
1724 STATUS_OPEN = u'open'
1725 STATUS_OPEN = u'open'
1725 STATUS_CLOSED = u'closed'
1726 STATUS_CLOSED = u'closed'
1726
1727
1727 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1728 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1728 title = Column('title', Unicode(256), nullable=True)
1729 title = Column('title', Unicode(256), nullable=True)
1729 description = Column('description', UnicodeText(10240), nullable=True)
1730 description = Column('description', UnicodeText(10240), nullable=True)
1730 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1731 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1731 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1732 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1732 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1733 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1733 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1734 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1734 _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
1735 _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
1735 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1736 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1736 org_ref = Column('org_ref', Unicode(256), nullable=False)
1737 org_ref = Column('org_ref', Unicode(256), nullable=False)
1737 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1738 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1738 other_ref = Column('other_ref', Unicode(256), nullable=False)
1739 other_ref = Column('other_ref', Unicode(256), nullable=False)
1739
1740
1740 @hybrid_property
1741 @hybrid_property
1741 def revisions(self):
1742 def revisions(self):
1742 return self._revisions.split(':')
1743 return self._revisions.split(':')
1743
1744
1744 @revisions.setter
1745 @revisions.setter
1745 def revisions(self, val):
1746 def revisions(self, val):
1746 self._revisions = ':'.join(val)
1747 self._revisions = ':'.join(val)
1747
1748
1748 author = relationship('User', lazy='joined')
1749 author = relationship('User', lazy='joined')
1749 reviewers = relationship('PullRequestReviewers',
1750 reviewers = relationship('PullRequestReviewers',
1750 cascade="all, delete, delete-orphan")
1751 cascade="all, delete, delete-orphan")
1751 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1752 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1752 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1753 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1753 statuses = relationship('ChangesetStatus')
1754 statuses = relationship('ChangesetStatus')
1754 comments = relationship('ChangesetComment',
1755 comments = relationship('ChangesetComment',
1755 cascade="all, delete, delete-orphan")
1756 cascade="all, delete, delete-orphan")
1756
1757
1757 def is_closed(self):
1758 def is_closed(self):
1758 return self.status == self.STATUS_CLOSED
1759 return self.status == self.STATUS_CLOSED
1759
1760
1760 def __json__(self):
1761 def __json__(self):
1761 return dict(
1762 return dict(
1762 revisions=self.revisions
1763 revisions=self.revisions
1763 )
1764 )
1764
1765
1765
1766
1766 class PullRequestReviewers(Base, BaseModel):
1767 class PullRequestReviewers(Base, BaseModel):
1767 __tablename__ = 'pull_request_reviewers'
1768 __tablename__ = 'pull_request_reviewers'
1768 __table_args__ = (
1769 __table_args__ = (
1769 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1770 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1770 'mysql_charset': 'utf8'},
1771 'mysql_charset': 'utf8'},
1771 )
1772 )
1772
1773
1773 def __init__(self, user=None, pull_request=None):
1774 def __init__(self, user=None, pull_request=None):
1774 self.user = user
1775 self.user = user
1775 self.pull_request = pull_request
1776 self.pull_request = pull_request
1776
1777
1777 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1778 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1778 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1779 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1779 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1780 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1780
1781
1781 user = relationship('User')
1782 user = relationship('User')
1782 pull_request = relationship('PullRequest')
1783 pull_request = relationship('PullRequest')
1783
1784
1784
1785
1785 class Notification(Base, BaseModel):
1786 class Notification(Base, BaseModel):
1786 __tablename__ = 'notifications'
1787 __tablename__ = 'notifications'
1787 __table_args__ = (
1788 __table_args__ = (
1788 Index('notification_type_idx', 'type'),
1789 Index('notification_type_idx', 'type'),
1789 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1790 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1790 'mysql_charset': 'utf8'},
1791 'mysql_charset': 'utf8'},
1791 )
1792 )
1792
1793
1793 TYPE_CHANGESET_COMMENT = u'cs_comment'
1794 TYPE_CHANGESET_COMMENT = u'cs_comment'
1794 TYPE_MESSAGE = u'message'
1795 TYPE_MESSAGE = u'message'
1795 TYPE_MENTION = u'mention'
1796 TYPE_MENTION = u'mention'
1796 TYPE_REGISTRATION = u'registration'
1797 TYPE_REGISTRATION = u'registration'
1797 TYPE_PULL_REQUEST = u'pull_request'
1798 TYPE_PULL_REQUEST = u'pull_request'
1798 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1799 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1799
1800
1800 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1801 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1801 subject = Column('subject', Unicode(512), nullable=True)
1802 subject = Column('subject', Unicode(512), nullable=True)
1802 body = Column('body', UnicodeText(50000), nullable=True)
1803 body = Column('body', UnicodeText(50000), nullable=True)
1803 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1804 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1804 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1805 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1805 type_ = Column('type', Unicode(256))
1806 type_ = Column('type', Unicode(256))
1806
1807
1807 created_by_user = relationship('User')
1808 created_by_user = relationship('User')
1808 notifications_to_users = relationship('UserNotification', lazy='joined',
1809 notifications_to_users = relationship('UserNotification', lazy='joined',
1809 cascade="all, delete, delete-orphan")
1810 cascade="all, delete, delete-orphan")
1810
1811
1811 @property
1812 @property
1812 def recipients(self):
1813 def recipients(self):
1813 return [x.user for x in UserNotification.query()\
1814 return [x.user for x in UserNotification.query()\
1814 .filter(UserNotification.notification == self)\
1815 .filter(UserNotification.notification == self)\
1815 .order_by(UserNotification.user_id.asc()).all()]
1816 .order_by(UserNotification.user_id.asc()).all()]
1816
1817
1817 @classmethod
1818 @classmethod
1818 def create(cls, created_by, subject, body, recipients, type_=None):
1819 def create(cls, created_by, subject, body, recipients, type_=None):
1819 if type_ is None:
1820 if type_ is None:
1820 type_ = Notification.TYPE_MESSAGE
1821 type_ = Notification.TYPE_MESSAGE
1821
1822
1822 notification = cls()
1823 notification = cls()
1823 notification.created_by_user = created_by
1824 notification.created_by_user = created_by
1824 notification.subject = subject
1825 notification.subject = subject
1825 notification.body = body
1826 notification.body = body
1826 notification.type_ = type_
1827 notification.type_ = type_
1827 notification.created_on = datetime.datetime.now()
1828 notification.created_on = datetime.datetime.now()
1828
1829
1829 for u in recipients:
1830 for u in recipients:
1830 assoc = UserNotification()
1831 assoc = UserNotification()
1831 assoc.notification = notification
1832 assoc.notification = notification
1832 u.notifications.append(assoc)
1833 u.notifications.append(assoc)
1833 Session().add(notification)
1834 Session().add(notification)
1834 return notification
1835 return notification
1835
1836
1836 @property
1837 @property
1837 def description(self):
1838 def description(self):
1838 from rhodecode.model.notification import NotificationModel
1839 from rhodecode.model.notification import NotificationModel
1839 return NotificationModel().make_description(self)
1840 return NotificationModel().make_description(self)
1840
1841
1841
1842
1842 class UserNotification(Base, BaseModel):
1843 class UserNotification(Base, BaseModel):
1843 __tablename__ = 'user_to_notification'
1844 __tablename__ = 'user_to_notification'
1844 __table_args__ = (
1845 __table_args__ = (
1845 UniqueConstraint('user_id', 'notification_id'),
1846 UniqueConstraint('user_id', 'notification_id'),
1846 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1847 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1847 'mysql_charset': 'utf8'}
1848 'mysql_charset': 'utf8'}
1848 )
1849 )
1849 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1850 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1850 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1851 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1851 read = Column('read', Boolean, default=False)
1852 read = Column('read', Boolean, default=False)
1852 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1853 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1853
1854
1854 user = relationship('User', lazy="joined")
1855 user = relationship('User', lazy="joined")
1855 notification = relationship('Notification', lazy="joined",
1856 notification = relationship('Notification', lazy="joined",
1856 order_by=lambda: Notification.created_on.desc(),)
1857 order_by=lambda: Notification.created_on.desc(),)
1857
1858
1858 def mark_as_read(self):
1859 def mark_as_read(self):
1859 self.read = True
1860 self.read = True
1860 Session().add(self)
1861 Session().add(self)
1861
1862
1862
1863
1863 class DbMigrateVersion(Base, BaseModel):
1864 class DbMigrateVersion(Base, BaseModel):
1864 __tablename__ = 'db_migrate_version'
1865 __tablename__ = 'db_migrate_version'
1865 __table_args__ = (
1866 __table_args__ = (
1866 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1867 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1867 'mysql_charset': 'utf8'},
1868 'mysql_charset': 'utf8'},
1868 )
1869 )
1869 repository_id = Column('repository_id', String(250), primary_key=True)
1870 repository_id = Column('repository_id', String(250), primary_key=True)
1870 repository_path = Column('repository_path', Text)
1871 repository_path = Column('repository_path', Text)
1871 version = Column('version', Integer)
1872 version = Column('version', Integer)
@@ -1,737 +1,736 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.user
3 rhodecode.model.user
4 ~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~
5
5
6 users model for RhodeCode
6 users model for RhodeCode
7
7
8 :created_on: Apr 9, 2010
8 :created_on: Apr 9, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import logging
26 import logging
27 import traceback
27 import traceback
28 import itertools
28 import itertools
29 import collections
29 import collections
30 import functools
31 from pylons import url
30 from pylons import url
32 from pylons.i18n.translation import _
31 from pylons.i18n.translation import _
33
32
34 from sqlalchemy.exc import DatabaseError
33 from sqlalchemy.exc import DatabaseError
35 from sqlalchemy.orm import joinedload
34 from sqlalchemy.orm import joinedload
36
35
37 from rhodecode.lib.utils2 import safe_unicode, generate_api_key
36 from rhodecode.lib.utils2 import safe_unicode, generate_api_key
38 from rhodecode.lib.caching_query import FromCache
37 from rhodecode.lib.caching_query import FromCache
39 from rhodecode.model import BaseModel
38 from rhodecode.model import BaseModel
40 from rhodecode.model.db import User, UserRepoToPerm, Repository, Permission, \
39 from rhodecode.model.db import User, UserRepoToPerm, Repository, Permission, \
41 UserToPerm, UsersGroupRepoToPerm, UsersGroupToPerm, UsersGroupMember, \
40 UserToPerm, UsersGroupRepoToPerm, UsersGroupToPerm, UsersGroupMember, \
42 Notification, RepoGroup, UserRepoGroupToPerm, UsersGroupRepoGroupToPerm, \
41 Notification, RepoGroup, UserRepoGroupToPerm, UsersGroupRepoGroupToPerm, \
43 UserEmailMap, UserIpMap
42 UserEmailMap, UserIpMap
44 from rhodecode.lib.exceptions import DefaultUserException, \
43 from rhodecode.lib.exceptions import DefaultUserException, \
45 UserOwnsReposException
44 UserOwnsReposException
46
45
47
46
48 log = logging.getLogger(__name__)
47 log = logging.getLogger(__name__)
49
48
50 PERM_WEIGHTS = Permission.PERM_WEIGHTS
49 PERM_WEIGHTS = Permission.PERM_WEIGHTS
51
50
52
51
53 class UserModel(BaseModel):
52 class UserModel(BaseModel):
54 cls = User
53 cls = User
55
54
56 def get(self, user_id, cache=False):
55 def get(self, user_id, cache=False):
57 user = self.sa.query(User)
56 user = self.sa.query(User)
58 if cache:
57 if cache:
59 user = user.options(FromCache("sql_cache_short",
58 user = user.options(FromCache("sql_cache_short",
60 "get_user_%s" % user_id))
59 "get_user_%s" % user_id))
61 return user.get(user_id)
60 return user.get(user_id)
62
61
63 def get_user(self, user):
62 def get_user(self, user):
64 return self._get_user(user)
63 return self._get_user(user)
65
64
66 def get_by_username(self, username, cache=False, case_insensitive=False):
65 def get_by_username(self, username, cache=False, case_insensitive=False):
67
66
68 if case_insensitive:
67 if case_insensitive:
69 user = self.sa.query(User).filter(User.username.ilike(username))
68 user = self.sa.query(User).filter(User.username.ilike(username))
70 else:
69 else:
71 user = self.sa.query(User)\
70 user = self.sa.query(User)\
72 .filter(User.username == username)
71 .filter(User.username == username)
73 if cache:
72 if cache:
74 user = user.options(FromCache("sql_cache_short",
73 user = user.options(FromCache("sql_cache_short",
75 "get_user_%s" % username))
74 "get_user_%s" % username))
76 return user.scalar()
75 return user.scalar()
77
76
78 def get_by_email(self, email, cache=False, case_insensitive=False):
77 def get_by_email(self, email, cache=False, case_insensitive=False):
79 return User.get_by_email(email, case_insensitive, cache)
78 return User.get_by_email(email, case_insensitive, cache)
80
79
81 def get_by_api_key(self, api_key, cache=False):
80 def get_by_api_key(self, api_key, cache=False):
82 return User.get_by_api_key(api_key, cache)
81 return User.get_by_api_key(api_key, cache)
83
82
84 def create(self, form_data):
83 def create(self, form_data):
85 from rhodecode.lib.auth import get_crypt_password
84 from rhodecode.lib.auth import get_crypt_password
86 try:
85 try:
87 new_user = User()
86 new_user = User()
88 for k, v in form_data.items():
87 for k, v in form_data.items():
89 if k == 'password':
88 if k == 'password':
90 v = get_crypt_password(v)
89 v = get_crypt_password(v)
91 if k == 'firstname':
90 if k == 'firstname':
92 k = 'name'
91 k = 'name'
93 setattr(new_user, k, v)
92 setattr(new_user, k, v)
94
93
95 new_user.api_key = generate_api_key(form_data['username'])
94 new_user.api_key = generate_api_key(form_data['username'])
96 self.sa.add(new_user)
95 self.sa.add(new_user)
97 return new_user
96 return new_user
98 except:
97 except:
99 log.error(traceback.format_exc())
98 log.error(traceback.format_exc())
100 raise
99 raise
101
100
102 def create_or_update(self, username, password, email, firstname='',
101 def create_or_update(self, username, password, email, firstname='',
103 lastname='', active=True, admin=False, ldap_dn=None):
102 lastname='', active=True, admin=False, ldap_dn=None):
104 """
103 """
105 Creates a new instance if not found, or updates current one
104 Creates a new instance if not found, or updates current one
106
105
107 :param username:
106 :param username:
108 :param password:
107 :param password:
109 :param email:
108 :param email:
110 :param active:
109 :param active:
111 :param firstname:
110 :param firstname:
112 :param lastname:
111 :param lastname:
113 :param active:
112 :param active:
114 :param admin:
113 :param admin:
115 :param ldap_dn:
114 :param ldap_dn:
116 """
115 """
117
116
118 from rhodecode.lib.auth import get_crypt_password
117 from rhodecode.lib.auth import get_crypt_password
119
118
120 log.debug('Checking for %s account in RhodeCode database' % username)
119 log.debug('Checking for %s account in RhodeCode database' % username)
121 user = User.get_by_username(username, case_insensitive=True)
120 user = User.get_by_username(username, case_insensitive=True)
122 if user is None:
121 if user is None:
123 log.debug('creating new user %s' % username)
122 log.debug('creating new user %s' % username)
124 new_user = User()
123 new_user = User()
125 edit = False
124 edit = False
126 else:
125 else:
127 log.debug('updating user %s' % username)
126 log.debug('updating user %s' % username)
128 new_user = user
127 new_user = user
129 edit = True
128 edit = True
130
129
131 try:
130 try:
132 new_user.username = username
131 new_user.username = username
133 new_user.admin = admin
132 new_user.admin = admin
134 # set password only if creating an user or password is changed
133 # set password only if creating an user or password is changed
135 if edit is False or user.password != password:
134 if edit is False or user.password != password:
136 new_user.password = get_crypt_password(password)
135 new_user.password = get_crypt_password(password)
137 new_user.api_key = generate_api_key(username)
136 new_user.api_key = generate_api_key(username)
138 new_user.email = email
137 new_user.email = email
139 new_user.active = active
138 new_user.active = active
140 new_user.ldap_dn = safe_unicode(ldap_dn) if ldap_dn else None
139 new_user.ldap_dn = safe_unicode(ldap_dn) if ldap_dn else None
141 new_user.name = firstname
140 new_user.name = firstname
142 new_user.lastname = lastname
141 new_user.lastname = lastname
143 self.sa.add(new_user)
142 self.sa.add(new_user)
144 return new_user
143 return new_user
145 except (DatabaseError,):
144 except (DatabaseError,):
146 log.error(traceback.format_exc())
145 log.error(traceback.format_exc())
147 raise
146 raise
148
147
149 def create_for_container_auth(self, username, attrs):
148 def create_for_container_auth(self, username, attrs):
150 """
149 """
151 Creates the given user if it's not already in the database
150 Creates the given user if it's not already in the database
152
151
153 :param username:
152 :param username:
154 :param attrs:
153 :param attrs:
155 """
154 """
156 if self.get_by_username(username, case_insensitive=True) is None:
155 if self.get_by_username(username, case_insensitive=True) is None:
157
156
158 # autogenerate email for container account without one
157 # autogenerate email for container account without one
159 generate_email = lambda usr: '%s@container_auth.account' % usr
158 generate_email = lambda usr: '%s@container_auth.account' % usr
160
159
161 try:
160 try:
162 new_user = User()
161 new_user = User()
163 new_user.username = username
162 new_user.username = username
164 new_user.password = None
163 new_user.password = None
165 new_user.api_key = generate_api_key(username)
164 new_user.api_key = generate_api_key(username)
166 new_user.email = attrs['email']
165 new_user.email = attrs['email']
167 new_user.active = attrs.get('active', True)
166 new_user.active = attrs.get('active', True)
168 new_user.name = attrs['name'] or generate_email(username)
167 new_user.name = attrs['name'] or generate_email(username)
169 new_user.lastname = attrs['lastname']
168 new_user.lastname = attrs['lastname']
170
169
171 self.sa.add(new_user)
170 self.sa.add(new_user)
172 return new_user
171 return new_user
173 except (DatabaseError,):
172 except (DatabaseError,):
174 log.error(traceback.format_exc())
173 log.error(traceback.format_exc())
175 self.sa.rollback()
174 self.sa.rollback()
176 raise
175 raise
177 log.debug('User %s already exists. Skipping creation of account'
176 log.debug('User %s already exists. Skipping creation of account'
178 ' for container auth.', username)
177 ' for container auth.', username)
179 return None
178 return None
180
179
181 def create_ldap(self, username, password, user_dn, attrs):
180 def create_ldap(self, username, password, user_dn, attrs):
182 """
181 """
183 Checks if user is in database, if not creates this user marked
182 Checks if user is in database, if not creates this user marked
184 as ldap user
183 as ldap user
185
184
186 :param username:
185 :param username:
187 :param password:
186 :param password:
188 :param user_dn:
187 :param user_dn:
189 :param attrs:
188 :param attrs:
190 """
189 """
191 from rhodecode.lib.auth import get_crypt_password
190 from rhodecode.lib.auth import get_crypt_password
192 log.debug('Checking for such ldap account in RhodeCode database')
191 log.debug('Checking for such ldap account in RhodeCode database')
193 if self.get_by_username(username, case_insensitive=True) is None:
192 if self.get_by_username(username, case_insensitive=True) is None:
194
193
195 # autogenerate email for ldap account without one
194 # autogenerate email for ldap account without one
196 generate_email = lambda usr: '%s@ldap.account' % usr
195 generate_email = lambda usr: '%s@ldap.account' % usr
197
196
198 try:
197 try:
199 new_user = User()
198 new_user = User()
200 username = username.lower()
199 username = username.lower()
201 # add ldap account always lowercase
200 # add ldap account always lowercase
202 new_user.username = username
201 new_user.username = username
203 new_user.password = get_crypt_password(password)
202 new_user.password = get_crypt_password(password)
204 new_user.api_key = generate_api_key(username)
203 new_user.api_key = generate_api_key(username)
205 new_user.email = attrs['email'] or generate_email(username)
204 new_user.email = attrs['email'] or generate_email(username)
206 new_user.active = attrs.get('active', True)
205 new_user.active = attrs.get('active', True)
207 new_user.ldap_dn = safe_unicode(user_dn)
206 new_user.ldap_dn = safe_unicode(user_dn)
208 new_user.name = attrs['name']
207 new_user.name = attrs['name']
209 new_user.lastname = attrs['lastname']
208 new_user.lastname = attrs['lastname']
210
209
211 self.sa.add(new_user)
210 self.sa.add(new_user)
212 return new_user
211 return new_user
213 except (DatabaseError,):
212 except (DatabaseError,):
214 log.error(traceback.format_exc())
213 log.error(traceback.format_exc())
215 self.sa.rollback()
214 self.sa.rollback()
216 raise
215 raise
217 log.debug('this %s user exists skipping creation of ldap account',
216 log.debug('this %s user exists skipping creation of ldap account',
218 username)
217 username)
219 return None
218 return None
220
219
221 def create_registration(self, form_data):
220 def create_registration(self, form_data):
222 from rhodecode.model.notification import NotificationModel
221 from rhodecode.model.notification import NotificationModel
223
222
224 try:
223 try:
225 form_data['admin'] = False
224 form_data['admin'] = False
226 new_user = self.create(form_data)
225 new_user = self.create(form_data)
227
226
228 self.sa.add(new_user)
227 self.sa.add(new_user)
229 self.sa.flush()
228 self.sa.flush()
230
229
231 # notification to admins
230 # notification to admins
232 subject = _('new user registration')
231 subject = _('new user registration')
233 body = ('New user registration\n'
232 body = ('New user registration\n'
234 '---------------------\n'
233 '---------------------\n'
235 '- Username: %s\n'
234 '- Username: %s\n'
236 '- Full Name: %s\n'
235 '- Full Name: %s\n'
237 '- Email: %s\n')
236 '- Email: %s\n')
238 body = body % (new_user.username, new_user.full_name,
237 body = body % (new_user.username, new_user.full_name,
239 new_user.email)
238 new_user.email)
240 edit_url = url('edit_user', id=new_user.user_id, qualified=True)
239 edit_url = url('edit_user', id=new_user.user_id, qualified=True)
241 kw = {'registered_user_url': edit_url}
240 kw = {'registered_user_url': edit_url}
242 NotificationModel().create(created_by=new_user, subject=subject,
241 NotificationModel().create(created_by=new_user, subject=subject,
243 body=body, recipients=None,
242 body=body, recipients=None,
244 type_=Notification.TYPE_REGISTRATION,
243 type_=Notification.TYPE_REGISTRATION,
245 email_kwargs=kw)
244 email_kwargs=kw)
246
245
247 except:
246 except:
248 log.error(traceback.format_exc())
247 log.error(traceback.format_exc())
249 raise
248 raise
250
249
251 def update(self, user_id, form_data, skip_attrs=[]):
250 def update(self, user_id, form_data, skip_attrs=[]):
252 from rhodecode.lib.auth import get_crypt_password
251 from rhodecode.lib.auth import get_crypt_password
253 try:
252 try:
254 user = self.get(user_id, cache=False)
253 user = self.get(user_id, cache=False)
255 if user.username == 'default':
254 if user.username == 'default':
256 raise DefaultUserException(
255 raise DefaultUserException(
257 _("You can't Edit this user since it's"
256 _("You can't Edit this user since it's"
258 " crucial for entire application"))
257 " crucial for entire application"))
259
258
260 for k, v in form_data.items():
259 for k, v in form_data.items():
261 if k in skip_attrs:
260 if k in skip_attrs:
262 continue
261 continue
263 if k == 'new_password' and v:
262 if k == 'new_password' and v:
264 user.password = get_crypt_password(v)
263 user.password = get_crypt_password(v)
265 user.api_key = generate_api_key(user.username)
264 user.api_key = generate_api_key(user.username)
266 else:
265 else:
267 if k == 'firstname':
266 if k == 'firstname':
268 k = 'name'
267 k = 'name'
269 setattr(user, k, v)
268 setattr(user, k, v)
270 self.sa.add(user)
269 self.sa.add(user)
271 except:
270 except:
272 log.error(traceback.format_exc())
271 log.error(traceback.format_exc())
273 raise
272 raise
274
273
275 def update_user(self, user, **kwargs):
274 def update_user(self, user, **kwargs):
276 from rhodecode.lib.auth import get_crypt_password
275 from rhodecode.lib.auth import get_crypt_password
277 try:
276 try:
278 user = self._get_user(user)
277 user = self._get_user(user)
279 if user.username == 'default':
278 if user.username == 'default':
280 raise DefaultUserException(
279 raise DefaultUserException(
281 _("You can't Edit this user since it's"
280 _("You can't Edit this user since it's"
282 " crucial for entire application")
281 " crucial for entire application")
283 )
282 )
284
283
285 for k, v in kwargs.items():
284 for k, v in kwargs.items():
286 if k == 'password' and v:
285 if k == 'password' and v:
287 v = get_crypt_password(v)
286 v = get_crypt_password(v)
288 user.api_key = generate_api_key(user.username)
287 user.api_key = generate_api_key(user.username)
289
288
290 setattr(user, k, v)
289 setattr(user, k, v)
291 self.sa.add(user)
290 self.sa.add(user)
292 return user
291 return user
293 except:
292 except:
294 log.error(traceback.format_exc())
293 log.error(traceback.format_exc())
295 raise
294 raise
296
295
297 def update_my_account(self, user_id, form_data):
296 def update_my_account(self, user_id, form_data):
298 from rhodecode.lib.auth import get_crypt_password
297 from rhodecode.lib.auth import get_crypt_password
299 try:
298 try:
300 user = self.get(user_id, cache=False)
299 user = self.get(user_id, cache=False)
301 if user.username == 'default':
300 if user.username == 'default':
302 raise DefaultUserException(
301 raise DefaultUserException(
303 _("You can't Edit this user since it's"
302 _("You can't Edit this user since it's"
304 " crucial for entire application")
303 " crucial for entire application")
305 )
304 )
306 for k, v in form_data.items():
305 for k, v in form_data.items():
307 if k == 'new_password' and v:
306 if k == 'new_password' and v:
308 user.password = get_crypt_password(v)
307 user.password = get_crypt_password(v)
309 user.api_key = generate_api_key(user.username)
308 user.api_key = generate_api_key(user.username)
310 else:
309 else:
311 if k == 'firstname':
310 if k == 'firstname':
312 k = 'name'
311 k = 'name'
313 if k not in ['admin', 'active']:
312 if k not in ['admin', 'active']:
314 setattr(user, k, v)
313 setattr(user, k, v)
315
314
316 self.sa.add(user)
315 self.sa.add(user)
317 except:
316 except:
318 log.error(traceback.format_exc())
317 log.error(traceback.format_exc())
319 raise
318 raise
320
319
321 def delete(self, user):
320 def delete(self, user):
322 user = self._get_user(user)
321 user = self._get_user(user)
323
322
324 try:
323 try:
325 if user.username == 'default':
324 if user.username == 'default':
326 raise DefaultUserException(
325 raise DefaultUserException(
327 _(u"You can't remove this user since it's"
326 _(u"You can't remove this user since it's"
328 " crucial for entire application")
327 " crucial for entire application")
329 )
328 )
330 if user.repositories:
329 if user.repositories:
331 repos = [x.repo_name for x in user.repositories]
330 repos = [x.repo_name for x in user.repositories]
332 raise UserOwnsReposException(
331 raise UserOwnsReposException(
333 _(u'user "%s" still owns %s repositories and cannot be '
332 _(u'user "%s" still owns %s repositories and cannot be '
334 'removed. Switch owners or remove those repositories. %s')
333 'removed. Switch owners or remove those repositories. %s')
335 % (user.username, len(repos), ', '.join(repos))
334 % (user.username, len(repos), ', '.join(repos))
336 )
335 )
337 self.sa.delete(user)
336 self.sa.delete(user)
338 except:
337 except:
339 log.error(traceback.format_exc())
338 log.error(traceback.format_exc())
340 raise
339 raise
341
340
342 def reset_password_link(self, data):
341 def reset_password_link(self, data):
343 from rhodecode.lib.celerylib import tasks, run_task
342 from rhodecode.lib.celerylib import tasks, run_task
344 run_task(tasks.send_password_link, data['email'])
343 run_task(tasks.send_password_link, data['email'])
345
344
346 def reset_password(self, data):
345 def reset_password(self, data):
347 from rhodecode.lib.celerylib import tasks, run_task
346 from rhodecode.lib.celerylib import tasks, run_task
348 run_task(tasks.reset_user_password, data['email'])
347 run_task(tasks.reset_user_password, data['email'])
349
348
350 def fill_data(self, auth_user, user_id=None, api_key=None):
349 def fill_data(self, auth_user, user_id=None, api_key=None):
351 """
350 """
352 Fetches auth_user by user_id,or api_key if present.
351 Fetches auth_user by user_id,or api_key if present.
353 Fills auth_user attributes with those taken from database.
352 Fills auth_user attributes with those taken from database.
354 Additionally set's is_authenitated if lookup fails
353 Additionally set's is_authenitated if lookup fails
355 present in database
354 present in database
356
355
357 :param auth_user: instance of user to set attributes
356 :param auth_user: instance of user to set attributes
358 :param user_id: user id to fetch by
357 :param user_id: user id to fetch by
359 :param api_key: api key to fetch by
358 :param api_key: api key to fetch by
360 """
359 """
361 if user_id is None and api_key is None:
360 if user_id is None and api_key is None:
362 raise Exception('You need to pass user_id or api_key')
361 raise Exception('You need to pass user_id or api_key')
363
362
364 try:
363 try:
365 if api_key:
364 if api_key:
366 dbuser = self.get_by_api_key(api_key)
365 dbuser = self.get_by_api_key(api_key)
367 else:
366 else:
368 dbuser = self.get(user_id)
367 dbuser = self.get(user_id)
369
368
370 if dbuser is not None and dbuser.active:
369 if dbuser is not None and dbuser.active:
371 log.debug('filling %s data' % dbuser)
370 log.debug('filling %s data' % dbuser)
372 for k, v in dbuser.get_dict().items():
371 for k, v in dbuser.get_dict().items():
373 setattr(auth_user, k, v)
372 setattr(auth_user, k, v)
374 else:
373 else:
375 return False
374 return False
376
375
377 except:
376 except:
378 log.error(traceback.format_exc())
377 log.error(traceback.format_exc())
379 auth_user.is_authenticated = False
378 auth_user.is_authenticated = False
380 return False
379 return False
381
380
382 return True
381 return True
383
382
384 def fill_perms(self, user, explicit=True, algo='higherwin'):
383 def fill_perms(self, user, explicit=True, algo='higherwin'):
385 """
384 """
386 Fills user permission attribute with permissions taken from database
385 Fills user permission attribute with permissions taken from database
387 works for permissions given for repositories, and for permissions that
386 works for permissions given for repositories, and for permissions that
388 are granted to groups
387 are granted to groups
389
388
390 :param user: user instance to fill his perms
389 :param user: user instance to fill his perms
391 :param explicit: In case there are permissions both for user and a group
390 :param explicit: In case there are permissions both for user and a group
392 that user is part of, explicit flag will defiine if user will
391 that user is part of, explicit flag will defiine if user will
393 explicitly override permissions from group, if it's False it will
392 explicitly override permissions from group, if it's False it will
394 make decision based on the algo
393 make decision based on the algo
395 :param algo: algorithm to decide what permission should be choose if
394 :param algo: algorithm to decide what permission should be choose if
396 it's multiple defined, eg user in two different groups. It also
395 it's multiple defined, eg user in two different groups. It also
397 decides if explicit flag is turned off how to specify the permission
396 decides if explicit flag is turned off how to specify the permission
398 for case when user is in a group + have defined separate permission
397 for case when user is in a group + have defined separate permission
399 """
398 """
400 RK = 'repositories'
399 RK = 'repositories'
401 GK = 'repositories_groups'
400 GK = 'repositories_groups'
402 GLOBAL = 'global'
401 GLOBAL = 'global'
403 user.permissions[RK] = {}
402 user.permissions[RK] = {}
404 user.permissions[GK] = {}
403 user.permissions[GK] = {}
405 user.permissions[GLOBAL] = set()
404 user.permissions[GLOBAL] = set()
406
405
407 def _choose_perm(new_perm, cur_perm):
406 def _choose_perm(new_perm, cur_perm):
408 new_perm_val = PERM_WEIGHTS[new_perm]
407 new_perm_val = PERM_WEIGHTS[new_perm]
409 cur_perm_val = PERM_WEIGHTS[cur_perm]
408 cur_perm_val = PERM_WEIGHTS[cur_perm]
410 if algo == 'higherwin':
409 if algo == 'higherwin':
411 if new_perm_val > cur_perm_val:
410 if new_perm_val > cur_perm_val:
412 return new_perm
411 return new_perm
413 return cur_perm
412 return cur_perm
414 elif algo == 'lowerwin':
413 elif algo == 'lowerwin':
415 if new_perm_val < cur_perm_val:
414 if new_perm_val < cur_perm_val:
416 return new_perm
415 return new_perm
417 return cur_perm
416 return cur_perm
418
417
419 #======================================================================
418 #======================================================================
420 # fetch default permissions
419 # fetch default permissions
421 #======================================================================
420 #======================================================================
422 default_user = User.get_by_username('default', cache=True)
421 default_user = User.get_by_username('default', cache=True)
423 default_user_id = default_user.user_id
422 default_user_id = default_user.user_id
424
423
425 default_repo_perms = Permission.get_default_perms(default_user_id)
424 default_repo_perms = Permission.get_default_perms(default_user_id)
426 default_repo_groups_perms = Permission.get_default_group_perms(default_user_id)
425 default_repo_groups_perms = Permission.get_default_group_perms(default_user_id)
427
426
428 if user.is_admin:
427 if user.is_admin:
429 #==================================================================
428 #==================================================================
430 # admin user have all default rights for repositories
429 # admin user have all default rights for repositories
431 # and groups set to admin
430 # and groups set to admin
432 #==================================================================
431 #==================================================================
433 user.permissions[GLOBAL].add('hg.admin')
432 user.permissions[GLOBAL].add('hg.admin')
434
433
435 # repositories
434 # repositories
436 for perm in default_repo_perms:
435 for perm in default_repo_perms:
437 r_k = perm.UserRepoToPerm.repository.repo_name
436 r_k = perm.UserRepoToPerm.repository.repo_name
438 p = 'repository.admin'
437 p = 'repository.admin'
439 user.permissions[RK][r_k] = p
438 user.permissions[RK][r_k] = p
440
439
441 # repositories groups
440 # repositories groups
442 for perm in default_repo_groups_perms:
441 for perm in default_repo_groups_perms:
443 rg_k = perm.UserRepoGroupToPerm.group.group_name
442 rg_k = perm.UserRepoGroupToPerm.group.group_name
444 p = 'group.admin'
443 p = 'group.admin'
445 user.permissions[GK][rg_k] = p
444 user.permissions[GK][rg_k] = p
446 return user
445 return user
447
446
448 #==================================================================
447 #==================================================================
449 # SET DEFAULTS GLOBAL, REPOS, REPOS GROUPS
448 # SET DEFAULTS GLOBAL, REPOS, REPOS GROUPS
450 #==================================================================
449 #==================================================================
451 uid = user.user_id
450 uid = user.user_id
452
451
453 # default global permissions taken fron the default user
452 # default global permissions taken fron the default user
454 default_global_perms = self.sa.query(UserToPerm)\
453 default_global_perms = self.sa.query(UserToPerm)\
455 .filter(UserToPerm.user_id == default_user_id)
454 .filter(UserToPerm.user_id == default_user_id)
456
455
457 for perm in default_global_perms:
456 for perm in default_global_perms:
458 user.permissions[GLOBAL].add(perm.permission.permission_name)
457 user.permissions[GLOBAL].add(perm.permission.permission_name)
459
458
460 # defaults for repositories, taken from default user
459 # defaults for repositories, taken from default user
461 for perm in default_repo_perms:
460 for perm in default_repo_perms:
462 r_k = perm.UserRepoToPerm.repository.repo_name
461 r_k = perm.UserRepoToPerm.repository.repo_name
463 if perm.Repository.private and not (perm.Repository.user_id == uid):
462 if perm.Repository.private and not (perm.Repository.user_id == uid):
464 # disable defaults for private repos,
463 # disable defaults for private repos,
465 p = 'repository.none'
464 p = 'repository.none'
466 elif perm.Repository.user_id == uid:
465 elif perm.Repository.user_id == uid:
467 # set admin if owner
466 # set admin if owner
468 p = 'repository.admin'
467 p = 'repository.admin'
469 else:
468 else:
470 p = perm.Permission.permission_name
469 p = perm.Permission.permission_name
471
470
472 user.permissions[RK][r_k] = p
471 user.permissions[RK][r_k] = p
473
472
474 # defaults for repositories groups taken from default user permission
473 # defaults for repositories groups taken from default user permission
475 # on given group
474 # on given group
476 for perm in default_repo_groups_perms:
475 for perm in default_repo_groups_perms:
477 rg_k = perm.UserRepoGroupToPerm.group.group_name
476 rg_k = perm.UserRepoGroupToPerm.group.group_name
478 p = perm.Permission.permission_name
477 p = perm.Permission.permission_name
479 user.permissions[GK][rg_k] = p
478 user.permissions[GK][rg_k] = p
480
479
481 #======================================================================
480 #======================================================================
482 # !! OVERRIDE GLOBALS !! with user permissions if any found
481 # !! OVERRIDE GLOBALS !! with user permissions if any found
483 #======================================================================
482 #======================================================================
484 # those can be configured from groups or users explicitly
483 # those can be configured from groups or users explicitly
485 _configurable = set(['hg.fork.none', 'hg.fork.repository',
484 _configurable = set(['hg.fork.none', 'hg.fork.repository',
486 'hg.create.none', 'hg.create.repository'])
485 'hg.create.none', 'hg.create.repository'])
487
486
488 # USER GROUPS comes first
487 # USER GROUPS comes first
489 # users group global permissions
488 # users group global permissions
490 user_perms_from_users_groups = self.sa.query(UsersGroupToPerm)\
489 user_perms_from_users_groups = self.sa.query(UsersGroupToPerm)\
491 .options(joinedload(UsersGroupToPerm.permission))\
490 .options(joinedload(UsersGroupToPerm.permission))\
492 .join((UsersGroupMember, UsersGroupToPerm.users_group_id ==
491 .join((UsersGroupMember, UsersGroupToPerm.users_group_id ==
493 UsersGroupMember.users_group_id))\
492 UsersGroupMember.users_group_id))\
494 .filter(UsersGroupMember.user_id == uid)\
493 .filter(UsersGroupMember.user_id == uid)\
495 .order_by(UsersGroupToPerm.users_group_id)\
494 .order_by(UsersGroupToPerm.users_group_id)\
496 .all()
495 .all()
497 #need to group here by groups since user can be in more than one group
496 #need to group here by groups since user can be in more than one group
498 _grouped = [[x, list(y)] for x, y in
497 _grouped = [[x, list(y)] for x, y in
499 itertools.groupby(user_perms_from_users_groups,
498 itertools.groupby(user_perms_from_users_groups,
500 lambda x:x.users_group)]
499 lambda x:x.users_group)]
501 for gr, perms in _grouped:
500 for gr, perms in _grouped:
502 # since user can be in multiple groups iterate over them and
501 # since user can be in multiple groups iterate over them and
503 # select the lowest permissions first (more explicit)
502 # select the lowest permissions first (more explicit)
504 ##TODO: do this^^
503 ##TODO: do this^^
505 if not gr.inherit_default_permissions:
504 if not gr.inherit_default_permissions:
506 # NEED TO IGNORE all configurable permissions and
505 # NEED TO IGNORE all configurable permissions and
507 # replace them with explicitly set
506 # replace them with explicitly set
508 user.permissions[GLOBAL] = user.permissions[GLOBAL]\
507 user.permissions[GLOBAL] = user.permissions[GLOBAL]\
509 .difference(_configurable)
508 .difference(_configurable)
510 for perm in perms:
509 for perm in perms:
511 user.permissions[GLOBAL].add(perm.permission.permission_name)
510 user.permissions[GLOBAL].add(perm.permission.permission_name)
512
511
513 # user specific global permissions
512 # user specific global permissions
514 user_perms = self.sa.query(UserToPerm)\
513 user_perms = self.sa.query(UserToPerm)\
515 .options(joinedload(UserToPerm.permission))\
514 .options(joinedload(UserToPerm.permission))\
516 .filter(UserToPerm.user_id == uid).all()
515 .filter(UserToPerm.user_id == uid).all()
517
516
518 if not user.inherit_default_permissions:
517 if not user.inherit_default_permissions:
519 # NEED TO IGNORE all configurable permissions and
518 # NEED TO IGNORE all configurable permissions and
520 # replace them with explicitly set
519 # replace them with explicitly set
521 user.permissions[GLOBAL] = user.permissions[GLOBAL]\
520 user.permissions[GLOBAL] = user.permissions[GLOBAL]\
522 .difference(_configurable)
521 .difference(_configurable)
523
522
524 for perm in user_perms:
523 for perm in user_perms:
525 user.permissions[GLOBAL].add(perm.permission.permission_name)
524 user.permissions[GLOBAL].add(perm.permission.permission_name)
526
525
527 #======================================================================
526 #======================================================================
528 # !! PERMISSIONS FOR REPOSITORIES !!
527 # !! PERMISSIONS FOR REPOSITORIES !!
529 #======================================================================
528 #======================================================================
530 #======================================================================
529 #======================================================================
531 # check if user is part of user groups for this repository and
530 # check if user is part of user groups for this repository and
532 # fill in his permission from it. _choose_perm decides of which
531 # fill in his permission from it. _choose_perm decides of which
533 # permission should be selected based on selected method
532 # permission should be selected based on selected method
534 #======================================================================
533 #======================================================================
535
534
536 # users group for repositories permissions
535 # users group for repositories permissions
537 user_repo_perms_from_users_groups = \
536 user_repo_perms_from_users_groups = \
538 self.sa.query(UsersGroupRepoToPerm, Permission, Repository,)\
537 self.sa.query(UsersGroupRepoToPerm, Permission, Repository,)\
539 .join((Repository, UsersGroupRepoToPerm.repository_id ==
538 .join((Repository, UsersGroupRepoToPerm.repository_id ==
540 Repository.repo_id))\
539 Repository.repo_id))\
541 .join((Permission, UsersGroupRepoToPerm.permission_id ==
540 .join((Permission, UsersGroupRepoToPerm.permission_id ==
542 Permission.permission_id))\
541 Permission.permission_id))\
543 .join((UsersGroupMember, UsersGroupRepoToPerm.users_group_id ==
542 .join((UsersGroupMember, UsersGroupRepoToPerm.users_group_id ==
544 UsersGroupMember.users_group_id))\
543 UsersGroupMember.users_group_id))\
545 .filter(UsersGroupMember.user_id == uid)\
544 .filter(UsersGroupMember.user_id == uid)\
546 .all()
545 .all()
547
546
548 multiple_counter = collections.defaultdict(int)
547 multiple_counter = collections.defaultdict(int)
549 for perm in user_repo_perms_from_users_groups:
548 for perm in user_repo_perms_from_users_groups:
550 r_k = perm.UsersGroupRepoToPerm.repository.repo_name
549 r_k = perm.UsersGroupRepoToPerm.repository.repo_name
551 multiple_counter[r_k] += 1
550 multiple_counter[r_k] += 1
552 p = perm.Permission.permission_name
551 p = perm.Permission.permission_name
553 cur_perm = user.permissions[RK][r_k]
552 cur_perm = user.permissions[RK][r_k]
554
553
555 if perm.Repository.user_id == uid:
554 if perm.Repository.user_id == uid:
556 # set admin if owner
555 # set admin if owner
557 p = 'repository.admin'
556 p = 'repository.admin'
558 else:
557 else:
559 if multiple_counter[r_k] > 1:
558 if multiple_counter[r_k] > 1:
560 p = _choose_perm(p, cur_perm)
559 p = _choose_perm(p, cur_perm)
561 user.permissions[RK][r_k] = p
560 user.permissions[RK][r_k] = p
562
561
563 # user explicit permissions for repositories, overrides any specified
562 # user explicit permissions for repositories, overrides any specified
564 # by the group permission
563 # by the group permission
565 user_repo_perms = \
564 user_repo_perms = \
566 self.sa.query(UserRepoToPerm, Permission, Repository)\
565 self.sa.query(UserRepoToPerm, Permission, Repository)\
567 .join((Repository, UserRepoToPerm.repository_id ==
566 .join((Repository, UserRepoToPerm.repository_id ==
568 Repository.repo_id))\
567 Repository.repo_id))\
569 .join((Permission, UserRepoToPerm.permission_id ==
568 .join((Permission, UserRepoToPerm.permission_id ==
570 Permission.permission_id))\
569 Permission.permission_id))\
571 .filter(UserRepoToPerm.user_id == uid)\
570 .filter(UserRepoToPerm.user_id == uid)\
572 .all()
571 .all()
573
572
574 for perm in user_repo_perms:
573 for perm in user_repo_perms:
575 r_k = perm.UserRepoToPerm.repository.repo_name
574 r_k = perm.UserRepoToPerm.repository.repo_name
576 cur_perm = user.permissions[RK][r_k]
575 cur_perm = user.permissions[RK][r_k]
577 # set admin if owner
576 # set admin if owner
578 if perm.Repository.user_id == uid:
577 if perm.Repository.user_id == uid:
579 p = 'repository.admin'
578 p = 'repository.admin'
580 else:
579 else:
581 p = perm.Permission.permission_name
580 p = perm.Permission.permission_name
582 if not explicit:
581 if not explicit:
583 p = _choose_perm(p, cur_perm)
582 p = _choose_perm(p, cur_perm)
584 user.permissions[RK][r_k] = p
583 user.permissions[RK][r_k] = p
585
584
586 #======================================================================
585 #======================================================================
587 # !! PERMISSIONS FOR REPOSITORIES GROUPS !!
586 # !! PERMISSIONS FOR REPOSITORIES GROUPS !!
588 #======================================================================
587 #======================================================================
589 #======================================================================
588 #======================================================================
590 # check if user is part of user groups for this repository groups and
589 # check if user is part of user groups for this repository groups and
591 # fill in his permission from it. _choose_perm decides of which
590 # fill in his permission from it. _choose_perm decides of which
592 # permission should be selected based on selected method
591 # permission should be selected based on selected method
593 #======================================================================
592 #======================================================================
594 # users group for repo groups permissions
593 # users group for repo groups permissions
595 user_repo_group_perms_from_users_groups = \
594 user_repo_group_perms_from_users_groups = \
596 self.sa.query(UsersGroupRepoGroupToPerm, Permission, RepoGroup)\
595 self.sa.query(UsersGroupRepoGroupToPerm, Permission, RepoGroup)\
597 .join((RepoGroup, UsersGroupRepoGroupToPerm.group_id == RepoGroup.group_id))\
596 .join((RepoGroup, UsersGroupRepoGroupToPerm.group_id == RepoGroup.group_id))\
598 .join((Permission, UsersGroupRepoGroupToPerm.permission_id
597 .join((Permission, UsersGroupRepoGroupToPerm.permission_id
599 == Permission.permission_id))\
598 == Permission.permission_id))\
600 .join((UsersGroupMember, UsersGroupRepoGroupToPerm.users_group_id
599 .join((UsersGroupMember, UsersGroupRepoGroupToPerm.users_group_id
601 == UsersGroupMember.users_group_id))\
600 == UsersGroupMember.users_group_id))\
602 .filter(UsersGroupMember.user_id == uid)\
601 .filter(UsersGroupMember.user_id == uid)\
603 .all()
602 .all()
604
603
605 multiple_counter = collections.defaultdict(int)
604 multiple_counter = collections.defaultdict(int)
606 for perm in user_repo_group_perms_from_users_groups:
605 for perm in user_repo_group_perms_from_users_groups:
607 g_k = perm.UsersGroupRepoGroupToPerm.group.group_name
606 g_k = perm.UsersGroupRepoGroupToPerm.group.group_name
608 multiple_counter[g_k] += 1
607 multiple_counter[g_k] += 1
609 p = perm.Permission.permission_name
608 p = perm.Permission.permission_name
610 cur_perm = user.permissions[GK][g_k]
609 cur_perm = user.permissions[GK][g_k]
611 if multiple_counter[g_k] > 1:
610 if multiple_counter[g_k] > 1:
612 p = _choose_perm(p, cur_perm)
611 p = _choose_perm(p, cur_perm)
613 user.permissions[GK][g_k] = p
612 user.permissions[GK][g_k] = p
614
613
615 # user explicit permissions for repository groups
614 # user explicit permissions for repository groups
616 user_repo_groups_perms = \
615 user_repo_groups_perms = \
617 self.sa.query(UserRepoGroupToPerm, Permission, RepoGroup)\
616 self.sa.query(UserRepoGroupToPerm, Permission, RepoGroup)\
618 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
617 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
619 .join((Permission, UserRepoGroupToPerm.permission_id
618 .join((Permission, UserRepoGroupToPerm.permission_id
620 == Permission.permission_id))\
619 == Permission.permission_id))\
621 .filter(UserRepoGroupToPerm.user_id == uid)\
620 .filter(UserRepoGroupToPerm.user_id == uid)\
622 .all()
621 .all()
623
622
624 for perm in user_repo_groups_perms:
623 for perm in user_repo_groups_perms:
625 rg_k = perm.UserRepoGroupToPerm.group.group_name
624 rg_k = perm.UserRepoGroupToPerm.group.group_name
626 p = perm.Permission.permission_name
625 p = perm.Permission.permission_name
627 cur_perm = user.permissions[GK][rg_k]
626 cur_perm = user.permissions[GK][rg_k]
628 if not explicit:
627 if not explicit:
629 p = _choose_perm(p, cur_perm)
628 p = _choose_perm(p, cur_perm)
630 user.permissions[GK][rg_k] = p
629 user.permissions[GK][rg_k] = p
631
630
632 return user
631 return user
633
632
634 def has_perm(self, user, perm):
633 def has_perm(self, user, perm):
635 perm = self._get_perm(perm)
634 perm = self._get_perm(perm)
636 user = self._get_user(user)
635 user = self._get_user(user)
637
636
638 return UserToPerm.query().filter(UserToPerm.user == user)\
637 return UserToPerm.query().filter(UserToPerm.user == user)\
639 .filter(UserToPerm.permission == perm).scalar() is not None
638 .filter(UserToPerm.permission == perm).scalar() is not None
640
639
641 def grant_perm(self, user, perm):
640 def grant_perm(self, user, perm):
642 """
641 """
643 Grant user global permissions
642 Grant user global permissions
644
643
645 :param user:
644 :param user:
646 :param perm:
645 :param perm:
647 """
646 """
648 user = self._get_user(user)
647 user = self._get_user(user)
649 perm = self._get_perm(perm)
648 perm = self._get_perm(perm)
650 # if this permission is already granted skip it
649 # if this permission is already granted skip it
651 _perm = UserToPerm.query()\
650 _perm = UserToPerm.query()\
652 .filter(UserToPerm.user == user)\
651 .filter(UserToPerm.user == user)\
653 .filter(UserToPerm.permission == perm)\
652 .filter(UserToPerm.permission == perm)\
654 .scalar()
653 .scalar()
655 if _perm:
654 if _perm:
656 return
655 return
657 new = UserToPerm()
656 new = UserToPerm()
658 new.user = user
657 new.user = user
659 new.permission = perm
658 new.permission = perm
660 self.sa.add(new)
659 self.sa.add(new)
661
660
662 def revoke_perm(self, user, perm):
661 def revoke_perm(self, user, perm):
663 """
662 """
664 Revoke users global permissions
663 Revoke users global permissions
665
664
666 :param user:
665 :param user:
667 :param perm:
666 :param perm:
668 """
667 """
669 user = self._get_user(user)
668 user = self._get_user(user)
670 perm = self._get_perm(perm)
669 perm = self._get_perm(perm)
671
670
672 obj = UserToPerm.query()\
671 obj = UserToPerm.query()\
673 .filter(UserToPerm.user == user)\
672 .filter(UserToPerm.user == user)\
674 .filter(UserToPerm.permission == perm)\
673 .filter(UserToPerm.permission == perm)\
675 .scalar()
674 .scalar()
676 if obj:
675 if obj:
677 self.sa.delete(obj)
676 self.sa.delete(obj)
678
677
679 def add_extra_email(self, user, email):
678 def add_extra_email(self, user, email):
680 """
679 """
681 Adds email address to UserEmailMap
680 Adds email address to UserEmailMap
682
681
683 :param user:
682 :param user:
684 :param email:
683 :param email:
685 """
684 """
686 from rhodecode.model import forms
685 from rhodecode.model import forms
687 form = forms.UserExtraEmailForm()()
686 form = forms.UserExtraEmailForm()()
688 data = form.to_python(dict(email=email))
687 data = form.to_python(dict(email=email))
689 user = self._get_user(user)
688 user = self._get_user(user)
690
689
691 obj = UserEmailMap()
690 obj = UserEmailMap()
692 obj.user = user
691 obj.user = user
693 obj.email = data['email']
692 obj.email = data['email']
694 self.sa.add(obj)
693 self.sa.add(obj)
695 return obj
694 return obj
696
695
697 def delete_extra_email(self, user, email_id):
696 def delete_extra_email(self, user, email_id):
698 """
697 """
699 Removes email address from UserEmailMap
698 Removes email address from UserEmailMap
700
699
701 :param user:
700 :param user:
702 :param email_id:
701 :param email_id:
703 """
702 """
704 user = self._get_user(user)
703 user = self._get_user(user)
705 obj = UserEmailMap.query().get(email_id)
704 obj = UserEmailMap.query().get(email_id)
706 if obj:
705 if obj:
707 self.sa.delete(obj)
706 self.sa.delete(obj)
708
707
709 def add_extra_ip(self, user, ip):
708 def add_extra_ip(self, user, ip):
710 """
709 """
711 Adds ip address to UserIpMap
710 Adds ip address to UserIpMap
712
711
713 :param user:
712 :param user:
714 :param ip:
713 :param ip:
715 """
714 """
716 from rhodecode.model import forms
715 from rhodecode.model import forms
717 form = forms.UserExtraIpForm()()
716 form = forms.UserExtraIpForm()()
718 data = form.to_python(dict(ip=ip))
717 data = form.to_python(dict(ip=ip))
719 user = self._get_user(user)
718 user = self._get_user(user)
720
719
721 obj = UserIpMap()
720 obj = UserIpMap()
722 obj.user = user
721 obj.user = user
723 obj.ip_addr = data['ip']
722 obj.ip_addr = data['ip']
724 self.sa.add(obj)
723 self.sa.add(obj)
725 return obj
724 return obj
726
725
727 def delete_extra_ip(self, user, ip_id):
726 def delete_extra_ip(self, user, ip_id):
728 """
727 """
729 Removes ip address from UserIpMap
728 Removes ip address from UserIpMap
730
729
731 :param user:
730 :param user:
732 :param ip_id:
731 :param ip_id:
733 """
732 """
734 user = self._get_user(user)
733 user = self._get_user(user)
735 obj = UserIpMap.query().get(ip_id)
734 obj = UserIpMap.query().get(ip_id)
736 if obj:
735 if obj:
737 self.sa.delete(obj)
736 self.sa.delete(obj)
@@ -1,4850 +1,4847 b''
1 html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td
1 html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td
2 {
2 {
3 border: 0;
3 border: 0;
4 outline: 0;
4 outline: 0;
5 font-size: 100%;
5 font-size: 100%;
6 vertical-align: baseline;
6 vertical-align: baseline;
7 background: transparent;
7 background: transparent;
8 margin: 0;
8 margin: 0;
9 padding: 0;
9 padding: 0;
10 }
10 }
11
11
12 body {
12 body {
13 line-height: 1;
13 line-height: 1;
14 height: 100%;
14 height: 100%;
15 background: url("../images/background.png") repeat scroll 0 0 #B0B0B0;
15 background: url("../images/background.png") repeat scroll 0 0 #B0B0B0;
16 font-family: Lucida Grande, Verdana, Lucida Sans Regular,
16 font-family: Lucida Grande, Verdana, Lucida Sans Regular,
17 Lucida Sans Unicode, Arial, sans-serif; font-size : 12px;
17 Lucida Sans Unicode, Arial, sans-serif; font-size : 12px;
18 color: #000;
18 color: #000;
19 margin: 0;
19 margin: 0;
20 padding: 0;
20 padding: 0;
21 font-size: 12px;
21 font-size: 12px;
22 }
22 }
23
23
24 ol,ul {
24 ol,ul {
25 list-style: none;
25 list-style: none;
26 }
26 }
27
27
28 blockquote,q {
28 blockquote,q {
29 quotes: none;
29 quotes: none;
30 }
30 }
31
31
32 blockquote:before,blockquote:after,q:before,q:after {
32 blockquote:before,blockquote:after,q:before,q:after {
33 content: none;
33 content: none;
34 }
34 }
35
35
36 :focus {
36 :focus {
37 outline: 0;
37 outline: 0;
38 }
38 }
39
39
40 del {
40 del {
41 text-decoration: line-through;
41 text-decoration: line-through;
42 }
42 }
43
43
44 table {
44 table {
45 border-collapse: collapse;
45 border-collapse: collapse;
46 border-spacing: 0;
46 border-spacing: 0;
47 }
47 }
48
48
49 html {
49 html {
50 height: 100%;
50 height: 100%;
51 }
51 }
52
52
53 a {
53 a {
54 color: #003367;
54 color: #003367;
55 text-decoration: none;
55 text-decoration: none;
56 cursor: pointer;
56 cursor: pointer;
57 }
57 }
58
58
59 a:hover {
59 a:hover {
60 color: #316293;
60 color: #316293;
61 text-decoration: underline;
61 text-decoration: underline;
62 }
62 }
63
63
64 h1,h2,h3,h4,h5,h6,
64 h1,h2,h3,h4,h5,h6,
65 div.h1,div.h2,div.h3,div.h4,div.h5,div.h6 {
65 div.h1,div.h2,div.h3,div.h4,div.h5,div.h6 {
66 color: #292929;
66 color: #292929;
67 font-weight: 700;
67 font-weight: 700;
68 }
68 }
69
69
70 h1,div.h1 {
70 h1,div.h1 {
71 font-size: 22px;
71 font-size: 22px;
72 }
72 }
73
73
74 h2,div.h2 {
74 h2,div.h2 {
75 font-size: 20px;
75 font-size: 20px;
76 }
76 }
77
77
78 h3,div.h3 {
78 h3,div.h3 {
79 font-size: 18px;
79 font-size: 18px;
80 }
80 }
81
81
82 h4,div.h4 {
82 h4,div.h4 {
83 font-size: 16px;
83 font-size: 16px;
84 }
84 }
85
85
86 h5,div.h5 {
86 h5,div.h5 {
87 font-size: 14px;
87 font-size: 14px;
88 }
88 }
89
89
90 h6,div.h6 {
90 h6,div.h6 {
91 font-size: 11px;
91 font-size: 11px;
92 }
92 }
93
93
94 ul.circle {
94 ul.circle {
95 list-style-type: circle;
95 list-style-type: circle;
96 }
96 }
97
97
98 ul.disc {
98 ul.disc {
99 list-style-type: disc;
99 list-style-type: disc;
100 }
100 }
101
101
102 ul.square {
102 ul.square {
103 list-style-type: square;
103 list-style-type: square;
104 }
104 }
105
105
106 ol.lower-roman {
106 ol.lower-roman {
107 list-style-type: lower-roman;
107 list-style-type: lower-roman;
108 }
108 }
109
109
110 ol.upper-roman {
110 ol.upper-roman {
111 list-style-type: upper-roman;
111 list-style-type: upper-roman;
112 }
112 }
113
113
114 ol.lower-alpha {
114 ol.lower-alpha {
115 list-style-type: lower-alpha;
115 list-style-type: lower-alpha;
116 }
116 }
117
117
118 ol.upper-alpha {
118 ol.upper-alpha {
119 list-style-type: upper-alpha;
119 list-style-type: upper-alpha;
120 }
120 }
121
121
122 ol.decimal {
122 ol.decimal {
123 list-style-type: decimal;
123 list-style-type: decimal;
124 }
124 }
125
125
126 div.color {
126 div.color {
127 clear: both;
127 clear: both;
128 overflow: hidden;
128 overflow: hidden;
129 position: absolute;
129 position: absolute;
130 background: #FFF;
130 background: #FFF;
131 margin: 7px 0 0 60px;
131 margin: 7px 0 0 60px;
132 padding: 1px 1px 1px 0;
132 padding: 1px 1px 1px 0;
133 }
133 }
134
134
135 div.color a {
135 div.color a {
136 width: 15px;
136 width: 15px;
137 height: 15px;
137 height: 15px;
138 display: block;
138 display: block;
139 float: left;
139 float: left;
140 margin: 0 0 0 1px;
140 margin: 0 0 0 1px;
141 padding: 0;
141 padding: 0;
142 }
142 }
143
143
144 div.options {
144 div.options {
145 clear: both;
145 clear: both;
146 overflow: hidden;
146 overflow: hidden;
147 position: absolute;
147 position: absolute;
148 background: #FFF;
148 background: #FFF;
149 margin: 7px 0 0 162px;
149 margin: 7px 0 0 162px;
150 padding: 0;
150 padding: 0;
151 }
151 }
152
152
153 div.options a {
153 div.options a {
154 height: 1%;
154 height: 1%;
155 display: block;
155 display: block;
156 text-decoration: none;
156 text-decoration: none;
157 margin: 0;
157 margin: 0;
158 padding: 3px 8px;
158 padding: 3px 8px;
159 }
159 }
160
160
161 .top-left-rounded-corner {
161 .top-left-rounded-corner {
162 -webkit-border-top-left-radius: 8px;
162 -webkit-border-top-left-radius: 8px;
163 -khtml-border-radius-topleft: 8px;
163 -khtml-border-radius-topleft: 8px;
164 -moz-border-radius-topleft: 8px;
164 -moz-border-radius-topleft: 8px;
165 border-top-left-radius: 8px;
165 border-top-left-radius: 8px;
166 }
166 }
167
167
168 .top-right-rounded-corner {
168 .top-right-rounded-corner {
169 -webkit-border-top-right-radius: 8px;
169 -webkit-border-top-right-radius: 8px;
170 -khtml-border-radius-topright: 8px;
170 -khtml-border-radius-topright: 8px;
171 -moz-border-radius-topright: 8px;
171 -moz-border-radius-topright: 8px;
172 border-top-right-radius: 8px;
172 border-top-right-radius: 8px;
173 }
173 }
174
174
175 .bottom-left-rounded-corner {
175 .bottom-left-rounded-corner {
176 -webkit-border-bottom-left-radius: 8px;
176 -webkit-border-bottom-left-radius: 8px;
177 -khtml-border-radius-bottomleft: 8px;
177 -khtml-border-radius-bottomleft: 8px;
178 -moz-border-radius-bottomleft: 8px;
178 -moz-border-radius-bottomleft: 8px;
179 border-bottom-left-radius: 8px;
179 border-bottom-left-radius: 8px;
180 }
180 }
181
181
182 .bottom-right-rounded-corner {
182 .bottom-right-rounded-corner {
183 -webkit-border-bottom-right-radius: 8px;
183 -webkit-border-bottom-right-radius: 8px;
184 -khtml-border-radius-bottomright: 8px;
184 -khtml-border-radius-bottomright: 8px;
185 -moz-border-radius-bottomright: 8px;
185 -moz-border-radius-bottomright: 8px;
186 border-bottom-right-radius: 8px;
186 border-bottom-right-radius: 8px;
187 }
187 }
188
188
189 .top-left-rounded-corner-mid {
189 .top-left-rounded-corner-mid {
190 -webkit-border-top-left-radius: 4px;
190 -webkit-border-top-left-radius: 4px;
191 -khtml-border-radius-topleft: 4px;
191 -khtml-border-radius-topleft: 4px;
192 -moz-border-radius-topleft: 4px;
192 -moz-border-radius-topleft: 4px;
193 border-top-left-radius: 4px;
193 border-top-left-radius: 4px;
194 }
194 }
195
195
196 .top-right-rounded-corner-mid {
196 .top-right-rounded-corner-mid {
197 -webkit-border-top-right-radius: 4px;
197 -webkit-border-top-right-radius: 4px;
198 -khtml-border-radius-topright: 4px;
198 -khtml-border-radius-topright: 4px;
199 -moz-border-radius-topright: 4px;
199 -moz-border-radius-topright: 4px;
200 border-top-right-radius: 4px;
200 border-top-right-radius: 4px;
201 }
201 }
202
202
203 .bottom-left-rounded-corner-mid {
203 .bottom-left-rounded-corner-mid {
204 -webkit-border-bottom-left-radius: 4px;
204 -webkit-border-bottom-left-radius: 4px;
205 -khtml-border-radius-bottomleft: 4px;
205 -khtml-border-radius-bottomleft: 4px;
206 -moz-border-radius-bottomleft: 4px;
206 -moz-border-radius-bottomleft: 4px;
207 border-bottom-left-radius: 4px;
207 border-bottom-left-radius: 4px;
208 }
208 }
209
209
210 .bottom-right-rounded-corner-mid {
210 .bottom-right-rounded-corner-mid {
211 -webkit-border-bottom-right-radius: 4px;
211 -webkit-border-bottom-right-radius: 4px;
212 -khtml-border-radius-bottomright: 4px;
212 -khtml-border-radius-bottomright: 4px;
213 -moz-border-radius-bottomright: 4px;
213 -moz-border-radius-bottomright: 4px;
214 border-bottom-right-radius: 4px;
214 border-bottom-right-radius: 4px;
215 }
215 }
216
216
217 .help-block {
217 .help-block {
218 color: #999999;
218 color: #999999;
219 display: block;
219 display: block;
220 margin-bottom: 0;
220 margin-bottom: 0;
221 margin-top: 5px;
221 margin-top: 5px;
222 }
222 }
223
223
224 .empty_data{
224 .empty_data{
225 color:#B9B9B9;
225 color:#B9B9B9;
226 }
226 }
227
227
228 a.permalink{
228 a.permalink{
229 visibility: hidden;
229 visibility: hidden;
230 }
230 }
231
231
232 a.permalink:hover{
232 a.permalink:hover{
233 text-decoration: none;
233 text-decoration: none;
234 }
234 }
235
235
236 h1:hover > a.permalink,
236 h1:hover > a.permalink,
237 h2:hover > a.permalink,
237 h2:hover > a.permalink,
238 h3:hover > a.permalink,
238 h3:hover > a.permalink,
239 h4:hover > a.permalink,
239 h4:hover > a.permalink,
240 h5:hover > a.permalink,
240 h5:hover > a.permalink,
241 h6:hover > a.permalink,
241 h6:hover > a.permalink,
242 div:hover > a.permalink {
242 div:hover > a.permalink {
243 visibility: visible;
243 visibility: visible;
244 }
244 }
245
245
246 #header {
246 #header {
247 margin: 0;
247 margin: 0;
248 padding: 0 10px;
248 padding: 0 10px;
249 }
249 }
250
250
251 #header ul#logged-user {
251 #header ul#logged-user {
252 margin-bottom: 5px !important;
252 margin-bottom: 5px !important;
253 -webkit-border-radius: 0px 0px 8px 8px;
253 -webkit-border-radius: 0px 0px 8px 8px;
254 -khtml-border-radius: 0px 0px 8px 8px;
254 -khtml-border-radius: 0px 0px 8px 8px;
255 -moz-border-radius: 0px 0px 8px 8px;
255 -moz-border-radius: 0px 0px 8px 8px;
256 border-radius: 0px 0px 8px 8px;
256 border-radius: 0px 0px 8px 8px;
257 height: 37px;
257 height: 37px;
258 background-color: #003B76;
258 background-color: #003B76;
259 background-repeat: repeat-x;
259 background-repeat: repeat-x;
260 background-image: -khtml-gradient(linear, left top, left bottom, from(#003B76), to(#00376E) );
260 background-image: -khtml-gradient(linear, left top, left bottom, from(#003B76), to(#00376E) );
261 background-image: -moz-linear-gradient(top, #003b76, #00376e);
261 background-image: -moz-linear-gradient(top, #003b76, #00376e);
262 background-image: -ms-linear-gradient(top, #003b76, #00376e);
262 background-image: -ms-linear-gradient(top, #003b76, #00376e);
263 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76), color-stop(100%, #00376e) );
263 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76), color-stop(100%, #00376e) );
264 background-image: -webkit-linear-gradient(top, #003b76, #00376e);
264 background-image: -webkit-linear-gradient(top, #003b76, #00376e);
265 background-image: -o-linear-gradient(top, #003b76, #00376e);
265 background-image: -o-linear-gradient(top, #003b76, #00376e);
266 background-image: linear-gradient(top, #003b76, #00376e);
266 background-image: linear-gradient(top, #003b76, #00376e);
267 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76',endColorstr='#00376e', GradientType=0 );
267 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76',endColorstr='#00376e', GradientType=0 );
268 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
268 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
269 }
269 }
270
270
271 #header ul#logged-user li {
271 #header ul#logged-user li {
272 list-style: none;
272 list-style: none;
273 float: left;
273 float: left;
274 margin: 8px 0 0;
274 margin: 8px 0 0;
275 padding: 4px 12px;
275 padding: 4px 12px;
276 border-left: 1px solid #316293;
276 border-left: 1px solid #316293;
277 }
277 }
278
278
279 #header ul#logged-user li.first {
279 #header ul#logged-user li.first {
280 border-left: none;
280 border-left: none;
281 margin: 4px;
281 margin: 4px;
282 }
282 }
283
283
284 #header ul#logged-user li.first div.gravatar {
284 #header ul#logged-user li.first div.gravatar {
285 margin-top: -2px;
285 margin-top: -2px;
286 }
286 }
287
287
288 #header ul#logged-user li.first div.account {
288 #header ul#logged-user li.first div.account {
289 padding-top: 4px;
289 padding-top: 4px;
290 float: left;
290 float: left;
291 }
291 }
292
292
293 #header ul#logged-user li.last {
293 #header ul#logged-user li.last {
294 border-right: none;
294 border-right: none;
295 }
295 }
296
296
297 #header ul#logged-user li a {
297 #header ul#logged-user li a {
298 color: #fff;
298 color: #fff;
299 font-weight: 700;
299 font-weight: 700;
300 text-decoration: none;
300 text-decoration: none;
301 }
301 }
302
302
303 #header ul#logged-user li a:hover {
303 #header ul#logged-user li a:hover {
304 text-decoration: underline;
304 text-decoration: underline;
305 }
305 }
306
306
307 #header ul#logged-user li.highlight a {
307 #header ul#logged-user li.highlight a {
308 color: #fff;
308 color: #fff;
309 }
309 }
310
310
311 #header ul#logged-user li.highlight a:hover {
311 #header ul#logged-user li.highlight a:hover {
312 color: #FFF;
312 color: #FFF;
313 }
313 }
314
314
315 #header #header-inner {
315 #header #header-inner {
316 min-height: 44px;
316 min-height: 44px;
317 clear: both;
317 clear: both;
318 position: relative;
318 position: relative;
319 background-color: #003B76;
319 background-color: #003B76;
320 background-repeat: repeat-x;
320 background-repeat: repeat-x;
321 background-image: -khtml-gradient(linear, left top, left bottom, from(#003B76), to(#00376E) );
321 background-image: -khtml-gradient(linear, left top, left bottom, from(#003B76), to(#00376E) );
322 background-image: -moz-linear-gradient(top, #003b76, #00376e);
322 background-image: -moz-linear-gradient(top, #003b76, #00376e);
323 background-image: -ms-linear-gradient(top, #003b76, #00376e);
323 background-image: -ms-linear-gradient(top, #003b76, #00376e);
324 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76),color-stop(100%, #00376e) );
324 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76),color-stop(100%, #00376e) );
325 background-image: -webkit-linear-gradient(top, #003b76, #00376e);
325 background-image: -webkit-linear-gradient(top, #003b76, #00376e);
326 background-image: -o-linear-gradient(top, #003b76, #00376e);
326 background-image: -o-linear-gradient(top, #003b76, #00376e);
327 background-image: linear-gradient(top, #003b76, #00376e);
327 background-image: linear-gradient(top, #003b76, #00376e);
328 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76',endColorstr='#00376e', GradientType=0 );
328 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76',endColorstr='#00376e', GradientType=0 );
329 margin: 0;
329 margin: 0;
330 padding: 0;
330 padding: 0;
331 display: block;
331 display: block;
332 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
332 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
333 -webkit-border-radius: 4px 4px 4px 4px;
333 -webkit-border-radius: 4px 4px 4px 4px;
334 -khtml-border-radius: 4px 4px 4px 4px;
334 -khtml-border-radius: 4px 4px 4px 4px;
335 -moz-border-radius: 4px 4px 4px 4px;
335 -moz-border-radius: 4px 4px 4px 4px;
336 border-radius: 4px 4px 4px 4px;
336 border-radius: 4px 4px 4px 4px;
337 }
337 }
338 #header #header-inner.hover{
338 #header #header-inner.hover{
339 position: fixed !important;
339 position: fixed !important;
340 width: 100% !important;
340 width: 100% !important;
341 margin-left: -10px !important;
341 margin-left: -10px !important;
342 z-index: 10000;
342 z-index: 10000;
343 -webkit-border-radius: 0px 0px 0px 0px;
343 -webkit-border-radius: 0px 0px 0px 0px;
344 -khtml-border-radius: 0px 0px 0px 0px;
344 -khtml-border-radius: 0px 0px 0px 0px;
345 -moz-border-radius: 0px 0px 0px 0px;
345 -moz-border-radius: 0px 0px 0px 0px;
346 border-radius: 0px 0px 0px 0px;
346 border-radius: 0px 0px 0px 0px;
347 }
347 }
348
348
349 .ie7 #header #header-inner.hover,
349 .ie7 #header #header-inner.hover,
350 .ie8 #header #header-inner.hover,
350 .ie8 #header #header-inner.hover,
351 .ie9 #header #header-inner.hover
351 .ie9 #header #header-inner.hover
352 {
352 {
353 z-index: auto !important;
353 z-index: auto !important;
354 }
354 }
355
355
356 .header-pos-fix, .anchor{
356 .header-pos-fix, .anchor{
357 margin-top: -46px;
357 margin-top: -46px;
358 padding-top: 46px;
358 padding-top: 46px;
359 }
359 }
360
360
361 #header #header-inner #home a {
361 #header #header-inner #home a {
362 height: 40px;
362 height: 40px;
363 width: 46px;
363 width: 46px;
364 display: block;
364 display: block;
365 background: url("../images/button_home.png");
365 background: url("../images/button_home.png");
366 background-position: 0 0;
366 background-position: 0 0;
367 margin: 0;
367 margin: 0;
368 padding: 0;
368 padding: 0;
369 }
369 }
370
370
371 #header #header-inner #home a:hover {
371 #header #header-inner #home a:hover {
372 background-position: 0 -40px;
372 background-position: 0 -40px;
373 }
373 }
374
374
375 #header #header-inner #logo {
375 #header #header-inner #logo {
376 float: left;
376 float: left;
377 position: absolute;
377 position: absolute;
378 }
378 }
379
379
380 #header #header-inner #logo h1 {
380 #header #header-inner #logo h1 {
381 color: #FFF;
381 color: #FFF;
382 font-size: 20px;
382 font-size: 20px;
383 margin: 12px 0 0 13px;
383 margin: 12px 0 0 13px;
384 padding: 0;
384 padding: 0;
385 }
385 }
386
386
387 #header #header-inner #logo a {
387 #header #header-inner #logo a {
388 color: #fff;
388 color: #fff;
389 text-decoration: none;
389 text-decoration: none;
390 }
390 }
391
391
392 #header #header-inner #logo a:hover {
392 #header #header-inner #logo a:hover {
393 color: #bfe3ff;
393 color: #bfe3ff;
394 }
394 }
395
395
396 #header #header-inner #quick,#header #header-inner #quick ul {
396 #header #header-inner #quick,#header #header-inner #quick ul {
397 position: relative;
397 position: relative;
398 float: right;
398 float: right;
399 list-style-type: none;
399 list-style-type: none;
400 list-style-position: outside;
400 list-style-position: outside;
401 margin: 8px 8px 0 0;
401 margin: 8px 8px 0 0;
402 padding: 0;
402 padding: 0;
403 }
403 }
404
404
405 #header #header-inner #quick li {
405 #header #header-inner #quick li {
406 position: relative;
406 position: relative;
407 float: left;
407 float: left;
408 margin: 0 5px 0 0;
408 margin: 0 5px 0 0;
409 padding: 0;
409 padding: 0;
410 }
410 }
411
411
412 #header #header-inner #quick li a.menu_link {
412 #header #header-inner #quick li a.menu_link {
413 top: 0;
413 top: 0;
414 left: 0;
414 left: 0;
415 height: 1%;
415 height: 1%;
416 display: block;
416 display: block;
417 clear: both;
417 clear: both;
418 overflow: hidden;
418 overflow: hidden;
419 color: #FFF;
419 color: #FFF;
420 font-weight: 700;
420 font-weight: 700;
421 text-decoration: none;
421 text-decoration: none;
422 background: #369;
422 background: #369;
423 padding: 0;
423 padding: 0;
424 -webkit-border-radius: 4px 4px 4px 4px;
424 -webkit-border-radius: 4px 4px 4px 4px;
425 -khtml-border-radius: 4px 4px 4px 4px;
425 -khtml-border-radius: 4px 4px 4px 4px;
426 -moz-border-radius: 4px 4px 4px 4px;
426 -moz-border-radius: 4px 4px 4px 4px;
427 border-radius: 4px 4px 4px 4px;
427 border-radius: 4px 4px 4px 4px;
428 }
428 }
429
429
430 #header #header-inner #quick li span.short {
430 #header #header-inner #quick li span.short {
431 padding: 9px 6px 8px 6px;
431 padding: 9px 6px 8px 6px;
432 }
432 }
433
433
434 #header #header-inner #quick li span {
434 #header #header-inner #quick li span {
435 top: 0;
435 top: 0;
436 right: 0;
436 right: 0;
437 height: 1%;
437 height: 1%;
438 display: block;
438 display: block;
439 float: left;
439 float: left;
440 border-left: 1px solid #3f6f9f;
440 border-left: 1px solid #3f6f9f;
441 margin: 0;
441 margin: 0;
442 padding: 10px 12px 8px 10px;
442 padding: 10px 12px 8px 10px;
443 }
443 }
444
444
445 #header #header-inner #quick li span.normal {
445 #header #header-inner #quick li span.normal {
446 border: none;
446 border: none;
447 padding: 10px 12px 8px;
447 padding: 10px 12px 8px;
448 }
448 }
449
449
450 #header #header-inner #quick li span.icon {
450 #header #header-inner #quick li span.icon {
451 top: 0;
451 top: 0;
452 left: 0;
452 left: 0;
453 border-left: none;
453 border-left: none;
454 border-right: 1px solid #2e5c89;
454 border-right: 1px solid #2e5c89;
455 padding: 8px 6px 4px;
455 padding: 8px 6px 4px;
456 }
456 }
457
457
458 #header #header-inner #quick li span.icon_short {
458 #header #header-inner #quick li span.icon_short {
459 top: 0;
459 top: 0;
460 left: 0;
460 left: 0;
461 border-left: none;
461 border-left: none;
462 border-right: 1px solid #2e5c89;
462 border-right: 1px solid #2e5c89;
463 padding: 8px 6px 4px;
463 padding: 8px 6px 4px;
464 }
464 }
465
465
466 #header #header-inner #quick li span.icon img,#header #header-inner #quick li span.icon_short img
466 #header #header-inner #quick li span.icon img,#header #header-inner #quick li span.icon_short img
467 {
467 {
468 margin: 0px -2px 0px 0px;
468 margin: 0px -2px 0px 0px;
469 }
469 }
470
470
471 #header #header-inner #quick li a:hover {
471 #header #header-inner #quick li a:hover {
472 background: #4e4e4e no-repeat top left;
472 background: #4e4e4e no-repeat top left;
473 }
473 }
474
474
475 #header #header-inner #quick li a:hover span {
475 #header #header-inner #quick li a:hover span {
476 border-left: 1px solid #545454;
476 border-left: 1px solid #545454;
477 }
477 }
478
478
479 #header #header-inner #quick li a:hover span.icon,#header #header-inner #quick li a:hover span.icon_short
479 #header #header-inner #quick li a:hover span.icon,#header #header-inner #quick li a:hover span.icon_short
480 {
480 {
481 border-left: none;
481 border-left: none;
482 border-right: 1px solid #464646;
482 border-right: 1px solid #464646;
483 }
483 }
484
484
485 #header #header-inner #quick ul {
485 #header #header-inner #quick ul {
486 top: 29px;
486 top: 29px;
487 right: 0;
487 right: 0;
488 min-width: 200px;
488 min-width: 200px;
489 display: none;
489 display: none;
490 position: absolute;
490 position: absolute;
491 background: #FFF;
491 background: #FFF;
492 border: 1px solid #666;
492 border: 1px solid #666;
493 border-top: 1px solid #003367;
493 border-top: 1px solid #003367;
494 z-index: 100;
494 z-index: 100;
495 margin: 0px 0px 0px 0px;
495 margin: 0px 0px 0px 0px;
496 padding: 0;
496 padding: 0;
497 }
497 }
498
498
499 #header #header-inner #quick ul.repo_switcher {
499 #header #header-inner #quick ul.repo_switcher {
500 max-height: 275px;
500 max-height: 275px;
501 overflow-x: hidden;
501 overflow-x: hidden;
502 overflow-y: auto;
502 overflow-y: auto;
503 }
503 }
504
504
505 #header #header-inner #quick ul.repo_switcher li.qfilter_rs {
505 #header #header-inner #quick ul.repo_switcher li.qfilter_rs {
506 float: none;
506 float: none;
507 margin: 0;
507 margin: 0;
508 border-bottom: 2px solid #003367;
508 border-bottom: 2px solid #003367;
509 }
509 }
510
510
511 #header #header-inner #quick .repo_switcher_type {
511 #header #header-inner #quick .repo_switcher_type {
512 position: absolute;
512 position: absolute;
513 left: 0;
513 left: 0;
514 top: 9px;
514 top: 9px;
515 }
515 }
516
516
517 #header #header-inner #quick li ul li {
517 #header #header-inner #quick li ul li {
518 border-bottom: 1px solid #ddd;
518 border-bottom: 1px solid #ddd;
519 }
519 }
520
520
521 #header #header-inner #quick li ul li a {
521 #header #header-inner #quick li ul li a {
522 width: 182px;
522 width: 182px;
523 height: auto;
523 height: auto;
524 display: block;
524 display: block;
525 float: left;
525 float: left;
526 background: #FFF;
526 background: #FFF;
527 color: #003367;
527 color: #003367;
528 font-weight: 400;
528 font-weight: 400;
529 margin: 0;
529 margin: 0;
530 padding: 7px 9px;
530 padding: 7px 9px;
531 }
531 }
532
532
533 #header #header-inner #quick li ul li a:hover {
533 #header #header-inner #quick li ul li a:hover {
534 color: #000;
534 color: #000;
535 background: #FFF;
535 background: #FFF;
536 }
536 }
537
537
538 #header #header-inner #quick ul ul {
538 #header #header-inner #quick ul ul {
539 top: auto;
539 top: auto;
540 }
540 }
541
541
542 #header #header-inner #quick li ul ul {
542 #header #header-inner #quick li ul ul {
543 right: 200px;
543 right: 200px;
544 max-height: 290px;
544 max-height: 290px;
545 overflow: auto;
545 overflow: auto;
546 overflow-x: hidden;
546 overflow-x: hidden;
547 white-space: normal;
547 white-space: normal;
548 }
548 }
549
549
550 #header #header-inner #quick li ul li a.journal,#header #header-inner #quick li ul li a.journal:hover
550 #header #header-inner #quick li ul li a.journal,#header #header-inner #quick li ul li a.journal:hover
551 {
551 {
552 background: url("../images/icons/book.png") no-repeat scroll 4px 9px
552 background: url("../images/icons/book.png") no-repeat scroll 4px 9px
553 #FFF;
553 #FFF;
554 width: 167px;
554 width: 167px;
555 margin: 0;
555 margin: 0;
556 padding: 12px 9px 7px 24px;
556 padding: 12px 9px 7px 24px;
557 }
557 }
558
558
559 #header #header-inner #quick li ul li a.private_repo,#header #header-inner #quick li ul li a.private_repo:hover
559 #header #header-inner #quick li ul li a.private_repo,#header #header-inner #quick li ul li a.private_repo:hover
560 {
560 {
561 background: url("../images/icons/lock.png") no-repeat scroll 4px 9px
561 background: url("../images/icons/lock.png") no-repeat scroll 4px 9px
562 #FFF;
562 #FFF;
563 min-width: 167px;
563 min-width: 167px;
564 margin: 0;
564 margin: 0;
565 padding: 12px 9px 7px 24px;
565 padding: 12px 9px 7px 24px;
566 }
566 }
567
567
568 #header #header-inner #quick li ul li a.public_repo,#header #header-inner #quick li ul li a.public_repo:hover
568 #header #header-inner #quick li ul li a.public_repo,#header #header-inner #quick li ul li a.public_repo:hover
569 {
569 {
570 background: url("../images/icons/lock_open.png") no-repeat scroll 4px
570 background: url("../images/icons/lock_open.png") no-repeat scroll 4px
571 9px #FFF;
571 9px #FFF;
572 min-width: 167px;
572 min-width: 167px;
573 margin: 0;
573 margin: 0;
574 padding: 12px 9px 7px 24px;
574 padding: 12px 9px 7px 24px;
575 }
575 }
576
576
577 #header #header-inner #quick li ul li a.hg,#header #header-inner #quick li ul li a.hg:hover
577 #header #header-inner #quick li ul li a.hg,#header #header-inner #quick li ul li a.hg:hover
578 {
578 {
579 background: url("../images/icons/hgicon.png") no-repeat scroll 4px 9px
579 background: url("../images/icons/hgicon.png") no-repeat scroll 4px 9px
580 #FFF;
580 #FFF;
581 min-width: 167px;
581 min-width: 167px;
582 margin: 0 0 0 14px;
582 margin: 0 0 0 14px;
583 padding: 12px 9px 7px 24px;
583 padding: 12px 9px 7px 24px;
584 }
584 }
585
585
586 #header #header-inner #quick li ul li a.git,#header #header-inner #quick li ul li a.git:hover
586 #header #header-inner #quick li ul li a.git,#header #header-inner #quick li ul li a.git:hover
587 {
587 {
588 background: url("../images/icons/giticon.png") no-repeat scroll 4px 9px
588 background: url("../images/icons/giticon.png") no-repeat scroll 4px 9px
589 #FFF;
589 #FFF;
590 min-width: 167px;
590 min-width: 167px;
591 margin: 0 0 0 14px;
591 margin: 0 0 0 14px;
592 padding: 12px 9px 7px 24px;
592 padding: 12px 9px 7px 24px;
593 }
593 }
594
594
595 #header #header-inner #quick li ul li a.repos,#header #header-inner #quick li ul li a.repos:hover
595 #header #header-inner #quick li ul li a.repos,#header #header-inner #quick li ul li a.repos:hover
596 {
596 {
597 background: url("../images/icons/database_edit.png") no-repeat scroll
597 background: url("../images/icons/database_edit.png") no-repeat scroll
598 4px 9px #FFF;
598 4px 9px #FFF;
599 width: 167px;
599 width: 167px;
600 margin: 0;
600 margin: 0;
601 padding: 12px 9px 7px 24px;
601 padding: 12px 9px 7px 24px;
602 }
602 }
603
603
604 #header #header-inner #quick li ul li a.repos_groups,#header #header-inner #quick li ul li a.repos_groups:hover
604 #header #header-inner #quick li ul li a.repos_groups,#header #header-inner #quick li ul li a.repos_groups:hover
605 {
605 {
606 background: url("../images/icons/database_link.png") no-repeat scroll
606 background: url("../images/icons/database_link.png") no-repeat scroll
607 4px 9px #FFF;
607 4px 9px #FFF;
608 width: 167px;
608 width: 167px;
609 margin: 0;
609 margin: 0;
610 padding: 12px 9px 7px 24px;
610 padding: 12px 9px 7px 24px;
611 }
611 }
612
612
613 #header #header-inner #quick li ul li a.users,#header #header-inner #quick li ul li a.users:hover
613 #header #header-inner #quick li ul li a.users,#header #header-inner #quick li ul li a.users:hover
614 {
614 {
615 background: #FFF url("../images/icons/user_edit.png") no-repeat 4px 9px;
615 background: #FFF url("../images/icons/user_edit.png") no-repeat 4px 9px;
616 width: 167px;
616 width: 167px;
617 margin: 0;
617 margin: 0;
618 padding: 12px 9px 7px 24px;
618 padding: 12px 9px 7px 24px;
619 }
619 }
620
620
621 #header #header-inner #quick li ul li a.groups,#header #header-inner #quick li ul li a.groups:hover
621 #header #header-inner #quick li ul li a.groups,#header #header-inner #quick li ul li a.groups:hover
622 {
622 {
623 background: #FFF url("../images/icons/group_edit.png") no-repeat 4px 9px;
623 background: #FFF url("../images/icons/group_edit.png") no-repeat 4px 9px;
624 width: 167px;
624 width: 167px;
625 margin: 0;
625 margin: 0;
626 padding: 12px 9px 7px 24px;
626 padding: 12px 9px 7px 24px;
627 }
627 }
628
628
629 #header #header-inner #quick li ul li a.defaults,#header #header-inner #quick li ul li a.defaults:hover
629 #header #header-inner #quick li ul li a.defaults,#header #header-inner #quick li ul li a.defaults:hover
630 {
630 {
631 background: #FFF url("../images/icons/wrench.png") no-repeat 4px 9px;
631 background: #FFF url("../images/icons/wrench.png") no-repeat 4px 9px;
632 width: 167px;
632 width: 167px;
633 margin: 0;
633 margin: 0;
634 padding: 12px 9px 7px 24px;
634 padding: 12px 9px 7px 24px;
635 }
635 }
636
636
637 #header #header-inner #quick li ul li a.settings,#header #header-inner #quick li ul li a.settings:hover
637 #header #header-inner #quick li ul li a.settings,#header #header-inner #quick li ul li a.settings:hover
638 {
638 {
639 background: #FFF url("../images/icons/cog.png") no-repeat 4px 9px;
639 background: #FFF url("../images/icons/cog.png") no-repeat 4px 9px;
640 width: 167px;
640 width: 167px;
641 margin: 0;
641 margin: 0;
642 padding: 12px 9px 7px 24px;
642 padding: 12px 9px 7px 24px;
643 }
643 }
644
644
645 #header #header-inner #quick li ul li a.permissions,#header #header-inner #quick li ul li a.permissions:hover
645 #header #header-inner #quick li ul li a.permissions,#header #header-inner #quick li ul li a.permissions:hover
646 {
646 {
647 background: #FFF url("../images/icons/key.png") no-repeat 4px 9px;
647 background: #FFF url("../images/icons/key.png") no-repeat 4px 9px;
648 width: 167px;
648 width: 167px;
649 margin: 0;
649 margin: 0;
650 padding: 12px 9px 7px 24px;
650 padding: 12px 9px 7px 24px;
651 }
651 }
652
652
653 #header #header-inner #quick li ul li a.ldap,#header #header-inner #quick li ul li a.ldap:hover
653 #header #header-inner #quick li ul li a.ldap,#header #header-inner #quick li ul li a.ldap:hover
654 {
654 {
655 background: #FFF url("../images/icons/server_key.png") no-repeat 4px 9px;
655 background: #FFF url("../images/icons/server_key.png") no-repeat 4px 9px;
656 width: 167px;
656 width: 167px;
657 margin: 0;
657 margin: 0;
658 padding: 12px 9px 7px 24px;
658 padding: 12px 9px 7px 24px;
659 }
659 }
660
660
661 #header #header-inner #quick li ul li a.fork,#header #header-inner #quick li ul li a.fork:hover
661 #header #header-inner #quick li ul li a.fork,#header #header-inner #quick li ul li a.fork:hover
662 {
662 {
663 background: #FFF url("../images/icons/arrow_divide.png") no-repeat 4px
663 background: #FFF url("../images/icons/arrow_divide.png") no-repeat 4px
664 9px;
664 9px;
665 width: 167px;
665 width: 167px;
666 margin: 0;
666 margin: 0;
667 padding: 12px 9px 7px 24px;
667 padding: 12px 9px 7px 24px;
668 }
668 }
669
669
670 #header #header-inner #quick li ul li a.locking_add,#header #header-inner #quick li ul li a.locking_add:hover
670 #header #header-inner #quick li ul li a.locking_add,#header #header-inner #quick li ul li a.locking_add:hover
671 {
671 {
672 background: #FFF url("../images/icons/lock_add.png") no-repeat 4px
672 background: #FFF url("../images/icons/lock_add.png") no-repeat 4px
673 9px;
673 9px;
674 width: 167px;
674 width: 167px;
675 margin: 0;
675 margin: 0;
676 padding: 12px 9px 7px 24px;
676 padding: 12px 9px 7px 24px;
677 }
677 }
678
678
679 #header #header-inner #quick li ul li a.locking_del,#header #header-inner #quick li ul li a.locking_del:hover
679 #header #header-inner #quick li ul li a.locking_del,#header #header-inner #quick li ul li a.locking_del:hover
680 {
680 {
681 background: #FFF url("../images/icons/lock_delete.png") no-repeat 4px
681 background: #FFF url("../images/icons/lock_delete.png") no-repeat 4px
682 9px;
682 9px;
683 width: 167px;
683 width: 167px;
684 margin: 0;
684 margin: 0;
685 padding: 12px 9px 7px 24px;
685 padding: 12px 9px 7px 24px;
686 }
686 }
687
687
688 #header #header-inner #quick li ul li a.pull_request,#header #header-inner #quick li ul li a.pull_request:hover
688 #header #header-inner #quick li ul li a.pull_request,#header #header-inner #quick li ul li a.pull_request:hover
689 {
689 {
690 background: #FFF url("../images/icons/arrow_join.png") no-repeat 4px
690 background: #FFF url("../images/icons/arrow_join.png") no-repeat 4px
691 9px;
691 9px;
692 width: 167px;
692 width: 167px;
693 margin: 0;
693 margin: 0;
694 padding: 12px 9px 7px 24px;
694 padding: 12px 9px 7px 24px;
695 }
695 }
696
696
697 #header #header-inner #quick li ul li a.compare_request,#header #header-inner #quick li ul li a.compare_request:hover
697 #header #header-inner #quick li ul li a.compare_request,#header #header-inner #quick li ul li a.compare_request:hover
698 {
698 {
699 background: #FFF url("../images/icons/arrow_inout.png") no-repeat 4px
699 background: #FFF url("../images/icons/arrow_inout.png") no-repeat 4px
700 9px;
700 9px;
701 width: 167px;
701 width: 167px;
702 margin: 0;
702 margin: 0;
703 padding: 12px 9px 7px 24px;
703 padding: 12px 9px 7px 24px;
704 }
704 }
705
705
706 #header #header-inner #quick li ul li a.search,#header #header-inner #quick li ul li a.search:hover
706 #header #header-inner #quick li ul li a.search,#header #header-inner #quick li ul li a.search:hover
707 {
707 {
708 background: #FFF url("../images/icons/search_16.png") no-repeat 4px 9px;
708 background: #FFF url("../images/icons/search_16.png") no-repeat 4px 9px;
709 width: 167px;
709 width: 167px;
710 margin: 0;
710 margin: 0;
711 padding: 12px 9px 7px 24px;
711 padding: 12px 9px 7px 24px;
712 }
712 }
713
713
714 #header #header-inner #quick li ul li a.delete,#header #header-inner #quick li ul li a.delete:hover
714 #header #header-inner #quick li ul li a.delete,#header #header-inner #quick li ul li a.delete:hover
715 {
715 {
716 background: #FFF url("../images/icons/delete.png") no-repeat 4px 9px;
716 background: #FFF url("../images/icons/delete.png") no-repeat 4px 9px;
717 width: 167px;
717 width: 167px;
718 margin: 0;
718 margin: 0;
719 padding: 12px 9px 7px 24px;
719 padding: 12px 9px 7px 24px;
720 }
720 }
721
721
722 #header #header-inner #quick li ul li a.branches,#header #header-inner #quick li ul li a.branches:hover
722 #header #header-inner #quick li ul li a.branches,#header #header-inner #quick li ul li a.branches:hover
723 {
723 {
724 background: #FFF url("../images/icons/arrow_branch.png") no-repeat 4px
724 background: #FFF url("../images/icons/arrow_branch.png") no-repeat 4px
725 9px;
725 9px;
726 width: 167px;
726 width: 167px;
727 margin: 0;
727 margin: 0;
728 padding: 12px 9px 7px 24px;
728 padding: 12px 9px 7px 24px;
729 }
729 }
730
730
731 #header #header-inner #quick li ul li a.tags,
731 #header #header-inner #quick li ul li a.tags,
732 #header #header-inner #quick li ul li a.tags:hover{
732 #header #header-inner #quick li ul li a.tags:hover{
733 background: #FFF url("../images/icons/tag_blue.png") no-repeat 4px 9px;
733 background: #FFF url("../images/icons/tag_blue.png") no-repeat 4px 9px;
734 width: 167px;
734 width: 167px;
735 margin: 0;
735 margin: 0;
736 padding: 12px 9px 7px 24px;
736 padding: 12px 9px 7px 24px;
737 }
737 }
738
738
739 #header #header-inner #quick li ul li a.bookmarks,
739 #header #header-inner #quick li ul li a.bookmarks,
740 #header #header-inner #quick li ul li a.bookmarks:hover{
740 #header #header-inner #quick li ul li a.bookmarks:hover{
741 background: #FFF url("../images/icons/tag_green.png") no-repeat 4px 9px;
741 background: #FFF url("../images/icons/tag_green.png") no-repeat 4px 9px;
742 width: 167px;
742 width: 167px;
743 margin: 0;
743 margin: 0;
744 padding: 12px 9px 7px 24px;
744 padding: 12px 9px 7px 24px;
745 }
745 }
746
746
747 #header #header-inner #quick li ul li a.admin,
747 #header #header-inner #quick li ul li a.admin,
748 #header #header-inner #quick li ul li a.admin:hover{
748 #header #header-inner #quick li ul li a.admin:hover{
749 background: #FFF url("../images/icons/cog_edit.png") no-repeat 4px 9px;
749 background: #FFF url("../images/icons/cog_edit.png") no-repeat 4px 9px;
750 width: 167px;
750 width: 167px;
751 margin: 0;
751 margin: 0;
752 padding: 12px 9px 7px 24px;
752 padding: 12px 9px 7px 24px;
753 }
753 }
754
754
755 .groups_breadcrumbs a {
755 .groups_breadcrumbs a {
756 color: #fff;
756 color: #fff;
757 }
757 }
758
758
759 .groups_breadcrumbs a:hover {
759 .groups_breadcrumbs a:hover {
760 color: #bfe3ff;
760 color: #bfe3ff;
761 text-decoration: none;
761 text-decoration: none;
762 }
762 }
763
763
764 td.quick_repo_menu {
764 td.quick_repo_menu {
765 background: #FFF url("../images/vertical-indicator.png") 8px 50% no-repeat !important;
765 background: #FFF url("../images/vertical-indicator.png") 8px 50% no-repeat !important;
766 cursor: pointer;
766 cursor: pointer;
767 width: 8px;
767 width: 8px;
768 border: 1px solid transparent;
768 border: 1px solid transparent;
769 }
769 }
770
770
771 td.quick_repo_menu.active {
771 td.quick_repo_menu.active {
772 background: url("../images/dt-arrow-dn.png") no-repeat scroll 5px 50% #FFFFFF !important;
772 background: url("../images/dt-arrow-dn.png") no-repeat scroll 5px 50% #FFFFFF !important;
773 border: 1px solid #003367;
773 border: 1px solid #003367;
774 box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
774 box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
775 cursor: pointer;
775 cursor: pointer;
776 }
776 }
777
777
778 td.quick_repo_menu .menu_items {
778 td.quick_repo_menu .menu_items {
779 margin-top: 10px;
779 margin-top: 10px;
780 margin-left:-6px;
780 margin-left:-6px;
781 width: 150px;
781 width: 150px;
782 position: absolute;
782 position: absolute;
783 background-color: #FFF;
783 background-color: #FFF;
784 background: none repeat scroll 0 0 #FFFFFF;
784 background: none repeat scroll 0 0 #FFFFFF;
785 border-color: #003367 #666666 #666666;
785 border-color: #003367 #666666 #666666;
786 border-right: 1px solid #666666;
786 border-right: 1px solid #666666;
787 border-style: solid;
787 border-style: solid;
788 border-width: 1px;
788 border-width: 1px;
789 box-shadow: 2px 8px 4px rgba(0, 0, 0, 0.2);
789 box-shadow: 2px 8px 4px rgba(0, 0, 0, 0.2);
790 border-top-style: none;
790 border-top-style: none;
791 }
791 }
792
792
793 td.quick_repo_menu .menu_items li {
793 td.quick_repo_menu .menu_items li {
794 padding: 0 !important;
794 padding: 0 !important;
795 }
795 }
796
796
797 td.quick_repo_menu .menu_items a {
797 td.quick_repo_menu .menu_items a {
798 display: block;
798 display: block;
799 padding: 4px 12px 4px 8px;
799 padding: 4px 12px 4px 8px;
800 }
800 }
801
801
802 td.quick_repo_menu .menu_items a:hover {
802 td.quick_repo_menu .menu_items a:hover {
803 background-color: #EEE;
803 background-color: #EEE;
804 text-decoration: none;
804 text-decoration: none;
805 }
805 }
806
806
807 td.quick_repo_menu .menu_items .icon img {
807 td.quick_repo_menu .menu_items .icon img {
808 margin-bottom: -2px;
808 margin-bottom: -2px;
809 }
809 }
810
810
811 td.quick_repo_menu .menu_items.hidden {
811 td.quick_repo_menu .menu_items.hidden {
812 display: none;
812 display: none;
813 }
813 }
814
814
815 .yui-dt-first th {
815 .yui-dt-first th {
816 text-align: left;
816 text-align: left;
817 }
817 }
818
818
819 /*
819 /*
820 Copyright (c) 2011, Yahoo! Inc. All rights reserved.
820 Copyright (c) 2011, Yahoo! Inc. All rights reserved.
821 Code licensed under the BSD License:
821 Code licensed under the BSD License:
822 http://developer.yahoo.com/yui/license.html
822 http://developer.yahoo.com/yui/license.html
823 version: 2.9.0
823 version: 2.9.0
824 */
824 */
825 .yui-skin-sam .yui-dt-mask {
825 .yui-skin-sam .yui-dt-mask {
826 position: absolute;
826 position: absolute;
827 z-index: 9500;
827 z-index: 9500;
828 }
828 }
829 .yui-dt-tmp {
829 .yui-dt-tmp {
830 position: absolute;
830 position: absolute;
831 left: -9000px;
831 left: -9000px;
832 }
832 }
833 .yui-dt-scrollable .yui-dt-bd { overflow: auto }
833 .yui-dt-scrollable .yui-dt-bd { overflow: auto }
834 .yui-dt-scrollable .yui-dt-hd {
834 .yui-dt-scrollable .yui-dt-hd {
835 overflow: hidden;
835 overflow: hidden;
836 position: relative;
836 position: relative;
837 }
837 }
838 .yui-dt-scrollable .yui-dt-bd thead tr,
838 .yui-dt-scrollable .yui-dt-bd thead tr,
839 .yui-dt-scrollable .yui-dt-bd thead th {
839 .yui-dt-scrollable .yui-dt-bd thead th {
840 position: absolute;
840 position: absolute;
841 left: -1500px;
841 left: -1500px;
842 }
842 }
843 .yui-dt-scrollable tbody { -moz-outline: 0 }
843 .yui-dt-scrollable tbody { -moz-outline: 0 }
844 .yui-skin-sam thead .yui-dt-sortable { cursor: pointer }
844 .yui-skin-sam thead .yui-dt-sortable { cursor: pointer }
845 .yui-skin-sam thead .yui-dt-draggable { cursor: move }
845 .yui-skin-sam thead .yui-dt-draggable { cursor: move }
846 .yui-dt-coltarget {
846 .yui-dt-coltarget {
847 position: absolute;
847 position: absolute;
848 z-index: 999;
848 z-index: 999;
849 }
849 }
850 .yui-dt-hd { zoom: 1 }
850 .yui-dt-hd { zoom: 1 }
851 th.yui-dt-resizeable .yui-dt-resizerliner { position: relative }
851 th.yui-dt-resizeable .yui-dt-resizerliner { position: relative }
852 .yui-dt-resizer {
852 .yui-dt-resizer {
853 position: absolute;
853 position: absolute;
854 right: 0;
854 right: 0;
855 bottom: 0;
855 bottom: 0;
856 height: 100%;
856 height: 100%;
857 cursor: e-resize;
857 cursor: e-resize;
858 cursor: col-resize;
858 cursor: col-resize;
859 background-color: #CCC;
859 background-color: #CCC;
860 opacity: 0;
860 opacity: 0;
861 filter: alpha(opacity=0);
861 filter: alpha(opacity=0);
862 }
862 }
863 .yui-dt-resizerproxy {
863 .yui-dt-resizerproxy {
864 visibility: hidden;
864 visibility: hidden;
865 position: absolute;
865 position: absolute;
866 z-index: 9000;
866 z-index: 9000;
867 background-color: #CCC;
867 background-color: #CCC;
868 opacity: 0;
868 opacity: 0;
869 filter: alpha(opacity=0);
869 filter: alpha(opacity=0);
870 }
870 }
871 th.yui-dt-hidden .yui-dt-liner,
871 th.yui-dt-hidden .yui-dt-liner,
872 td.yui-dt-hidden .yui-dt-liner,
872 td.yui-dt-hidden .yui-dt-liner,
873 th.yui-dt-hidden .yui-dt-resizer { display: none }
873 th.yui-dt-hidden .yui-dt-resizer { display: none }
874 .yui-dt-editor,
874 .yui-dt-editor,
875 .yui-dt-editor-shim {
875 .yui-dt-editor-shim {
876 position: absolute;
876 position: absolute;
877 z-index: 9000;
877 z-index: 9000;
878 }
878 }
879 .yui-skin-sam .yui-dt table {
879 .yui-skin-sam .yui-dt table {
880 margin: 0;
880 margin: 0;
881 padding: 0;
881 padding: 0;
882 font-family: arial;
882 font-family: arial;
883 font-size: inherit;
883 font-size: inherit;
884 border-collapse: separate;
884 border-collapse: separate;
885 *border-collapse: collapse;
885 *border-collapse: collapse;
886 border-spacing: 0;
886 border-spacing: 0;
887 border: 1px solid #7f7f7f;
887 border: 1px solid #7f7f7f;
888 }
888 }
889 .yui-skin-sam .yui-dt thead { border-spacing: 0 }
889 .yui-skin-sam .yui-dt thead { border-spacing: 0 }
890 .yui-skin-sam .yui-dt caption {
890 .yui-skin-sam .yui-dt caption {
891 color: #000;
891 color: #000;
892 font-size: 85%;
892 font-size: 85%;
893 font-weight: normal;
893 font-weight: normal;
894 font-style: italic;
894 font-style: italic;
895 line-height: 1;
895 line-height: 1;
896 padding: 1em 0;
896 padding: 1em 0;
897 text-align: center;
897 text-align: center;
898 }
898 }
899 .yui-skin-sam .yui-dt th { background: #d8d8da url(../images/sprite.png) repeat-x 0 0 }
899 .yui-skin-sam .yui-dt th { background: #d8d8da url(../images/sprite.png) repeat-x 0 0 }
900 .yui-skin-sam .yui-dt th,
900 .yui-skin-sam .yui-dt th,
901 .yui-skin-sam .yui-dt th a {
901 .yui-skin-sam .yui-dt th a {
902 font-weight: normal;
902 font-weight: normal;
903 text-decoration: none;
903 text-decoration: none;
904 color: #000;
904 color: #000;
905 vertical-align: bottom;
905 vertical-align: bottom;
906 }
906 }
907 .yui-skin-sam .yui-dt th {
907 .yui-skin-sam .yui-dt th {
908 margin: 0;
908 margin: 0;
909 padding: 0;
909 padding: 0;
910 border: 0;
910 border: 0;
911 border-right: 1px solid #cbcbcb;
911 border-right: 1px solid #cbcbcb;
912 }
912 }
913 .yui-skin-sam .yui-dt tr.yui-dt-first td { border-top: 1px solid #7f7f7f }
913 .yui-skin-sam .yui-dt tr.yui-dt-first td { border-top: 1px solid #7f7f7f }
914 .yui-skin-sam .yui-dt th .yui-dt-liner { white-space: nowrap }
914 .yui-skin-sam .yui-dt th .yui-dt-liner { white-space: nowrap }
915 .yui-skin-sam .yui-dt-liner {
915 .yui-skin-sam .yui-dt-liner {
916 margin: 0;
916 margin: 0;
917 padding: 0;
917 padding: 0;
918 }
918 }
919 .yui-skin-sam .yui-dt-coltarget {
919 .yui-skin-sam .yui-dt-coltarget {
920 width: 5px;
920 width: 5px;
921 background-color: red;
921 background-color: red;
922 }
922 }
923 .yui-skin-sam .yui-dt td {
923 .yui-skin-sam .yui-dt td {
924 margin: 0;
924 margin: 0;
925 padding: 0;
925 padding: 0;
926 border: 0;
926 border: 0;
927 border-right: 1px solid #cbcbcb;
927 border-right: 1px solid #cbcbcb;
928 text-align: left;
928 text-align: left;
929 }
929 }
930 .yui-skin-sam .yui-dt-list td { border-right: 0 }
930 .yui-skin-sam .yui-dt-list td { border-right: 0 }
931 .yui-skin-sam .yui-dt-resizer { width: 6px }
931 .yui-skin-sam .yui-dt-resizer { width: 6px }
932 .yui-skin-sam .yui-dt-mask {
932 .yui-skin-sam .yui-dt-mask {
933 background-color: #000;
933 background-color: #000;
934 opacity: .25;
934 opacity: .25;
935 filter: alpha(opacity=25);
935 filter: alpha(opacity=25);
936 }
936 }
937 .yui-skin-sam .yui-dt-message { background-color: #FFF }
937 .yui-skin-sam .yui-dt-message { background-color: #FFF }
938 .yui-skin-sam .yui-dt-scrollable table { border: 0 }
938 .yui-skin-sam .yui-dt-scrollable table { border: 0 }
939 .yui-skin-sam .yui-dt-scrollable .yui-dt-hd {
939 .yui-skin-sam .yui-dt-scrollable .yui-dt-hd {
940 border-left: 1px solid #7f7f7f;
940 border-left: 1px solid #7f7f7f;
941 border-top: 1px solid #7f7f7f;
941 border-top: 1px solid #7f7f7f;
942 border-right: 1px solid #7f7f7f;
942 border-right: 1px solid #7f7f7f;
943 }
943 }
944 .yui-skin-sam .yui-dt-scrollable .yui-dt-bd {
944 .yui-skin-sam .yui-dt-scrollable .yui-dt-bd {
945 border-left: 1px solid #7f7f7f;
945 border-left: 1px solid #7f7f7f;
946 border-bottom: 1px solid #7f7f7f;
946 border-bottom: 1px solid #7f7f7f;
947 border-right: 1px solid #7f7f7f;
947 border-right: 1px solid #7f7f7f;
948 background-color: #FFF;
948 background-color: #FFF;
949 }
949 }
950 .yui-skin-sam .yui-dt-scrollable .yui-dt-data tr.yui-dt-last td { border-bottom: 1px solid #7f7f7f }
950 .yui-skin-sam .yui-dt-scrollable .yui-dt-data tr.yui-dt-last td { border-bottom: 1px solid #7f7f7f }
951 .yui-skin-sam th.yui-dt-asc,
951 .yui-skin-sam th.yui-dt-asc,
952 .yui-skin-sam th.yui-dt-desc { background: url(../images/sprite.png) repeat-x 0 -100px }
952 .yui-skin-sam th.yui-dt-desc { background: url(../images/sprite.png) repeat-x 0 -100px }
953 .yui-skin-sam th.yui-dt-sortable .yui-dt-label { margin-right: 10px }
953 .yui-skin-sam th.yui-dt-sortable .yui-dt-label { margin-right: 10px }
954 .yui-skin-sam th.yui-dt-asc .yui-dt-liner { background: url(../images/dt-arrow-up.png) no-repeat right }
954 .yui-skin-sam th.yui-dt-asc .yui-dt-liner { background: url(../images/dt-arrow-up.png) no-repeat right }
955 .yui-skin-sam th.yui-dt-desc .yui-dt-liner { background: url(../images/dt-arrow-dn.png) no-repeat right }
955 .yui-skin-sam th.yui-dt-desc .yui-dt-liner { background: url(../images/dt-arrow-dn.png) no-repeat right }
956 tbody .yui-dt-editable { cursor: pointer }
956 tbody .yui-dt-editable { cursor: pointer }
957 .yui-dt-editor {
957 .yui-dt-editor {
958 text-align: left;
958 text-align: left;
959 background-color: #f2f2f2;
959 background-color: #f2f2f2;
960 border: 1px solid #808080;
960 border: 1px solid #808080;
961 padding: 6px;
961 padding: 6px;
962 }
962 }
963 .yui-dt-editor label {
963 .yui-dt-editor label {
964 padding-left: 4px;
964 padding-left: 4px;
965 padding-right: 6px;
965 padding-right: 6px;
966 }
966 }
967 .yui-dt-editor .yui-dt-button {
967 .yui-dt-editor .yui-dt-button {
968 padding-top: 6px;
968 padding-top: 6px;
969 text-align: right;
969 text-align: right;
970 }
970 }
971 .yui-dt-editor .yui-dt-button button {
971 .yui-dt-editor .yui-dt-button button {
972 background: url(../images/sprite.png) repeat-x 0 0;
972 background: url(../images/sprite.png) repeat-x 0 0;
973 border: 1px solid #999;
973 border: 1px solid #999;
974 width: 4em;
974 width: 4em;
975 height: 1.8em;
975 height: 1.8em;
976 margin-left: 6px;
976 margin-left: 6px;
977 }
977 }
978 .yui-dt-editor .yui-dt-button button.yui-dt-default {
978 .yui-dt-editor .yui-dt-button button.yui-dt-default {
979 background: url(../images/sprite.png) repeat-x 0 -1400px;
979 background: url(../images/sprite.png) repeat-x 0 -1400px;
980 background-color: #5584e0;
980 background-color: #5584e0;
981 border: 1px solid #304369;
981 border: 1px solid #304369;
982 color: #FFF;
982 color: #FFF;
983 }
983 }
984 .yui-dt-editor .yui-dt-button button:hover {
984 .yui-dt-editor .yui-dt-button button:hover {
985 background: url(../images/sprite.png) repeat-x 0 -1300px;
985 background: url(../images/sprite.png) repeat-x 0 -1300px;
986 color: #000;
986 color: #000;
987 }
987 }
988 .yui-dt-editor .yui-dt-button button:active {
988 .yui-dt-editor .yui-dt-button button:active {
989 background: url(../images/sprite.png) repeat-x 0 -1700px;
989 background: url(../images/sprite.png) repeat-x 0 -1700px;
990 color: #000;
990 color: #000;
991 }
991 }
992 .yui-skin-sam tr.yui-dt-even { background-color: #FFF }
992 .yui-skin-sam tr.yui-dt-even { background-color: #FFF }
993 .yui-skin-sam tr.yui-dt-odd { background-color: #edf5ff }
993 .yui-skin-sam tr.yui-dt-odd { background-color: #edf5ff }
994 .yui-skin-sam tr.yui-dt-even td.yui-dt-asc,
994 .yui-skin-sam tr.yui-dt-even td.yui-dt-asc,
995 .yui-skin-sam tr.yui-dt-even td.yui-dt-desc { background-color: #edf5ff }
995 .yui-skin-sam tr.yui-dt-even td.yui-dt-desc { background-color: #edf5ff }
996 .yui-skin-sam tr.yui-dt-odd td.yui-dt-asc,
996 .yui-skin-sam tr.yui-dt-odd td.yui-dt-asc,
997 .yui-skin-sam tr.yui-dt-odd td.yui-dt-desc { background-color: #dbeaff }
997 .yui-skin-sam tr.yui-dt-odd td.yui-dt-desc { background-color: #dbeaff }
998 .yui-skin-sam .yui-dt-list tr.yui-dt-even { background-color: #FFF }
998 .yui-skin-sam .yui-dt-list tr.yui-dt-even { background-color: #FFF }
999 .yui-skin-sam .yui-dt-list tr.yui-dt-odd { background-color: #FFF }
999 .yui-skin-sam .yui-dt-list tr.yui-dt-odd { background-color: #FFF }
1000 .yui-skin-sam .yui-dt-list tr.yui-dt-even td.yui-dt-asc,
1000 .yui-skin-sam .yui-dt-list tr.yui-dt-even td.yui-dt-asc,
1001 .yui-skin-sam .yui-dt-list tr.yui-dt-even td.yui-dt-desc { background-color: #edf5ff }
1001 .yui-skin-sam .yui-dt-list tr.yui-dt-even td.yui-dt-desc { background-color: #edf5ff }
1002 .yui-skin-sam .yui-dt-list tr.yui-dt-odd td.yui-dt-asc,
1002 .yui-skin-sam .yui-dt-list tr.yui-dt-odd td.yui-dt-asc,
1003 .yui-skin-sam .yui-dt-list tr.yui-dt-odd td.yui-dt-desc { background-color: #edf5ff }
1003 .yui-skin-sam .yui-dt-list tr.yui-dt-odd td.yui-dt-desc { background-color: #edf5ff }
1004 .yui-skin-sam th.yui-dt-highlighted,
1004 .yui-skin-sam th.yui-dt-highlighted,
1005 .yui-skin-sam th.yui-dt-highlighted a { background-color: #b2d2ff }
1005 .yui-skin-sam th.yui-dt-highlighted a { background-color: #b2d2ff }
1006 .yui-skin-sam tr.yui-dt-highlighted,
1006 .yui-skin-sam tr.yui-dt-highlighted,
1007 .yui-skin-sam tr.yui-dt-highlighted td.yui-dt-asc,
1007 .yui-skin-sam tr.yui-dt-highlighted td.yui-dt-asc,
1008 .yui-skin-sam tr.yui-dt-highlighted td.yui-dt-desc,
1008 .yui-skin-sam tr.yui-dt-highlighted td.yui-dt-desc,
1009 .yui-skin-sam tr.yui-dt-even td.yui-dt-highlighted,
1009 .yui-skin-sam tr.yui-dt-even td.yui-dt-highlighted,
1010 .yui-skin-sam tr.yui-dt-odd td.yui-dt-highlighted {
1010 .yui-skin-sam tr.yui-dt-odd td.yui-dt-highlighted {
1011 cursor: pointer;
1011 cursor: pointer;
1012 background-color: #b2d2ff;
1012 background-color: #b2d2ff;
1013 }
1013 }
1014 .yui-skin-sam .yui-dt-list th.yui-dt-highlighted,
1014 .yui-skin-sam .yui-dt-list th.yui-dt-highlighted,
1015 .yui-skin-sam .yui-dt-list th.yui-dt-highlighted a { background-color: #b2d2ff }
1015 .yui-skin-sam .yui-dt-list th.yui-dt-highlighted a { background-color: #b2d2ff }
1016 .yui-skin-sam .yui-dt-list tr.yui-dt-highlighted,
1016 .yui-skin-sam .yui-dt-list tr.yui-dt-highlighted,
1017 .yui-skin-sam .yui-dt-list tr.yui-dt-highlighted td.yui-dt-asc,
1017 .yui-skin-sam .yui-dt-list tr.yui-dt-highlighted td.yui-dt-asc,
1018 .yui-skin-sam .yui-dt-list tr.yui-dt-highlighted td.yui-dt-desc,
1018 .yui-skin-sam .yui-dt-list tr.yui-dt-highlighted td.yui-dt-desc,
1019 .yui-skin-sam .yui-dt-list tr.yui-dt-even td.yui-dt-highlighted,
1019 .yui-skin-sam .yui-dt-list tr.yui-dt-even td.yui-dt-highlighted,
1020 .yui-skin-sam .yui-dt-list tr.yui-dt-odd td.yui-dt-highlighted {
1020 .yui-skin-sam .yui-dt-list tr.yui-dt-odd td.yui-dt-highlighted {
1021 cursor: pointer;
1021 cursor: pointer;
1022 background-color: #b2d2ff;
1022 background-color: #b2d2ff;
1023 }
1023 }
1024 .yui-skin-sam th.yui-dt-selected,
1024 .yui-skin-sam th.yui-dt-selected,
1025 .yui-skin-sam th.yui-dt-selected a { background-color: #446cd7 }
1025 .yui-skin-sam th.yui-dt-selected a { background-color: #446cd7 }
1026 .yui-skin-sam tr.yui-dt-selected td,
1026 .yui-skin-sam tr.yui-dt-selected td,
1027 .yui-skin-sam tr.yui-dt-selected td.yui-dt-asc,
1027 .yui-skin-sam tr.yui-dt-selected td.yui-dt-asc,
1028 .yui-skin-sam tr.yui-dt-selected td.yui-dt-desc {
1028 .yui-skin-sam tr.yui-dt-selected td.yui-dt-desc {
1029 background-color: #426fd9;
1029 background-color: #426fd9;
1030 color: #FFF;
1030 color: #FFF;
1031 }
1031 }
1032 .yui-skin-sam tr.yui-dt-even td.yui-dt-selected,
1032 .yui-skin-sam tr.yui-dt-even td.yui-dt-selected,
1033 .yui-skin-sam tr.yui-dt-odd td.yui-dt-selected {
1033 .yui-skin-sam tr.yui-dt-odd td.yui-dt-selected {
1034 background-color: #446cd7;
1034 background-color: #446cd7;
1035 color: #FFF;
1035 color: #FFF;
1036 }
1036 }
1037 .yui-skin-sam .yui-dt-list th.yui-dt-selected,
1037 .yui-skin-sam .yui-dt-list th.yui-dt-selected,
1038 .yui-skin-sam .yui-dt-list th.yui-dt-selected a { background-color: #446cd7 }
1038 .yui-skin-sam .yui-dt-list th.yui-dt-selected a { background-color: #446cd7 }
1039 .yui-skin-sam .yui-dt-list tr.yui-dt-selected td,
1039 .yui-skin-sam .yui-dt-list tr.yui-dt-selected td,
1040 .yui-skin-sam .yui-dt-list tr.yui-dt-selected td.yui-dt-asc,
1040 .yui-skin-sam .yui-dt-list tr.yui-dt-selected td.yui-dt-asc,
1041 .yui-skin-sam .yui-dt-list tr.yui-dt-selected td.yui-dt-desc {
1041 .yui-skin-sam .yui-dt-list tr.yui-dt-selected td.yui-dt-desc {
1042 background-color: #426fd9;
1042 background-color: #426fd9;
1043 color: #FFF;
1043 color: #FFF;
1044 }
1044 }
1045 .yui-skin-sam .yui-dt-list tr.yui-dt-even td.yui-dt-selected,
1045 .yui-skin-sam .yui-dt-list tr.yui-dt-even td.yui-dt-selected,
1046 .yui-skin-sam .yui-dt-list tr.yui-dt-odd td.yui-dt-selected {
1046 .yui-skin-sam .yui-dt-list tr.yui-dt-odd td.yui-dt-selected {
1047 background-color: #446cd7;
1047 background-color: #446cd7;
1048 color: #FFF;
1048 color: #FFF;
1049 }
1049 }
1050 .yui-skin-sam .yui-dt-paginator {
1050 .yui-skin-sam .yui-dt-paginator {
1051 display: block;
1051 display: block;
1052 margin: 6px 0;
1052 margin: 6px 0;
1053 white-space: nowrap;
1053 white-space: nowrap;
1054 }
1054 }
1055 .yui-skin-sam .yui-dt-paginator .yui-dt-first,
1055 .yui-skin-sam .yui-dt-paginator .yui-dt-first,
1056 .yui-skin-sam .yui-dt-paginator .yui-dt-last,
1056 .yui-skin-sam .yui-dt-paginator .yui-dt-last,
1057 .yui-skin-sam .yui-dt-paginator .yui-dt-selected { padding: 2px 6px }
1057 .yui-skin-sam .yui-dt-paginator .yui-dt-selected { padding: 2px 6px }
1058 .yui-skin-sam .yui-dt-paginator a.yui-dt-first,
1058 .yui-skin-sam .yui-dt-paginator a.yui-dt-first,
1059 .yui-skin-sam .yui-dt-paginator a.yui-dt-last { text-decoration: none }
1059 .yui-skin-sam .yui-dt-paginator a.yui-dt-last { text-decoration: none }
1060 .yui-skin-sam .yui-dt-paginator .yui-dt-previous,
1060 .yui-skin-sam .yui-dt-paginator .yui-dt-previous,
1061 .yui-skin-sam .yui-dt-paginator .yui-dt-next { display: none }
1061 .yui-skin-sam .yui-dt-paginator .yui-dt-next { display: none }
1062 .yui-skin-sam a.yui-dt-page {
1062 .yui-skin-sam a.yui-dt-page {
1063 border: 1px solid #cbcbcb;
1063 border: 1px solid #cbcbcb;
1064 padding: 2px 6px;
1064 padding: 2px 6px;
1065 text-decoration: none;
1065 text-decoration: none;
1066 background-color: #fff;
1066 background-color: #fff;
1067 }
1067 }
1068 .yui-skin-sam .yui-dt-selected {
1068 .yui-skin-sam .yui-dt-selected {
1069 border: 1px solid #fff;
1069 border: 1px solid #fff;
1070 background-color: #fff;
1070 background-color: #fff;
1071 }
1071 }
1072
1072
1073 #content #left {
1073 #content #left {
1074 left: 0;
1074 left: 0;
1075 width: 280px;
1075 width: 280px;
1076 position: absolute;
1076 position: absolute;
1077 }
1077 }
1078
1078
1079 #content #right {
1079 #content #right {
1080 margin: 0 60px 10px 290px;
1080 margin: 0 60px 10px 290px;
1081 }
1081 }
1082
1082
1083 #content div.box {
1083 #content div.box {
1084 clear: both;
1084 clear: both;
1085 overflow: hidden;
1085 overflow: hidden;
1086 background: #fff;
1086 background: #fff;
1087 margin: 0 0 10px;
1087 margin: 0 0 10px;
1088 padding: 0 0 10px;
1088 padding: 0 0 10px;
1089 -webkit-border-radius: 4px 4px 4px 4px;
1089 -webkit-border-radius: 4px 4px 4px 4px;
1090 -khtml-border-radius: 4px 4px 4px 4px;
1090 -khtml-border-radius: 4px 4px 4px 4px;
1091 -moz-border-radius: 4px 4px 4px 4px;
1091 -moz-border-radius: 4px 4px 4px 4px;
1092 border-radius: 4px 4px 4px 4px;
1092 border-radius: 4px 4px 4px 4px;
1093 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
1093 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
1094 }
1094 }
1095
1095
1096 #content div.box-left {
1096 #content div.box-left {
1097 width: 49%;
1097 width: 49%;
1098 clear: none;
1098 clear: none;
1099 float: left;
1099 float: left;
1100 margin: 0 0 10px;
1100 margin: 0 0 10px;
1101 }
1101 }
1102
1102
1103 #content div.box-right {
1103 #content div.box-right {
1104 width: 49%;
1104 width: 49%;
1105 clear: none;
1105 clear: none;
1106 float: right;
1106 float: right;
1107 margin: 0 0 10px;
1107 margin: 0 0 10px;
1108 }
1108 }
1109
1109
1110 #content div.box div.title {
1110 #content div.box div.title {
1111 clear: both;
1111 clear: both;
1112 overflow: hidden;
1112 overflow: hidden;
1113 background-color: #003B76;
1113 background-color: #003B76;
1114 background-repeat: repeat-x;
1114 background-repeat: repeat-x;
1115 background-image: -khtml-gradient(linear, left top, left bottom, from(#003B76), to(#00376E) );
1115 background-image: -khtml-gradient(linear, left top, left bottom, from(#003B76), to(#00376E) );
1116 background-image: -moz-linear-gradient(top, #003b76, #00376e);
1116 background-image: -moz-linear-gradient(top, #003b76, #00376e);
1117 background-image: -ms-linear-gradient(top, #003b76, #00376e);
1117 background-image: -ms-linear-gradient(top, #003b76, #00376e);
1118 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76), color-stop(100%, #00376e) );
1118 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76), color-stop(100%, #00376e) );
1119 background-image: -webkit-linear-gradient(top, #003b76, #00376e);
1119 background-image: -webkit-linear-gradient(top, #003b76, #00376e);
1120 background-image: -o-linear-gradient(top, #003b76, #00376e);
1120 background-image: -o-linear-gradient(top, #003b76, #00376e);
1121 background-image: linear-gradient(top, #003b76, #00376e);
1121 background-image: linear-gradient(top, #003b76, #00376e);
1122 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76', endColorstr='#00376e', GradientType=0 );
1122 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76', endColorstr='#00376e', GradientType=0 );
1123 margin: 0 0 20px;
1123 margin: 0 0 20px;
1124 padding: 0;
1124 padding: 0;
1125 }
1125 }
1126
1126
1127 #content div.box div.title h5 {
1127 #content div.box div.title h5 {
1128 float: left;
1128 float: left;
1129 border: none;
1129 border: none;
1130 color: #fff;
1130 color: #fff;
1131 text-transform: uppercase;
1131 text-transform: uppercase;
1132 margin: 0;
1132 margin: 0;
1133 padding: 11px 0 11px 10px;
1133 padding: 11px 0 11px 10px;
1134 }
1134 }
1135
1135
1136 #content div.box div.title .link-white{
1136 #content div.box div.title .link-white{
1137 color: #FFFFFF;
1137 color: #FFFFFF;
1138 }
1138 }
1139
1139
1140 #content div.box div.title .link-white.current{
1140 #content div.box div.title .link-white.current{
1141 color: #BFE3FF;
1141 color: #BFE3FF;
1142 }
1142 }
1143
1143
1144 #content div.box div.title ul.links li {
1144 #content div.box div.title ul.links li {
1145 list-style: none;
1145 list-style: none;
1146 float: left;
1146 float: left;
1147 margin: 0;
1147 margin: 0;
1148 padding: 0;
1148 padding: 0;
1149 }
1149 }
1150
1150
1151 #content div.box div.title ul.links li a {
1151 #content div.box div.title ul.links li a {
1152 border-left: 1px solid #316293;
1152 border-left: 1px solid #316293;
1153 color: #FFFFFF;
1153 color: #FFFFFF;
1154 display: block;
1154 display: block;
1155 float: left;
1155 float: left;
1156 font-size: 13px;
1156 font-size: 13px;
1157 font-weight: 700;
1157 font-weight: 700;
1158 height: 1%;
1158 height: 1%;
1159 margin: 0;
1159 margin: 0;
1160 padding: 11px 22px 12px;
1160 padding: 11px 22px 12px;
1161 text-decoration: none;
1161 text-decoration: none;
1162 }
1162 }
1163
1163
1164 #content div.box h1,#content div.box h2,#content div.box h3,#content div.box h4,#content div.box h5,#content div.box h6,
1164 #content div.box h1,#content div.box h2,#content div.box h3,#content div.box h4,#content div.box h5,#content div.box h6,
1165 #content div.box div.h1,#content div.box div.h2,#content div.box div.h3,#content div.box div.h4,#content div.box div.h5,#content div.box div.h6
1165 #content div.box div.h1,#content div.box div.h2,#content div.box div.h3,#content div.box div.h4,#content div.box div.h5,#content div.box div.h6
1166
1166
1167 {
1167 {
1168 clear: both;
1168 clear: both;
1169 overflow: hidden;
1169 overflow: hidden;
1170 border-bottom: 1px solid #DDD;
1170 border-bottom: 1px solid #DDD;
1171 margin: 10px 20px;
1171 margin: 10px 20px;
1172 padding: 0 0 15px;
1172 padding: 0 0 15px;
1173 }
1173 }
1174
1174
1175 #content div.box p {
1175 #content div.box p {
1176 color: #5f5f5f;
1176 color: #5f5f5f;
1177 font-size: 12px;
1177 font-size: 12px;
1178 line-height: 150%;
1178 line-height: 150%;
1179 margin: 0 24px 10px;
1179 margin: 0 24px 10px;
1180 padding: 0;
1180 padding: 0;
1181 }
1181 }
1182
1182
1183 #content div.box blockquote {
1183 #content div.box blockquote {
1184 border-left: 4px solid #DDD;
1184 border-left: 4px solid #DDD;
1185 color: #5f5f5f;
1185 color: #5f5f5f;
1186 font-size: 11px;
1186 font-size: 11px;
1187 line-height: 150%;
1187 line-height: 150%;
1188 margin: 0 34px;
1188 margin: 0 34px;
1189 padding: 0 0 0 14px;
1189 padding: 0 0 0 14px;
1190 }
1190 }
1191
1191
1192 #content div.box blockquote p {
1192 #content div.box blockquote p {
1193 margin: 10px 0;
1193 margin: 10px 0;
1194 padding: 0;
1194 padding: 0;
1195 }
1195 }
1196
1196
1197 #content div.box dl {
1197 #content div.box dl {
1198 margin: 10px 0px;
1198 margin: 10px 0px;
1199 }
1199 }
1200
1200
1201 #content div.box dt {
1201 #content div.box dt {
1202 font-size: 12px;
1202 font-size: 12px;
1203 margin: 0;
1203 margin: 0;
1204 }
1204 }
1205
1205
1206 #content div.box dd {
1206 #content div.box dd {
1207 font-size: 12px;
1207 font-size: 12px;
1208 margin: 0;
1208 margin: 0;
1209 padding: 8px 0 8px 15px;
1209 padding: 8px 0 8px 15px;
1210 }
1210 }
1211
1211
1212 #content div.box li {
1212 #content div.box li {
1213 font-size: 12px;
1213 font-size: 12px;
1214 padding: 4px 0;
1214 padding: 4px 0;
1215 }
1215 }
1216
1216
1217 #content div.box ul.disc,#content div.box ul.circle {
1217 #content div.box ul.disc,#content div.box ul.circle {
1218 margin: 10px 24px 10px 38px;
1218 margin: 10px 24px 10px 38px;
1219 }
1219 }
1220
1220
1221 #content div.box ul.square {
1221 #content div.box ul.square {
1222 margin: 10px 24px 10px 40px;
1222 margin: 10px 24px 10px 40px;
1223 }
1223 }
1224
1224
1225 #content div.box img.left {
1225 #content div.box img.left {
1226 border: none;
1226 border: none;
1227 float: left;
1227 float: left;
1228 margin: 10px 10px 10px 0;
1228 margin: 10px 10px 10px 0;
1229 }
1229 }
1230
1230
1231 #content div.box img.right {
1231 #content div.box img.right {
1232 border: none;
1232 border: none;
1233 float: right;
1233 float: right;
1234 margin: 10px 0 10px 10px;
1234 margin: 10px 0 10px 10px;
1235 }
1235 }
1236
1236
1237 #content div.box div.messages {
1237 #content div.box div.messages {
1238 clear: both;
1238 clear: both;
1239 overflow: hidden;
1239 overflow: hidden;
1240 margin: 0 20px;
1240 margin: 0 20px;
1241 padding: 0;
1241 padding: 0;
1242 }
1242 }
1243
1243
1244 #content div.box div.message {
1244 #content div.box div.message {
1245 clear: both;
1245 clear: both;
1246 overflow: hidden;
1246 overflow: hidden;
1247 margin: 0;
1247 margin: 0;
1248 padding: 5px 0;
1248 padding: 5px 0;
1249 white-space: pre-wrap;
1249 white-space: pre-wrap;
1250 }
1250 }
1251 #content div.box div.expand {
1251 #content div.box div.expand {
1252 width: 110%;
1252 width: 110%;
1253 height:14px;
1253 height:14px;
1254 font-size:10px;
1254 font-size:10px;
1255 text-align:center;
1255 text-align:center;
1256 cursor: pointer;
1256 cursor: pointer;
1257 color:#666;
1257 color:#666;
1258
1258
1259 background:-webkit-gradient(linear,0% 50%,100% 50%,color-stop(0%,rgba(255,255,255,0)),color-stop(100%,rgba(64,96,128,0.1)));
1259 background:-webkit-gradient(linear,0% 50%,100% 50%,color-stop(0%,rgba(255,255,255,0)),color-stop(100%,rgba(64,96,128,0.1)));
1260 background:-webkit-linear-gradient(top,rgba(255,255,255,0),rgba(64,96,128,0.1));
1260 background:-webkit-linear-gradient(top,rgba(255,255,255,0),rgba(64,96,128,0.1));
1261 background:-moz-linear-gradient(top,rgba(255,255,255,0),rgba(64,96,128,0.1));
1261 background:-moz-linear-gradient(top,rgba(255,255,255,0),rgba(64,96,128,0.1));
1262 background:-o-linear-gradient(top,rgba(255,255,255,0),rgba(64,96,128,0.1));
1262 background:-o-linear-gradient(top,rgba(255,255,255,0),rgba(64,96,128,0.1));
1263 background:-ms-linear-gradient(top,rgba(255,255,255,0),rgba(64,96,128,0.1));
1263 background:-ms-linear-gradient(top,rgba(255,255,255,0),rgba(64,96,128,0.1));
1264 background:linear-gradient(top,rgba(255,255,255,0),rgba(64,96,128,0.1));
1264 background:linear-gradient(top,rgba(255,255,255,0),rgba(64,96,128,0.1));
1265
1265
1266 display: none;
1266 display: none;
1267 }
1267 }
1268 #content div.box div.expand .expandtext {
1268 #content div.box div.expand .expandtext {
1269 background-color: #ffffff;
1269 background-color: #ffffff;
1270 padding: 2px;
1270 padding: 2px;
1271 border-radius: 2px;
1271 border-radius: 2px;
1272 }
1272 }
1273
1273
1274 #content div.box div.message a {
1274 #content div.box div.message a {
1275 font-weight: 400 !important;
1275 font-weight: 400 !important;
1276 }
1276 }
1277
1277
1278 #content div.box div.message div.image {
1278 #content div.box div.message div.image {
1279 float: left;
1279 float: left;
1280 margin: 9px 0 0 5px;
1280 margin: 9px 0 0 5px;
1281 padding: 6px;
1281 padding: 6px;
1282 }
1282 }
1283
1283
1284 #content div.box div.message div.image img {
1284 #content div.box div.message div.image img {
1285 vertical-align: middle;
1285 vertical-align: middle;
1286 margin: 0;
1286 margin: 0;
1287 }
1287 }
1288
1288
1289 #content div.box div.message div.text {
1289 #content div.box div.message div.text {
1290 float: left;
1290 float: left;
1291 margin: 0;
1291 margin: 0;
1292 padding: 9px 6px;
1292 padding: 9px 6px;
1293 }
1293 }
1294
1294
1295 #content div.box div.message div.dismiss a {
1295 #content div.box div.message div.dismiss a {
1296 height: 16px;
1296 height: 16px;
1297 width: 16px;
1297 width: 16px;
1298 display: block;
1298 display: block;
1299 background: url("../images/icons/cross.png") no-repeat;
1299 background: url("../images/icons/cross.png") no-repeat;
1300 margin: 15px 14px 0 0;
1300 margin: 15px 14px 0 0;
1301 padding: 0;
1301 padding: 0;
1302 }
1302 }
1303
1303
1304 #content div.box div.message div.text h1,#content div.box div.message div.text h2,#content div.box div.message div.text h3,#content div.box div.message div.text h4,#content div.box div.message div.text h5,#content div.box div.message div.text h6
1304 #content div.box div.message div.text h1,#content div.box div.message div.text h2,#content div.box div.message div.text h3,#content div.box div.message div.text h4,#content div.box div.message div.text h5,#content div.box div.message div.text h6
1305 {
1305 {
1306 border: none;
1306 border: none;
1307 margin: 0;
1307 margin: 0;
1308 padding: 0;
1308 padding: 0;
1309 }
1309 }
1310
1310
1311 #content div.box div.message div.text span {
1311 #content div.box div.message div.text span {
1312 height: 1%;
1312 height: 1%;
1313 display: block;
1313 display: block;
1314 margin: 0;
1314 margin: 0;
1315 padding: 5px 0 0;
1315 padding: 5px 0 0;
1316 }
1316 }
1317
1317
1318 #content div.box div.message-error {
1318 #content div.box div.message-error {
1319 height: 1%;
1319 height: 1%;
1320 clear: both;
1320 clear: both;
1321 overflow: hidden;
1321 overflow: hidden;
1322 background: #FBE3E4;
1322 background: #FBE3E4;
1323 border: 1px solid #FBC2C4;
1323 border: 1px solid #FBC2C4;
1324 color: #860006;
1324 color: #860006;
1325 }
1325 }
1326
1326
1327 #content div.box div.message-error h6 {
1327 #content div.box div.message-error h6 {
1328 color: #860006;
1328 color: #860006;
1329 }
1329 }
1330
1330
1331 #content div.box div.message-warning {
1331 #content div.box div.message-warning {
1332 height: 1%;
1332 height: 1%;
1333 clear: both;
1333 clear: both;
1334 overflow: hidden;
1334 overflow: hidden;
1335 background: #FFF6BF;
1335 background: #FFF6BF;
1336 border: 1px solid #FFD324;
1336 border: 1px solid #FFD324;
1337 color: #5f5200;
1337 color: #5f5200;
1338 }
1338 }
1339
1339
1340 #content div.box div.message-warning h6 {
1340 #content div.box div.message-warning h6 {
1341 color: #5f5200;
1341 color: #5f5200;
1342 }
1342 }
1343
1343
1344 #content div.box div.message-notice {
1344 #content div.box div.message-notice {
1345 height: 1%;
1345 height: 1%;
1346 clear: both;
1346 clear: both;
1347 overflow: hidden;
1347 overflow: hidden;
1348 background: #8FBDE0;
1348 background: #8FBDE0;
1349 border: 1px solid #6BACDE;
1349 border: 1px solid #6BACDE;
1350 color: #003863;
1350 color: #003863;
1351 }
1351 }
1352
1352
1353 #content div.box div.message-notice h6 {
1353 #content div.box div.message-notice h6 {
1354 color: #003863;
1354 color: #003863;
1355 }
1355 }
1356
1356
1357 #content div.box div.message-success {
1357 #content div.box div.message-success {
1358 height: 1%;
1358 height: 1%;
1359 clear: both;
1359 clear: both;
1360 overflow: hidden;
1360 overflow: hidden;
1361 background: #E6EFC2;
1361 background: #E6EFC2;
1362 border: 1px solid #C6D880;
1362 border: 1px solid #C6D880;
1363 color: #4e6100;
1363 color: #4e6100;
1364 }
1364 }
1365
1365
1366 #content div.box div.message-success h6 {
1366 #content div.box div.message-success h6 {
1367 color: #4e6100;
1367 color: #4e6100;
1368 }
1368 }
1369
1369
1370 #content div.box div.form div.fields div.field {
1370 #content div.box div.form div.fields div.field {
1371 height: 1%;
1371 height: 1%;
1372 border-bottom: 1px solid #DDD;
1372 border-bottom: 1px solid #DDD;
1373 clear: both;
1373 clear: both;
1374 margin: 0;
1374 margin: 0;
1375 padding: 10px 0;
1375 padding: 10px 0;
1376 }
1376 }
1377
1377
1378 #content div.box div.form div.fields div.field-first {
1378 #content div.box div.form div.fields div.field-first {
1379 padding: 0 0 10px;
1379 padding: 0 0 10px;
1380 }
1380 }
1381
1381
1382 #content div.box div.form div.fields div.field-noborder {
1382 #content div.box div.form div.fields div.field-noborder {
1383 border-bottom: 0 !important;
1383 border-bottom: 0 !important;
1384 }
1384 }
1385
1385
1386 #content div.box div.form div.fields div.field span.error-message {
1386 #content div.box div.form div.fields div.field span.error-message {
1387 height: 1%;
1387 height: 1%;
1388 display: inline-block;
1388 display: inline-block;
1389 color: red;
1389 color: red;
1390 margin: 8px 0 0 4px;
1390 margin: 8px 0 0 4px;
1391 padding: 0;
1391 padding: 0;
1392 }
1392 }
1393
1393
1394 #content div.box div.form div.fields div.field span.success {
1394 #content div.box div.form div.fields div.field span.success {
1395 height: 1%;
1395 height: 1%;
1396 display: block;
1396 display: block;
1397 color: #316309;
1397 color: #316309;
1398 margin: 8px 0 0;
1398 margin: 8px 0 0;
1399 padding: 0;
1399 padding: 0;
1400 }
1400 }
1401
1401
1402 #content div.box div.form div.fields div.field div.label {
1402 #content div.box div.form div.fields div.field div.label {
1403 left: 70px;
1403 left: 70px;
1404 width: 155px;
1404 width: 155px;
1405 position: absolute;
1405 position: absolute;
1406 margin: 0;
1406 margin: 0;
1407 padding: 5px 0 0 0px;
1407 padding: 5px 0 0 0px;
1408 }
1408 }
1409
1409
1410 #content div.box div.form div.fields div.field div.label-summary {
1410 #content div.box div.form div.fields div.field div.label-summary {
1411 left: 30px;
1411 left: 30px;
1412 width: 155px;
1412 width: 155px;
1413 position: absolute;
1413 position: absolute;
1414 margin: 0;
1414 margin: 0;
1415 padding: 0px 0 0 0px;
1415 padding: 0px 0 0 0px;
1416 }
1416 }
1417
1417
1418 #content div.box-left div.form div.fields div.field div.label,
1418 #content div.box-left div.form div.fields div.field div.label,
1419 #content div.box-right div.form div.fields div.field div.label,
1419 #content div.box-right div.form div.fields div.field div.label,
1420 #content div.box-left div.form div.fields div.field div.label,
1420 #content div.box-left div.form div.fields div.field div.label,
1421 #content div.box-left div.form div.fields div.field div.label-summary,
1421 #content div.box-left div.form div.fields div.field div.label-summary,
1422 #content div.box-right div.form div.fields div.field div.label-summary,
1422 #content div.box-right div.form div.fields div.field div.label-summary,
1423 #content div.box-left div.form div.fields div.field div.label-summary
1423 #content div.box-left div.form div.fields div.field div.label-summary
1424 {
1424 {
1425 clear: both;
1425 clear: both;
1426 overflow: hidden;
1426 overflow: hidden;
1427 left: 0;
1427 left: 0;
1428 width: auto;
1428 width: auto;
1429 position: relative;
1429 position: relative;
1430 margin: 0;
1430 margin: 0;
1431 padding: 0 0 8px;
1431 padding: 0 0 8px;
1432 }
1432 }
1433
1433
1434 #content div.box div.form div.fields div.field div.label-select {
1434 #content div.box div.form div.fields div.field div.label-select {
1435 padding: 5px 0 0 5px;
1435 padding: 5px 0 0 5px;
1436 }
1436 }
1437
1437
1438 #content div.box-left div.form div.fields div.field div.label-select,
1438 #content div.box-left div.form div.fields div.field div.label-select,
1439 #content div.box-right div.form div.fields div.field div.label-select
1439 #content div.box-right div.form div.fields div.field div.label-select
1440 {
1440 {
1441 padding: 0 0 8px;
1441 padding: 0 0 8px;
1442 }
1442 }
1443
1443
1444 #content div.box-left div.form div.fields div.field div.label-textarea,
1444 #content div.box-left div.form div.fields div.field div.label-textarea,
1445 #content div.box-right div.form div.fields div.field div.label-textarea
1445 #content div.box-right div.form div.fields div.field div.label-textarea
1446 {
1446 {
1447 padding: 0 0 8px !important;
1447 padding: 0 0 8px !important;
1448 }
1448 }
1449
1449
1450 #content div.box div.form div.fields div.field div.label label,div.label label
1450 #content div.box div.form div.fields div.field div.label label,div.label label
1451 {
1451 {
1452 color: #393939;
1452 color: #393939;
1453 font-weight: 700;
1453 font-weight: 700;
1454 }
1454 }
1455 #content div.box div.form div.fields div.field div.label label,div.label-summary label
1455 #content div.box div.form div.fields div.field div.label label,div.label-summary label
1456 {
1456 {
1457 color: #393939;
1457 color: #393939;
1458 font-weight: 700;
1458 font-weight: 700;
1459 }
1459 }
1460 #content div.box div.form div.fields div.field div.input {
1460 #content div.box div.form div.fields div.field div.input {
1461 margin: 0 0 0 200px;
1461 margin: 0 0 0 200px;
1462 }
1462 }
1463
1463
1464 #content div.box div.form div.fields div.field div.input.summary {
1464 #content div.box div.form div.fields div.field div.input.summary {
1465 margin: 0 0 0 110px;
1465 margin: 0 0 0 110px;
1466 }
1466 }
1467 #content div.box div.form div.fields div.field div.input.summary-short {
1467 #content div.box div.form div.fields div.field div.input.summary-short {
1468 margin: 0 0 0 110px;
1468 margin: 0 0 0 110px;
1469 }
1469 }
1470 #content div.box div.form div.fields div.field div.file {
1470 #content div.box div.form div.fields div.field div.file {
1471 margin: 0 0 0 200px;
1471 margin: 0 0 0 200px;
1472 }
1472 }
1473
1473
1474 #content div.box-left div.form div.fields div.field div.input,#content div.box-right div.form div.fields div.field div.input
1474 #content div.box-left div.form div.fields div.field div.input,#content div.box-right div.form div.fields div.field div.input
1475 {
1475 {
1476 margin: 0 0 0 0px;
1476 margin: 0 0 0 0px;
1477 }
1477 }
1478
1478
1479 #content div.box div.form div.fields div.field div.input input,
1479 #content div.box div.form div.fields div.field div.input input,
1480 .reviewer_ac input {
1480 .reviewer_ac input {
1481 background: #FFF;
1481 background: #FFF;
1482 border-top: 1px solid #b3b3b3;
1482 border-top: 1px solid #b3b3b3;
1483 border-left: 1px solid #b3b3b3;
1483 border-left: 1px solid #b3b3b3;
1484 border-right: 1px solid #eaeaea;
1484 border-right: 1px solid #eaeaea;
1485 border-bottom: 1px solid #eaeaea;
1485 border-bottom: 1px solid #eaeaea;
1486 color: #000;
1486 color: #000;
1487 font-size: 11px;
1487 font-size: 11px;
1488 margin: 0;
1488 margin: 0;
1489 padding: 7px 7px 6px;
1489 padding: 7px 7px 6px;
1490 }
1490 }
1491
1491
1492 #content div.box div.form div.fields div.field div.input input#clone_url,
1492 #content div.box div.form div.fields div.field div.input input#clone_url,
1493 #content div.box div.form div.fields div.field div.input input#clone_url_id
1493 #content div.box div.form div.fields div.field div.input input#clone_url_id
1494 {
1494 {
1495 font-size: 16px;
1495 font-size: 16px;
1496 padding: 2px;
1496 padding: 2px;
1497 }
1497 }
1498
1498
1499 #content div.box div.form div.fields div.field div.file input {
1499 #content div.box div.form div.fields div.field div.file input {
1500 background: none repeat scroll 0 0 #FFFFFF;
1500 background: none repeat scroll 0 0 #FFFFFF;
1501 border-color: #B3B3B3 #EAEAEA #EAEAEA #B3B3B3;
1501 border-color: #B3B3B3 #EAEAEA #EAEAEA #B3B3B3;
1502 border-style: solid;
1502 border-style: solid;
1503 border-width: 1px;
1503 border-width: 1px;
1504 color: #000000;
1504 color: #000000;
1505 font-size: 11px;
1505 font-size: 11px;
1506 margin: 0;
1506 margin: 0;
1507 padding: 7px 7px 6px;
1507 padding: 7px 7px 6px;
1508 }
1508 }
1509
1509
1510 input.disabled {
1510 input.disabled {
1511 background-color: #F5F5F5 !important;
1511 background-color: #F5F5F5 !important;
1512 }
1512 }
1513 #content div.box div.form div.fields div.field div.input input.small {
1513 #content div.box div.form div.fields div.field div.input input.small {
1514 width: 30%;
1514 width: 30%;
1515 }
1515 }
1516
1516
1517 #content div.box div.form div.fields div.field div.input input.medium {
1517 #content div.box div.form div.fields div.field div.input input.medium {
1518 width: 55%;
1518 width: 55%;
1519 }
1519 }
1520
1520
1521 #content div.box div.form div.fields div.field div.input input.large {
1521 #content div.box div.form div.fields div.field div.input input.large {
1522 width: 85%;
1522 width: 85%;
1523 }
1523 }
1524
1524
1525 #content div.box div.form div.fields div.field div.input input.date {
1525 #content div.box div.form div.fields div.field div.input input.date {
1526 width: 177px;
1526 width: 177px;
1527 }
1527 }
1528
1528
1529 #content div.box div.form div.fields div.field div.input input.button {
1529 #content div.box div.form div.fields div.field div.input input.button {
1530 background: #D4D0C8;
1530 background: #D4D0C8;
1531 border-top: 1px solid #FFF;
1531 border-top: 1px solid #FFF;
1532 border-left: 1px solid #FFF;
1532 border-left: 1px solid #FFF;
1533 border-right: 1px solid #404040;
1533 border-right: 1px solid #404040;
1534 border-bottom: 1px solid #404040;
1534 border-bottom: 1px solid #404040;
1535 color: #000;
1535 color: #000;
1536 margin: 0;
1536 margin: 0;
1537 padding: 4px 8px;
1537 padding: 4px 8px;
1538 }
1538 }
1539
1539
1540 #content div.box div.form div.fields div.field div.textarea {
1540 #content div.box div.form div.fields div.field div.textarea {
1541 border-top: 1px solid #b3b3b3;
1541 border-top: 1px solid #b3b3b3;
1542 border-left: 1px solid #b3b3b3;
1542 border-left: 1px solid #b3b3b3;
1543 border-right: 1px solid #eaeaea;
1543 border-right: 1px solid #eaeaea;
1544 border-bottom: 1px solid #eaeaea;
1544 border-bottom: 1px solid #eaeaea;
1545 margin: 0 0 0 200px;
1545 margin: 0 0 0 200px;
1546 padding: 10px;
1546 padding: 10px;
1547 }
1547 }
1548
1548
1549 #content div.box div.form div.fields div.field div.textarea-editor {
1549 #content div.box div.form div.fields div.field div.textarea-editor {
1550 border: 1px solid #ddd;
1550 border: 1px solid #ddd;
1551 padding: 0;
1551 padding: 0;
1552 }
1552 }
1553
1553
1554 #content div.box div.form div.fields div.field div.textarea textarea {
1554 #content div.box div.form div.fields div.field div.textarea textarea {
1555 width: 100%;
1555 width: 100%;
1556 height: 220px;
1556 height: 220px;
1557 overflow: hidden;
1557 overflow: hidden;
1558 background: #FFF;
1558 background: #FFF;
1559 color: #000;
1559 color: #000;
1560 font-size: 11px;
1560 font-size: 11px;
1561 outline: none;
1561 outline: none;
1562 border-width: 0;
1562 border-width: 0;
1563 margin: 0;
1563 margin: 0;
1564 padding: 0;
1564 padding: 0;
1565 }
1565 }
1566
1566
1567 #content div.box-left div.form div.fields div.field div.textarea textarea,#content div.box-right div.form div.fields div.field div.textarea textarea
1567 #content div.box-left div.form div.fields div.field div.textarea textarea,#content div.box-right div.form div.fields div.field div.textarea textarea
1568 {
1568 {
1569 width: 100%;
1569 width: 100%;
1570 height: 100px;
1570 height: 100px;
1571 }
1571 }
1572
1572
1573 #content div.box div.form div.fields div.field div.textarea table {
1573 #content div.box div.form div.fields div.field div.textarea table {
1574 width: 100%;
1574 width: 100%;
1575 border: none;
1575 border: none;
1576 margin: 0;
1576 margin: 0;
1577 padding: 0;
1577 padding: 0;
1578 }
1578 }
1579
1579
1580 #content div.box div.form div.fields div.field div.textarea table td {
1580 #content div.box div.form div.fields div.field div.textarea table td {
1581 background: #DDD;
1581 background: #DDD;
1582 border: none;
1582 border: none;
1583 padding: 0;
1583 padding: 0;
1584 }
1584 }
1585
1585
1586 #content div.box div.form div.fields div.field div.textarea table td table
1586 #content div.box div.form div.fields div.field div.textarea table td table
1587 {
1587 {
1588 width: auto;
1588 width: auto;
1589 border: none;
1589 border: none;
1590 margin: 0;
1590 margin: 0;
1591 padding: 0;
1591 padding: 0;
1592 }
1592 }
1593
1593
1594 #content div.box div.form div.fields div.field div.textarea table td table td
1594 #content div.box div.form div.fields div.field div.textarea table td table td
1595 {
1595 {
1596 font-size: 11px;
1596 font-size: 11px;
1597 padding: 5px 5px 5px 0;
1597 padding: 5px 5px 5px 0;
1598 }
1598 }
1599
1599
1600 #content div.box div.form div.fields div.field input[type=text]:focus,
1600 #content div.box div.form div.fields div.field input[type=text]:focus,
1601 #content div.box div.form div.fields div.field input[type=password]:focus,
1601 #content div.box div.form div.fields div.field input[type=password]:focus,
1602 #content div.box div.form div.fields div.field input[type=file]:focus,
1602 #content div.box div.form div.fields div.field input[type=file]:focus,
1603 #content div.box div.form div.fields div.field textarea:focus,
1603 #content div.box div.form div.fields div.field textarea:focus,
1604 #content div.box div.form div.fields div.field select:focus,
1604 #content div.box div.form div.fields div.field select:focus,
1605 .reviewer_ac input:focus
1605 .reviewer_ac input:focus
1606 {
1606 {
1607 background: #f6f6f6;
1607 background: #f6f6f6;
1608 border-color: #666;
1608 border-color: #666;
1609 }
1609 }
1610
1610
1611 .reviewer_ac {
1611 .reviewer_ac {
1612 padding:10px
1612 padding:10px
1613 }
1613 }
1614
1614
1615 div.form div.fields div.field div.button {
1615 div.form div.fields div.field div.button {
1616 margin: 0;
1616 margin: 0;
1617 padding: 0 0 0 8px;
1617 padding: 0 0 0 8px;
1618 }
1618 }
1619 #content div.box table.noborder {
1619 #content div.box table.noborder {
1620 border: 1px solid transparent;
1620 border: 1px solid transparent;
1621 }
1621 }
1622
1622
1623 #content div.box table {
1623 #content div.box table {
1624 width: 100%;
1624 width: 100%;
1625 border-collapse: separate;
1625 border-collapse: separate;
1626 margin: 0;
1626 margin: 0;
1627 padding: 0;
1627 padding: 0;
1628 border: 1px solid #eee;
1628 border: 1px solid #eee;
1629 -webkit-border-radius: 4px;
1629 -webkit-border-radius: 4px;
1630 -moz-border-radius: 4px;
1630 -moz-border-radius: 4px;
1631 border-radius: 4px;
1631 border-radius: 4px;
1632 }
1632 }
1633
1633
1634 #content div.box table th {
1634 #content div.box table th {
1635 background: #eee;
1635 background: #eee;
1636 border-bottom: 1px solid #ddd;
1636 border-bottom: 1px solid #ddd;
1637 padding: 5px 0px 5px 5px;
1637 padding: 5px 0px 5px 5px;
1638 text-align: left;
1638 text-align: left;
1639 }
1639 }
1640
1640
1641 #content div.box table th.left {
1641 #content div.box table th.left {
1642 text-align: left;
1642 text-align: left;
1643 }
1643 }
1644
1644
1645 #content div.box table th.right {
1645 #content div.box table th.right {
1646 text-align: right;
1646 text-align: right;
1647 }
1647 }
1648
1648
1649 #content div.box table th.center {
1649 #content div.box table th.center {
1650 text-align: center;
1650 text-align: center;
1651 }
1651 }
1652
1652
1653 #content div.box table th.selected {
1653 #content div.box table th.selected {
1654 vertical-align: middle;
1654 vertical-align: middle;
1655 padding: 0;
1655 padding: 0;
1656 }
1656 }
1657
1657
1658 #content div.box table td {
1658 #content div.box table td {
1659 background: #fff;
1659 background: #fff;
1660 border-bottom: 1px solid #cdcdcd;
1660 border-bottom: 1px solid #cdcdcd;
1661 vertical-align: middle;
1661 vertical-align: middle;
1662 padding: 5px;
1662 padding: 5px;
1663 }
1663 }
1664
1664
1665 #content div.box table tr.selected td {
1665 #content div.box table tr.selected td {
1666 background: #FFC;
1666 background: #FFC;
1667 }
1667 }
1668
1668
1669 #content div.box table td.selected {
1669 #content div.box table td.selected {
1670 width: 3%;
1670 width: 3%;
1671 text-align: center;
1671 text-align: center;
1672 vertical-align: middle;
1672 vertical-align: middle;
1673 padding: 0;
1673 padding: 0;
1674 }
1674 }
1675
1675
1676 #content div.box table td.action {
1676 #content div.box table td.action {
1677 width: 45%;
1677 width: 45%;
1678 text-align: left;
1678 text-align: left;
1679 }
1679 }
1680
1680
1681 #content div.box table td.date {
1681 #content div.box table td.date {
1682 width: 33%;
1682 width: 33%;
1683 text-align: center;
1683 text-align: center;
1684 }
1684 }
1685
1685
1686 #content div.box div.action {
1686 #content div.box div.action {
1687 float: right;
1687 float: right;
1688 background: #FFF;
1688 background: #FFF;
1689 text-align: right;
1689 text-align: right;
1690 margin: 10px 0 0;
1690 margin: 10px 0 0;
1691 padding: 0;
1691 padding: 0;
1692 }
1692 }
1693
1693
1694 #content div.box div.action select {
1694 #content div.box div.action select {
1695 font-size: 11px;
1695 font-size: 11px;
1696 margin: 0;
1696 margin: 0;
1697 }
1697 }
1698
1698
1699 #content div.box div.action .ui-selectmenu {
1699 #content div.box div.action .ui-selectmenu {
1700 margin: 0;
1700 margin: 0;
1701 padding: 0;
1701 padding: 0;
1702 }
1702 }
1703
1703
1704 #content div.box div.pagination {
1704 #content div.box div.pagination {
1705 height: 1%;
1705 height: 1%;
1706 clear: both;
1706 clear: both;
1707 overflow: hidden;
1707 overflow: hidden;
1708 margin: 10px 0 0;
1708 margin: 10px 0 0;
1709 padding: 0;
1709 padding: 0;
1710 }
1710 }
1711
1711
1712 #content div.box div.pagination ul.pager {
1712 #content div.box div.pagination ul.pager {
1713 float: right;
1713 float: right;
1714 text-align: right;
1714 text-align: right;
1715 margin: 0;
1715 margin: 0;
1716 padding: 0;
1716 padding: 0;
1717 }
1717 }
1718
1718
1719 #content div.box div.pagination ul.pager li {
1719 #content div.box div.pagination ul.pager li {
1720 height: 1%;
1720 height: 1%;
1721 float: left;
1721 float: left;
1722 list-style: none;
1722 list-style: none;
1723 background: #ebebeb url("../images/pager.png") repeat-x;
1723 background: #ebebeb url("../images/pager.png") repeat-x;
1724 border-top: 1px solid #dedede;
1724 border-top: 1px solid #dedede;
1725 border-left: 1px solid #cfcfcf;
1725 border-left: 1px solid #cfcfcf;
1726 border-right: 1px solid #c4c4c4;
1726 border-right: 1px solid #c4c4c4;
1727 border-bottom: 1px solid #c4c4c4;
1727 border-bottom: 1px solid #c4c4c4;
1728 color: #4A4A4A;
1728 color: #4A4A4A;
1729 font-weight: 700;
1729 font-weight: 700;
1730 margin: 0 0 0 4px;
1730 margin: 0 0 0 4px;
1731 padding: 0;
1731 padding: 0;
1732 }
1732 }
1733
1733
1734 #content div.box div.pagination ul.pager li.separator {
1734 #content div.box div.pagination ul.pager li.separator {
1735 padding: 6px;
1735 padding: 6px;
1736 }
1736 }
1737
1737
1738 #content div.box div.pagination ul.pager li.current {
1738 #content div.box div.pagination ul.pager li.current {
1739 background: #b4b4b4 url("../images/pager_selected.png") repeat-x;
1739 background: #b4b4b4 url("../images/pager_selected.png") repeat-x;
1740 border-top: 1px solid #ccc;
1740 border-top: 1px solid #ccc;
1741 border-left: 1px solid #bebebe;
1741 border-left: 1px solid #bebebe;
1742 border-right: 1px solid #b1b1b1;
1742 border-right: 1px solid #b1b1b1;
1743 border-bottom: 1px solid #afafaf;
1743 border-bottom: 1px solid #afafaf;
1744 color: #515151;
1744 color: #515151;
1745 padding: 6px;
1745 padding: 6px;
1746 }
1746 }
1747
1747
1748 #content div.box div.pagination ul.pager li a {
1748 #content div.box div.pagination ul.pager li a {
1749 height: 1%;
1749 height: 1%;
1750 display: block;
1750 display: block;
1751 float: left;
1751 float: left;
1752 color: #515151;
1752 color: #515151;
1753 text-decoration: none;
1753 text-decoration: none;
1754 margin: 0;
1754 margin: 0;
1755 padding: 6px;
1755 padding: 6px;
1756 }
1756 }
1757
1757
1758 #content div.box div.pagination ul.pager li a:hover,#content div.box div.pagination ul.pager li a:active
1758 #content div.box div.pagination ul.pager li a:hover,#content div.box div.pagination ul.pager li a:active
1759 {
1759 {
1760 background: #b4b4b4 url("../images/pager_selected.png") repeat-x;
1760 background: #b4b4b4 url("../images/pager_selected.png") repeat-x;
1761 border-top: 1px solid #ccc;
1761 border-top: 1px solid #ccc;
1762 border-left: 1px solid #bebebe;
1762 border-left: 1px solid #bebebe;
1763 border-right: 1px solid #b1b1b1;
1763 border-right: 1px solid #b1b1b1;
1764 border-bottom: 1px solid #afafaf;
1764 border-bottom: 1px solid #afafaf;
1765 margin: -1px;
1765 margin: -1px;
1766 }
1766 }
1767
1767
1768 #content div.box div.pagination-wh {
1768 #content div.box div.pagination-wh {
1769 height: 1%;
1769 height: 1%;
1770 clear: both;
1770 clear: both;
1771 overflow: hidden;
1771 overflow: hidden;
1772 text-align: right;
1772 text-align: right;
1773 margin: 10px 0 0;
1773 margin: 10px 0 0;
1774 padding: 0;
1774 padding: 0;
1775 }
1775 }
1776
1776
1777 #content div.box div.pagination-right {
1777 #content div.box div.pagination-right {
1778 float: right;
1778 float: right;
1779 }
1779 }
1780
1780
1781 #content div.box div.pagination-wh a,
1781 #content div.box div.pagination-wh a,
1782 #content div.box div.pagination-wh span.pager_dotdot,
1782 #content div.box div.pagination-wh span.pager_dotdot,
1783 #content div.box div.pagination-wh span.yui-pg-previous,
1783 #content div.box div.pagination-wh span.yui-pg-previous,
1784 #content div.box div.pagination-wh span.yui-pg-last,
1784 #content div.box div.pagination-wh span.yui-pg-last,
1785 #content div.box div.pagination-wh span.yui-pg-next,
1785 #content div.box div.pagination-wh span.yui-pg-next,
1786 #content div.box div.pagination-wh span.yui-pg-first
1786 #content div.box div.pagination-wh span.yui-pg-first
1787 {
1787 {
1788 height: 1%;
1788 height: 1%;
1789 float: left;
1789 float: left;
1790 background: #ebebeb url("../images/pager.png") repeat-x;
1790 background: #ebebeb url("../images/pager.png") repeat-x;
1791 border-top: 1px solid #dedede;
1791 border-top: 1px solid #dedede;
1792 border-left: 1px solid #cfcfcf;
1792 border-left: 1px solid #cfcfcf;
1793 border-right: 1px solid #c4c4c4;
1793 border-right: 1px solid #c4c4c4;
1794 border-bottom: 1px solid #c4c4c4;
1794 border-bottom: 1px solid #c4c4c4;
1795 color: #4A4A4A;
1795 color: #4A4A4A;
1796 font-weight: 700;
1796 font-weight: 700;
1797 margin: 0 0 0 4px;
1797 margin: 0 0 0 4px;
1798 padding: 6px;
1798 padding: 6px;
1799 }
1799 }
1800
1800
1801 #content div.box div.pagination-wh span.pager_curpage {
1801 #content div.box div.pagination-wh span.pager_curpage {
1802 height: 1%;
1802 height: 1%;
1803 float: left;
1803 float: left;
1804 background: #b4b4b4 url("../images/pager_selected.png") repeat-x;
1804 background: #b4b4b4 url("../images/pager_selected.png") repeat-x;
1805 border-top: 1px solid #ccc;
1805 border-top: 1px solid #ccc;
1806 border-left: 1px solid #bebebe;
1806 border-left: 1px solid #bebebe;
1807 border-right: 1px solid #b1b1b1;
1807 border-right: 1px solid #b1b1b1;
1808 border-bottom: 1px solid #afafaf;
1808 border-bottom: 1px solid #afafaf;
1809 color: #515151;
1809 color: #515151;
1810 font-weight: 700;
1810 font-weight: 700;
1811 margin: 0 0 0 4px;
1811 margin: 0 0 0 4px;
1812 padding: 6px;
1812 padding: 6px;
1813 }
1813 }
1814
1814
1815 #content div.box div.pagination-wh a:hover,#content div.box div.pagination-wh a:active
1815 #content div.box div.pagination-wh a:hover,#content div.box div.pagination-wh a:active
1816 {
1816 {
1817 background: #b4b4b4 url("../images/pager_selected.png") repeat-x;
1817 background: #b4b4b4 url("../images/pager_selected.png") repeat-x;
1818 border-top: 1px solid #ccc;
1818 border-top: 1px solid #ccc;
1819 border-left: 1px solid #bebebe;
1819 border-left: 1px solid #bebebe;
1820 border-right: 1px solid #b1b1b1;
1820 border-right: 1px solid #b1b1b1;
1821 border-bottom: 1px solid #afafaf;
1821 border-bottom: 1px solid #afafaf;
1822 text-decoration: none;
1822 text-decoration: none;
1823 }
1823 }
1824
1824
1825 #content div.box div.traffic div.legend {
1825 #content div.box div.traffic div.legend {
1826 clear: both;
1826 clear: both;
1827 overflow: hidden;
1827 overflow: hidden;
1828 border-bottom: 1px solid #ddd;
1828 border-bottom: 1px solid #ddd;
1829 margin: 0 0 10px;
1829 margin: 0 0 10px;
1830 padding: 0 0 10px;
1830 padding: 0 0 10px;
1831 }
1831 }
1832
1832
1833 #content div.box div.traffic div.legend h6 {
1833 #content div.box div.traffic div.legend h6 {
1834 float: left;
1834 float: left;
1835 border: none;
1835 border: none;
1836 margin: 0;
1836 margin: 0;
1837 padding: 0;
1837 padding: 0;
1838 }
1838 }
1839
1839
1840 #content div.box div.traffic div.legend li {
1840 #content div.box div.traffic div.legend li {
1841 list-style: none;
1841 list-style: none;
1842 float: left;
1842 float: left;
1843 font-size: 11px;
1843 font-size: 11px;
1844 margin: 0;
1844 margin: 0;
1845 padding: 0 8px 0 4px;
1845 padding: 0 8px 0 4px;
1846 }
1846 }
1847
1847
1848 #content div.box div.traffic div.legend li.visits {
1848 #content div.box div.traffic div.legend li.visits {
1849 border-left: 12px solid #edc240;
1849 border-left: 12px solid #edc240;
1850 }
1850 }
1851
1851
1852 #content div.box div.traffic div.legend li.pageviews {
1852 #content div.box div.traffic div.legend li.pageviews {
1853 border-left: 12px solid #afd8f8;
1853 border-left: 12px solid #afd8f8;
1854 }
1854 }
1855
1855
1856 #content div.box div.traffic table {
1856 #content div.box div.traffic table {
1857 width: auto;
1857 width: auto;
1858 }
1858 }
1859
1859
1860 #content div.box div.traffic table td {
1860 #content div.box div.traffic table td {
1861 background: transparent;
1861 background: transparent;
1862 border: none;
1862 border: none;
1863 padding: 2px 3px 3px;
1863 padding: 2px 3px 3px;
1864 }
1864 }
1865
1865
1866 #content div.box div.traffic table td.legendLabel {
1866 #content div.box div.traffic table td.legendLabel {
1867 padding: 0 3px 2px;
1867 padding: 0 3px 2px;
1868 }
1868 }
1869
1869
1870 #summary {
1870 #summary {
1871
1871
1872 }
1872 }
1873
1873
1874 #summary .metatag {
1874 #summary .metatag {
1875 display: inline-block;
1875 display: inline-block;
1876 padding: 3px 5px;
1876 padding: 3px 5px;
1877 margin-bottom: 3px;
1877 margin-bottom: 3px;
1878 margin-right: 1px;
1878 margin-right: 1px;
1879 border-radius: 5px;
1879 border-radius: 5px;
1880 }
1880 }
1881
1881
1882 #content div.box #summary p {
1882 #content div.box #summary p {
1883 margin-bottom: -5px;
1883 margin-bottom: -5px;
1884 width: 600px;
1884 width: 600px;
1885 white-space: pre-wrap;
1885 white-space: pre-wrap;
1886 }
1886 }
1887
1887
1888 #content div.box #summary p:last-child {
1888 #content div.box #summary p:last-child {
1889 margin-bottom: 9px;
1889 margin-bottom: 9px;
1890 }
1890 }
1891
1891
1892 #content div.box #summary p:first-of-type {
1892 #content div.box #summary p:first-of-type {
1893 margin-top: 9px;
1893 margin-top: 9px;
1894 }
1894 }
1895
1895
1896 .metatag {
1896 .metatag {
1897 display: inline-block;
1897 display: inline-block;
1898 margin-right: 1px;
1898 margin-right: 1px;
1899 -webkit-border-radius: 4px 4px 4px 4px;
1899 -webkit-border-radius: 4px 4px 4px 4px;
1900 -khtml-border-radius: 4px 4px 4px 4px;
1900 -khtml-border-radius: 4px 4px 4px 4px;
1901 -moz-border-radius: 4px 4px 4px 4px;
1901 -moz-border-radius: 4px 4px 4px 4px;
1902 border-radius: 4px 4px 4px 4px;
1902 border-radius: 4px 4px 4px 4px;
1903
1903
1904 border: solid 1px #9CF;
1904 border: solid 1px #9CF;
1905 padding: 2px 3px 2px 3px !important;
1905 padding: 2px 3px 2px 3px !important;
1906 background-color: #DEF;
1906 background-color: #DEF;
1907 }
1907 }
1908
1908
1909 .metatag[tag="dead"] {
1909 .metatag[tag="dead"] {
1910 background-color: #E44;
1910 background-color: #E44;
1911 }
1911 }
1912
1912
1913 .metatag[tag="stale"] {
1913 .metatag[tag="stale"] {
1914 background-color: #EA4;
1914 background-color: #EA4;
1915 }
1915 }
1916
1916
1917 .metatag[tag="featured"] {
1917 .metatag[tag="featured"] {
1918 background-color: #AEA;
1918 background-color: #AEA;
1919 }
1919 }
1920
1920
1921 .metatag[tag="requires"] {
1921 .metatag[tag="requires"] {
1922 background-color: #9CF;
1922 background-color: #9CF;
1923 }
1923 }
1924
1924
1925 .metatag[tag="recommends"] {
1925 .metatag[tag="recommends"] {
1926 background-color: #BDF;
1926 background-color: #BDF;
1927 }
1927 }
1928
1928
1929 .metatag[tag="lang"] {
1929 .metatag[tag="lang"] {
1930 background-color: #FAF474;
1930 background-color: #FAF474;
1931 }
1931 }
1932
1932
1933 .metatag[tag="license"] {
1933 .metatag[tag="license"] {
1934 border: solid 1px #9CF;
1934 border: solid 1px #9CF;
1935 background-color: #DEF;
1935 background-color: #DEF;
1936 target-new: tab !important;
1936 target-new: tab !important;
1937 }
1937 }
1938 .metatag[tag="see"] {
1938 .metatag[tag="see"] {
1939 border: solid 1px #CBD;
1939 border: solid 1px #CBD;
1940 background-color: #EDF;
1940 background-color: #EDF;
1941 }
1941 }
1942
1942
1943 a.metatag[tag="license"]:hover {
1943 a.metatag[tag="license"]:hover {
1944 background-color: #003367;
1944 background-color: #003367;
1945 color: #FFF;
1945 color: #FFF;
1946 text-decoration: none;
1946 text-decoration: none;
1947 }
1947 }
1948
1948
1949 #summary .desc {
1949 #summary .desc {
1950 white-space: pre;
1950 white-space: pre;
1951 width: 100%;
1951 width: 100%;
1952 }
1952 }
1953
1953
1954 #summary .repo_name {
1954 #summary .repo_name {
1955 font-size: 1.6em;
1955 font-size: 1.6em;
1956 font-weight: bold;
1956 font-weight: bold;
1957 vertical-align: baseline;
1957 vertical-align: baseline;
1958 clear: right
1958 clear: right
1959 }
1959 }
1960
1960
1961 #footer {
1961 #footer {
1962 clear: both;
1962 clear: both;
1963 overflow: hidden;
1963 overflow: hidden;
1964 text-align: right;
1964 text-align: right;
1965 margin: 0;
1965 margin: 0;
1966 padding: 0 10px 4px;
1966 padding: 0 10px 4px;
1967 margin: -10px 0 0;
1967 margin: -10px 0 0;
1968 }
1968 }
1969
1969
1970 #footer div#footer-inner {
1970 #footer div#footer-inner {
1971 background-color: #003B76;
1971 background-color: #003B76;
1972 background-repeat : repeat-x;
1972 background-repeat : repeat-x;
1973 background-image : -khtml-gradient( linear, left top, left bottom, from(#003B76), to(#00376E));
1973 background-image : -khtml-gradient( linear, left top, left bottom, from(#003B76), to(#00376E));
1974 background-image : -moz-linear-gradient(top, #003b76, #00376e);
1974 background-image : -moz-linear-gradient(top, #003b76, #00376e);
1975 background-image : -ms-linear-gradient( top, #003b76, #00376e);
1975 background-image : -ms-linear-gradient( top, #003b76, #00376e);
1976 background-image : -webkit-gradient( linear, left top, left bottom, color-stop( 0%, #003b76), color-stop( 100%, #00376e));
1976 background-image : -webkit-gradient( linear, left top, left bottom, color-stop( 0%, #003b76), color-stop( 100%, #00376e));
1977 background-image : -webkit-linear-gradient( top, #003b76, #00376e));
1977 background-image : -webkit-linear-gradient( top, #003b76, #00376e));
1978 background-image : -o-linear-gradient( top, #003b76, #00376e));
1978 background-image : -o-linear-gradient( top, #003b76, #00376e));
1979 background-image : linear-gradient( top, #003b76, #00376e);
1979 background-image : linear-gradient( top, #003b76, #00376e);
1980 filter :progid : DXImageTransform.Microsoft.gradient ( startColorstr = '#003b76', endColorstr = '#00376e', GradientType = 0);
1980 filter :progid : DXImageTransform.Microsoft.gradient ( startColorstr = '#003b76', endColorstr = '#00376e', GradientType = 0);
1981 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
1981 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
1982 -webkit-border-radius: 4px 4px 4px 4px;
1982 -webkit-border-radius: 4px 4px 4px 4px;
1983 -khtml-border-radius: 4px 4px 4px 4px;
1983 -khtml-border-radius: 4px 4px 4px 4px;
1984 -moz-border-radius: 4px 4px 4px 4px;
1984 -moz-border-radius: 4px 4px 4px 4px;
1985 border-radius: 4px 4px 4px 4px;
1985 border-radius: 4px 4px 4px 4px;
1986 }
1986 }
1987
1987
1988 #footer div#footer-inner p {
1988 #footer div#footer-inner p {
1989 padding: 15px 25px 15px 0;
1989 padding: 15px 25px 15px 0;
1990 color: #FFF;
1990 color: #FFF;
1991 font-weight: 700;
1991 font-weight: 700;
1992 }
1992 }
1993
1993
1994 #footer div#footer-inner .footer-link {
1994 #footer div#footer-inner .footer-link {
1995 float: left;
1995 float: left;
1996 padding-left: 10px;
1996 padding-left: 10px;
1997 }
1997 }
1998
1998
1999 #footer div#footer-inner .footer-link a,#footer div#footer-inner .footer-link-right a
1999 #footer div#footer-inner .footer-link a,#footer div#footer-inner .footer-link-right a
2000 {
2000 {
2001 color: #FFF;
2001 color: #FFF;
2002 }
2002 }
2003
2003
2004 #login div.title {
2004 #login div.title {
2005 width: 420px;
2006 clear: both;
2005 clear: both;
2007 overflow: hidden;
2006 overflow: hidden;
2008 position: relative;
2007 position: relative;
2009 background-color: #003B76;
2008 background-color: #003B76;
2010 background-repeat : repeat-x;
2009 background-repeat : repeat-x;
2011 background-image : -khtml-gradient( linear, left top, left bottom, from(#003B76), to(#00376E));
2010 background-image : -khtml-gradient( linear, left top, left bottom, from(#003B76), to(#00376E));
2012 background-image : -moz-linear-gradient( top, #003b76, #00376e);
2011 background-image : -moz-linear-gradient( top, #003b76, #00376e);
2013 background-image : -ms-linear-gradient( top, #003b76, #00376e);
2012 background-image : -ms-linear-gradient( top, #003b76, #00376e);
2014 background-image : -webkit-gradient( linear, left top, left bottom, color-stop( 0%, #003b76), color-stop( 100%, #00376e));
2013 background-image : -webkit-gradient( linear, left top, left bottom, color-stop( 0%, #003b76), color-stop( 100%, #00376e));
2015 background-image : -webkit-linear-gradient( top, #003b76, #00376e));
2014 background-image : -webkit-linear-gradient( top, #003b76, #00376e));
2016 background-image : -o-linear-gradient( top, #003b76, #00376e));
2015 background-image : -o-linear-gradient( top, #003b76, #00376e));
2017 background-image : linear-gradient( top, #003b76, #00376e);
2016 background-image : linear-gradient( top, #003b76, #00376e);
2018 filter : progid : DXImageTransform.Microsoft.gradient ( startColorstr = '#003b76', endColorstr = '#00376e', GradientType = 0);
2017 filter : progid : DXImageTransform.Microsoft.gradient ( startColorstr = '#003b76', endColorstr = '#00376e', GradientType = 0);
2019 margin: 0 auto;
2018 margin: 0 auto;
2020 padding: 0;
2019 padding: 0;
2021 }
2020 }
2022
2021
2023 #login div.inner {
2022 #login div.inner {
2024 width: 380px;
2025 background: #FFF url("../images/login.png") no-repeat top left;
2023 background: #FFF url("../images/login.png") no-repeat top left;
2026 border-top: none;
2024 border-top: none;
2027 border-bottom: none;
2025 border-bottom: none;
2028 margin: 0 auto;
2026 margin: 0 auto;
2029 padding: 20px;
2027 padding: 20px;
2030 }
2028 }
2031
2029
2032 #login div.form div.fields div.field div.label {
2030 #login div.form div.fields div.field div.label {
2033 width: 173px;
2031 width: 173px;
2034 float: left;
2032 float: left;
2035 text-align: right;
2033 text-align: right;
2036 margin: 2px 10px 0 0;
2034 margin: 2px 10px 0 0;
2037 padding: 5px 0 0 5px;
2035 padding: 5px 0 0 5px;
2038 }
2036 }
2039
2037
2040 #login div.form div.fields div.field div.input input {
2038 #login div.form div.fields div.field div.input input {
2041 width: 176px;
2042 background: #FFF;
2039 background: #FFF;
2043 border-top: 1px solid #b3b3b3;
2040 border-top: 1px solid #b3b3b3;
2044 border-left: 1px solid #b3b3b3;
2041 border-left: 1px solid #b3b3b3;
2045 border-right: 1px solid #eaeaea;
2042 border-right: 1px solid #eaeaea;
2046 border-bottom: 1px solid #eaeaea;
2043 border-bottom: 1px solid #eaeaea;
2047 color: #000;
2044 color: #000;
2048 font-size: 11px;
2045 font-size: 11px;
2049 margin: 0;
2046 margin: 0;
2050 padding: 7px 7px 6px;
2047 padding: 7px 7px 6px;
2051 }
2048 }
2052
2049
2053 #login div.form div.fields div.buttons {
2050 #login div.form div.fields div.buttons {
2054 clear: both;
2051 clear: both;
2055 overflow: hidden;
2052 overflow: hidden;
2056 border-top: 1px solid #DDD;
2053 border-top: 1px solid #DDD;
2057 text-align: right;
2054 text-align: right;
2058 margin: 0;
2055 margin: 0;
2059 padding: 10px 0 0;
2056 padding: 10px 0 0;
2060 }
2057 }
2061
2058
2062 #login div.form div.links {
2059 #login div.form div.links {
2063 clear: both;
2060 clear: both;
2064 overflow: hidden;
2061 overflow: hidden;
2065 margin: 10px 0 0;
2062 margin: 10px 0 0;
2066 padding: 0 0 2px;
2063 padding: 0 0 2px;
2067 }
2064 }
2068
2065
2069 .user-menu{
2066 .user-menu{
2070 margin: 0px !important;
2067 margin: 0px !important;
2071 float: left;
2068 float: left;
2072 }
2069 }
2073
2070
2074 .user-menu .container{
2071 .user-menu .container{
2075 padding:0px 4px 0px 4px;
2072 padding:0px 4px 0px 4px;
2076 margin: 0px 0px 0px 0px;
2073 margin: 0px 0px 0px 0px;
2077 }
2074 }
2078
2075
2079 .user-menu .gravatar{
2076 .user-menu .gravatar{
2080 margin: 0px 0px 0px 0px;
2077 margin: 0px 0px 0px 0px;
2081 cursor: pointer;
2078 cursor: pointer;
2082 }
2079 }
2083 .user-menu .gravatar.enabled{
2080 .user-menu .gravatar.enabled{
2084 background-color: #FDF784 !important;
2081 background-color: #FDF784 !important;
2085 }
2082 }
2086 .user-menu .gravatar:hover{
2083 .user-menu .gravatar:hover{
2087 background-color: #FDF784 !important;
2084 background-color: #FDF784 !important;
2088 }
2085 }
2089 #quick_login{
2086 #quick_login{
2090 min-height: 80px;
2087 min-height: 80px;
2091 margin: 37px 0 0 -251px;
2088 margin: 37px 0 0 -251px;
2092 padding: 4px;
2089 padding: 4px;
2093 position: absolute;
2090 position: absolute;
2094 width: 278px;
2091 width: 278px;
2095 background-color: #003B76;
2092 background-color: #003B76;
2096 background-repeat: repeat-x;
2093 background-repeat: repeat-x;
2097 background-image: -khtml-gradient(linear, left top, left bottom, from(#003B76), to(#00376E) );
2094 background-image: -khtml-gradient(linear, left top, left bottom, from(#003B76), to(#00376E) );
2098 background-image: -moz-linear-gradient(top, #003b76, #00376e);
2095 background-image: -moz-linear-gradient(top, #003b76, #00376e);
2099 background-image: -ms-linear-gradient(top, #003b76, #00376e);
2096 background-image: -ms-linear-gradient(top, #003b76, #00376e);
2100 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76), color-stop(100%, #00376e) );
2097 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76), color-stop(100%, #00376e) );
2101 background-image: -webkit-linear-gradient(top, #003b76, #00376e);
2098 background-image: -webkit-linear-gradient(top, #003b76, #00376e);
2102 background-image: -o-linear-gradient(top, #003b76, #00376e);
2099 background-image: -o-linear-gradient(top, #003b76, #00376e);
2103 background-image: linear-gradient(top, #003b76, #00376e);
2100 background-image: linear-gradient(top, #003b76, #00376e);
2104 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76', endColorstr='#00376e', GradientType=0 );
2101 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76', endColorstr='#00376e', GradientType=0 );
2105
2102
2106 z-index: 999;
2103 z-index: 999;
2107 -webkit-border-radius: 0px 0px 4px 4px;
2104 -webkit-border-radius: 0px 0px 4px 4px;
2108 -khtml-border-radius: 0px 0px 4px 4px;
2105 -khtml-border-radius: 0px 0px 4px 4px;
2109 -moz-border-radius: 0px 0px 4px 4px;
2106 -moz-border-radius: 0px 0px 4px 4px;
2110 border-radius: 0px 0px 4px 4px;
2107 border-radius: 0px 0px 4px 4px;
2111 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
2108 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
2112 }
2109 }
2113 #quick_login h4{
2110 #quick_login h4{
2114 color: #fff;
2111 color: #fff;
2115 padding: 5px 0px 5px 14px;
2112 padding: 5px 0px 5px 14px;
2116 }
2113 }
2117
2114
2118 #quick_login .password_forgoten {
2115 #quick_login .password_forgoten {
2119 padding-right: 10px;
2116 padding-right: 10px;
2120 padding-top: 0px;
2117 padding-top: 0px;
2121 text-align: left;
2118 text-align: left;
2122 }
2119 }
2123
2120
2124 #quick_login .password_forgoten a {
2121 #quick_login .password_forgoten a {
2125 font-size: 10px;
2122 font-size: 10px;
2126 color: #fff;
2123 color: #fff;
2127 }
2124 }
2128
2125
2129 #quick_login .register {
2126 #quick_login .register {
2130 padding-right: 10px;
2127 padding-right: 10px;
2131 padding-top: 5px;
2128 padding-top: 5px;
2132 text-align: left;
2129 text-align: left;
2133 }
2130 }
2134
2131
2135 #quick_login .register a {
2132 #quick_login .register a {
2136 font-size: 10px;
2133 font-size: 10px;
2137 color: #fff;
2134 color: #fff;
2138 }
2135 }
2139
2136
2140 #quick_login .submit {
2137 #quick_login .submit {
2141 margin: -20px 0 0 0px;
2138 margin: -20px 0 0 0px;
2142 position: absolute;
2139 position: absolute;
2143 right: 15px;
2140 right: 15px;
2144 }
2141 }
2145
2142
2146 #quick_login .links_left{
2143 #quick_login .links_left{
2147 float: left;
2144 float: left;
2148 }
2145 }
2149 #quick_login .links_right{
2146 #quick_login .links_right{
2150 float: right;
2147 float: right;
2151 }
2148 }
2152 #quick_login .full_name{
2149 #quick_login .full_name{
2153 color: #FFFFFF;
2150 color: #FFFFFF;
2154 font-weight: bold;
2151 font-weight: bold;
2155 padding: 3px;
2152 padding: 3px;
2156 }
2153 }
2157 #quick_login .big_gravatar{
2154 #quick_login .big_gravatar{
2158 padding:4px 0px 0px 6px;
2155 padding:4px 0px 0px 6px;
2159 }
2156 }
2160 #quick_login .inbox{
2157 #quick_login .inbox{
2161 padding:4px 0px 0px 6px;
2158 padding:4px 0px 0px 6px;
2162 color: #FFFFFF;
2159 color: #FFFFFF;
2163 font-weight: bold;
2160 font-weight: bold;
2164 }
2161 }
2165 #quick_login .inbox a{
2162 #quick_login .inbox a{
2166 color: #FFFFFF;
2163 color: #FFFFFF;
2167 }
2164 }
2168 #quick_login .email,#quick_login .email a{
2165 #quick_login .email,#quick_login .email a{
2169 color: #FFFFFF;
2166 color: #FFFFFF;
2170 padding: 3px;
2167 padding: 3px;
2171
2168
2172 }
2169 }
2173 #quick_login .links .logout{
2170 #quick_login .links .logout{
2174
2171
2175 }
2172 }
2176
2173
2177 #quick_login div.form div.fields {
2174 #quick_login div.form div.fields {
2178 padding-top: 2px;
2175 padding-top: 2px;
2179 padding-left: 10px;
2176 padding-left: 10px;
2180 }
2177 }
2181
2178
2182 #quick_login div.form div.fields div.field {
2179 #quick_login div.form div.fields div.field {
2183 padding: 5px;
2180 padding: 5px;
2184 }
2181 }
2185
2182
2186 #quick_login div.form div.fields div.field div.label label {
2183 #quick_login div.form div.fields div.field div.label label {
2187 color: #fff;
2184 color: #fff;
2188 padding-bottom: 3px;
2185 padding-bottom: 3px;
2189 }
2186 }
2190
2187
2191 #quick_login div.form div.fields div.field div.input input {
2188 #quick_login div.form div.fields div.field div.input input {
2192 width: 236px;
2189 width: 236px;
2193 background: #FFF;
2190 background: #FFF;
2194 border-top: 1px solid #b3b3b3;
2191 border-top: 1px solid #b3b3b3;
2195 border-left: 1px solid #b3b3b3;
2192 border-left: 1px solid #b3b3b3;
2196 border-right: 1px solid #eaeaea;
2193 border-right: 1px solid #eaeaea;
2197 border-bottom: 1px solid #eaeaea;
2194 border-bottom: 1px solid #eaeaea;
2198 color: #000;
2195 color: #000;
2199 font-size: 11px;
2196 font-size: 11px;
2200 margin: 0;
2197 margin: 0;
2201 padding: 5px 7px 4px;
2198 padding: 5px 7px 4px;
2202 }
2199 }
2203
2200
2204 #quick_login div.form div.fields div.buttons {
2201 #quick_login div.form div.fields div.buttons {
2205 clear: both;
2202 clear: both;
2206 overflow: hidden;
2203 overflow: hidden;
2207 text-align: right;
2204 text-align: right;
2208 margin: 0;
2205 margin: 0;
2209 padding: 5px 14px 0px 5px;
2206 padding: 5px 14px 0px 5px;
2210 }
2207 }
2211
2208
2212 #quick_login div.form div.links {
2209 #quick_login div.form div.links {
2213 clear: both;
2210 clear: both;
2214 overflow: hidden;
2211 overflow: hidden;
2215 margin: 10px 0 0;
2212 margin: 10px 0 0;
2216 padding: 0 0 2px;
2213 padding: 0 0 2px;
2217 }
2214 }
2218
2215
2219 #quick_login ol.links{
2216 #quick_login ol.links{
2220 display: block;
2217 display: block;
2221 font-weight: bold;
2218 font-weight: bold;
2222 list-style: none outside none;
2219 list-style: none outside none;
2223 text-align: right;
2220 text-align: right;
2224 }
2221 }
2225 #quick_login ol.links li{
2222 #quick_login ol.links li{
2226 line-height: 27px;
2223 line-height: 27px;
2227 margin: 0;
2224 margin: 0;
2228 padding: 0;
2225 padding: 0;
2229 color: #fff;
2226 color: #fff;
2230 display: block;
2227 display: block;
2231 float:none !important;
2228 float:none !important;
2232 }
2229 }
2233
2230
2234 #quick_login ol.links li a{
2231 #quick_login ol.links li a{
2235 color: #fff;
2232 color: #fff;
2236 display: block;
2233 display: block;
2237 padding: 2px;
2234 padding: 2px;
2238 }
2235 }
2239 #quick_login ol.links li a:HOVER{
2236 #quick_login ol.links li a:HOVER{
2240 background-color: inherit !important;
2237 background-color: inherit !important;
2241 }
2238 }
2242
2239
2243 #register div.title {
2240 #register div.title {
2244 clear: both;
2241 clear: both;
2245 overflow: hidden;
2242 overflow: hidden;
2246 position: relative;
2243 position: relative;
2247 background-color: #003B76;
2244 background-color: #003B76;
2248 background-repeat: repeat-x;
2245 background-repeat: repeat-x;
2249 background-image: -khtml-gradient(linear, left top, left bottom, from(#003B76), to(#00376E) );
2246 background-image: -khtml-gradient(linear, left top, left bottom, from(#003B76), to(#00376E) );
2250 background-image: -moz-linear-gradient(top, #003b76, #00376e);
2247 background-image: -moz-linear-gradient(top, #003b76, #00376e);
2251 background-image: -ms-linear-gradient(top, #003b76, #00376e);
2248 background-image: -ms-linear-gradient(top, #003b76, #00376e);
2252 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76), color-stop(100%, #00376e) );
2249 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #003b76), color-stop(100%, #00376e) );
2253 background-image: -webkit-linear-gradient(top, #003b76, #00376e);
2250 background-image: -webkit-linear-gradient(top, #003b76, #00376e);
2254 background-image: -o-linear-gradient(top, #003b76, #00376e);
2251 background-image: -o-linear-gradient(top, #003b76, #00376e);
2255 background-image: linear-gradient(top, #003b76, #00376e);
2252 background-image: linear-gradient(top, #003b76, #00376e);
2256 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76',
2253 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#003b76',
2257 endColorstr='#00376e', GradientType=0 );
2254 endColorstr='#00376e', GradientType=0 );
2258 margin: 0 auto;
2255 margin: 0 auto;
2259 padding: 0;
2256 padding: 0;
2260 }
2257 }
2261
2258
2262 #register div.inner {
2259 #register div.inner {
2263 background: #FFF;
2260 background: #FFF;
2264 border-top: none;
2261 border-top: none;
2265 border-bottom: none;
2262 border-bottom: none;
2266 margin: 0 auto;
2263 margin: 0 auto;
2267 padding: 20px;
2264 padding: 20px;
2268 }
2265 }
2269
2266
2270 #register div.form div.fields div.field div.label {
2267 #register div.form div.fields div.field div.label {
2271 width: 135px;
2268 width: 135px;
2272 float: left;
2269 float: left;
2273 text-align: right;
2270 text-align: right;
2274 margin: 2px 10px 0 0;
2271 margin: 2px 10px 0 0;
2275 padding: 5px 0 0 5px;
2272 padding: 5px 0 0 5px;
2276 }
2273 }
2277
2274
2278 #register div.form div.fields div.field div.input input {
2275 #register div.form div.fields div.field div.input input {
2279 width: 300px;
2276 width: 300px;
2280 background: #FFF;
2277 background: #FFF;
2281 border-top: 1px solid #b3b3b3;
2278 border-top: 1px solid #b3b3b3;
2282 border-left: 1px solid #b3b3b3;
2279 border-left: 1px solid #b3b3b3;
2283 border-right: 1px solid #eaeaea;
2280 border-right: 1px solid #eaeaea;
2284 border-bottom: 1px solid #eaeaea;
2281 border-bottom: 1px solid #eaeaea;
2285 color: #000;
2282 color: #000;
2286 font-size: 11px;
2283 font-size: 11px;
2287 margin: 0;
2284 margin: 0;
2288 padding: 7px 7px 6px;
2285 padding: 7px 7px 6px;
2289 }
2286 }
2290
2287
2291 #register div.form div.fields div.buttons {
2288 #register div.form div.fields div.buttons {
2292 clear: both;
2289 clear: both;
2293 overflow: hidden;
2290 overflow: hidden;
2294 border-top: 1px solid #DDD;
2291 border-top: 1px solid #DDD;
2295 text-align: left;
2292 text-align: left;
2296 margin: 0;
2293 margin: 0;
2297 padding: 10px 0 0 150px;
2294 padding: 10px 0 0 150px;
2298 }
2295 }
2299
2296
2300 #register div.form div.activation_msg {
2297 #register div.form div.activation_msg {
2301 padding-top: 4px;
2298 padding-top: 4px;
2302 padding-bottom: 4px;
2299 padding-bottom: 4px;
2303 }
2300 }
2304
2301
2305 #journal .journal_day {
2302 #journal .journal_day {
2306 font-size: 20px;
2303 font-size: 20px;
2307 padding: 10px 0px;
2304 padding: 10px 0px;
2308 border-bottom: 2px solid #DDD;
2305 border-bottom: 2px solid #DDD;
2309 margin-left: 10px;
2306 margin-left: 10px;
2310 margin-right: 10px;
2307 margin-right: 10px;
2311 }
2308 }
2312
2309
2313 #journal .journal_container {
2310 #journal .journal_container {
2314 padding: 5px;
2311 padding: 5px;
2315 clear: both;
2312 clear: both;
2316 margin: 0px 5px 0px 10px;
2313 margin: 0px 5px 0px 10px;
2317 }
2314 }
2318
2315
2319 #journal .journal_action_container {
2316 #journal .journal_action_container {
2320 padding-left: 38px;
2317 padding-left: 38px;
2321 }
2318 }
2322
2319
2323 #journal .journal_user {
2320 #journal .journal_user {
2324 color: #747474;
2321 color: #747474;
2325 font-size: 14px;
2322 font-size: 14px;
2326 font-weight: bold;
2323 font-weight: bold;
2327 height: 30px;
2324 height: 30px;
2328 }
2325 }
2329
2326
2330 #journal .journal_user.deleted {
2327 #journal .journal_user.deleted {
2331 color: #747474;
2328 color: #747474;
2332 font-size: 14px;
2329 font-size: 14px;
2333 font-weight: normal;
2330 font-weight: normal;
2334 height: 30px;
2331 height: 30px;
2335 font-style: italic;
2332 font-style: italic;
2336 }
2333 }
2337
2334
2338
2335
2339 #journal .journal_icon {
2336 #journal .journal_icon {
2340 clear: both;
2337 clear: both;
2341 float: left;
2338 float: left;
2342 padding-right: 4px;
2339 padding-right: 4px;
2343 padding-top: 3px;
2340 padding-top: 3px;
2344 }
2341 }
2345
2342
2346 #journal .journal_action {
2343 #journal .journal_action {
2347 padding-top: 4px;
2344 padding-top: 4px;
2348 min-height: 2px;
2345 min-height: 2px;
2349 float: left
2346 float: left
2350 }
2347 }
2351
2348
2352 #journal .journal_action_params {
2349 #journal .journal_action_params {
2353 clear: left;
2350 clear: left;
2354 padding-left: 22px;
2351 padding-left: 22px;
2355 }
2352 }
2356
2353
2357 #journal .journal_repo {
2354 #journal .journal_repo {
2358 float: left;
2355 float: left;
2359 margin-left: 6px;
2356 margin-left: 6px;
2360 padding-top: 3px;
2357 padding-top: 3px;
2361 }
2358 }
2362
2359
2363 #journal .date {
2360 #journal .date {
2364 clear: both;
2361 clear: both;
2365 color: #777777;
2362 color: #777777;
2366 font-size: 11px;
2363 font-size: 11px;
2367 padding-left: 22px;
2364 padding-left: 22px;
2368 }
2365 }
2369
2366
2370 #journal .journal_repo .journal_repo_name {
2367 #journal .journal_repo .journal_repo_name {
2371 font-weight: bold;
2368 font-weight: bold;
2372 font-size: 1.1em;
2369 font-size: 1.1em;
2373 }
2370 }
2374
2371
2375 #journal .compare_view {
2372 #journal .compare_view {
2376 padding: 5px 0px 5px 0px;
2373 padding: 5px 0px 5px 0px;
2377 width: 95px;
2374 width: 95px;
2378 }
2375 }
2379
2376
2380 .journal_highlight {
2377 .journal_highlight {
2381 font-weight: bold;
2378 font-weight: bold;
2382 padding: 0 2px;
2379 padding: 0 2px;
2383 vertical-align: bottom;
2380 vertical-align: bottom;
2384 }
2381 }
2385
2382
2386 .trending_language_tbl,.trending_language_tbl td {
2383 .trending_language_tbl,.trending_language_tbl td {
2387 border: 0 !important;
2384 border: 0 !important;
2388 margin: 0 !important;
2385 margin: 0 !important;
2389 padding: 0 !important;
2386 padding: 0 !important;
2390 }
2387 }
2391
2388
2392 .trending_language_tbl,.trending_language_tbl tr {
2389 .trending_language_tbl,.trending_language_tbl tr {
2393 border-spacing: 1px;
2390 border-spacing: 1px;
2394 }
2391 }
2395
2392
2396 .trending_language {
2393 .trending_language {
2397 background-color: #003367;
2394 background-color: #003367;
2398 color: #FFF;
2395 color: #FFF;
2399 display: block;
2396 display: block;
2400 min-width: 20px;
2397 min-width: 20px;
2401 text-decoration: none;
2398 text-decoration: none;
2402 height: 12px;
2399 height: 12px;
2403 margin-bottom: 0px;
2400 margin-bottom: 0px;
2404 margin-left: 5px;
2401 margin-left: 5px;
2405 white-space: pre;
2402 white-space: pre;
2406 padding: 3px;
2403 padding: 3px;
2407 }
2404 }
2408
2405
2409 h3.files_location {
2406 h3.files_location {
2410 font-size: 1.8em;
2407 font-size: 1.8em;
2411 font-weight: 700;
2408 font-weight: 700;
2412 border-bottom: none !important;
2409 border-bottom: none !important;
2413 margin: 10px 0 !important;
2410 margin: 10px 0 !important;
2414 }
2411 }
2415
2412
2416 #files_data dl dt {
2413 #files_data dl dt {
2417 float: left;
2414 float: left;
2418 width: 60px;
2415 width: 60px;
2419 margin: 0 !important;
2416 margin: 0 !important;
2420 padding: 5px;
2417 padding: 5px;
2421 }
2418 }
2422
2419
2423 #files_data dl dd {
2420 #files_data dl dd {
2424 margin: 0 !important;
2421 margin: 0 !important;
2425 padding: 5px !important;
2422 padding: 5px !important;
2426 }
2423 }
2427
2424
2428 .file_history{
2425 .file_history{
2429 padding-top:10px;
2426 padding-top:10px;
2430 font-size:16px;
2427 font-size:16px;
2431 }
2428 }
2432 .file_author{
2429 .file_author{
2433 float: left;
2430 float: left;
2434 }
2431 }
2435
2432
2436 .file_author .item{
2433 .file_author .item{
2437 float:left;
2434 float:left;
2438 padding:5px;
2435 padding:5px;
2439 color: #888;
2436 color: #888;
2440 }
2437 }
2441
2438
2442 .tablerow0 {
2439 .tablerow0 {
2443 background-color: #F8F8F8;
2440 background-color: #F8F8F8;
2444 }
2441 }
2445
2442
2446 .tablerow1 {
2443 .tablerow1 {
2447 background-color: #FFFFFF;
2444 background-color: #FFFFFF;
2448 }
2445 }
2449
2446
2450 .changeset_id {
2447 .changeset_id {
2451 font-family: monospace;
2448 font-family: monospace;
2452 color: #666666;
2449 color: #666666;
2453 }
2450 }
2454
2451
2455 .changeset_hash {
2452 .changeset_hash {
2456 color: #000000;
2453 color: #000000;
2457 }
2454 }
2458
2455
2459 #changeset_content {
2456 #changeset_content {
2460 border-left: 1px solid #CCC;
2457 border-left: 1px solid #CCC;
2461 border-right: 1px solid #CCC;
2458 border-right: 1px solid #CCC;
2462 border-bottom: 1px solid #CCC;
2459 border-bottom: 1px solid #CCC;
2463 padding: 5px;
2460 padding: 5px;
2464 }
2461 }
2465
2462
2466 #changeset_compare_view_content {
2463 #changeset_compare_view_content {
2467 border: 1px solid #CCC;
2464 border: 1px solid #CCC;
2468 padding: 5px;
2465 padding: 5px;
2469 }
2466 }
2470
2467
2471 #changeset_content .container {
2468 #changeset_content .container {
2472 min-height: 100px;
2469 min-height: 100px;
2473 font-size: 1.2em;
2470 font-size: 1.2em;
2474 overflow: hidden;
2471 overflow: hidden;
2475 }
2472 }
2476
2473
2477 #changeset_compare_view_content .compare_view_commits {
2474 #changeset_compare_view_content .compare_view_commits {
2478 width: auto !important;
2475 width: auto !important;
2479 }
2476 }
2480
2477
2481 #changeset_compare_view_content .compare_view_commits td {
2478 #changeset_compare_view_content .compare_view_commits td {
2482 padding: 0px 0px 0px 12px !important;
2479 padding: 0px 0px 0px 12px !important;
2483 }
2480 }
2484
2481
2485 #changeset_content .container .right {
2482 #changeset_content .container .right {
2486 float: right;
2483 float: right;
2487 width: 20%;
2484 width: 20%;
2488 text-align: right;
2485 text-align: right;
2489 }
2486 }
2490
2487
2491 #changeset_content .container .left .message {
2488 #changeset_content .container .left .message {
2492 white-space: pre-wrap;
2489 white-space: pre-wrap;
2493 }
2490 }
2494 #changeset_content .container .left .message a:hover {
2491 #changeset_content .container .left .message a:hover {
2495 text-decoration: none;
2492 text-decoration: none;
2496 }
2493 }
2497 .cs_files .cur_cs {
2494 .cs_files .cur_cs {
2498 margin: 10px 2px;
2495 margin: 10px 2px;
2499 font-weight: bold;
2496 font-weight: bold;
2500 }
2497 }
2501
2498
2502 .cs_files .node {
2499 .cs_files .node {
2503 float: left;
2500 float: left;
2504 }
2501 }
2505
2502
2506 .cs_files .changes {
2503 .cs_files .changes {
2507 float: right;
2504 float: right;
2508 color:#003367;
2505 color:#003367;
2509
2506
2510 }
2507 }
2511
2508
2512 .cs_files .changes .added {
2509 .cs_files .changes .added {
2513 background-color: #BBFFBB;
2510 background-color: #BBFFBB;
2514 float: left;
2511 float: left;
2515 text-align: center;
2512 text-align: center;
2516 font-size: 9px;
2513 font-size: 9px;
2517 padding: 2px 0px 2px 0px;
2514 padding: 2px 0px 2px 0px;
2518 }
2515 }
2519
2516
2520 .cs_files .changes .deleted {
2517 .cs_files .changes .deleted {
2521 background-color: #FF8888;
2518 background-color: #FF8888;
2522 float: left;
2519 float: left;
2523 text-align: center;
2520 text-align: center;
2524 font-size: 9px;
2521 font-size: 9px;
2525 padding: 2px 0px 2px 0px;
2522 padding: 2px 0px 2px 0px;
2526 }
2523 }
2527 /*new binary*/
2524 /*new binary*/
2528 .cs_files .changes .bin1 {
2525 .cs_files .changes .bin1 {
2529 background-color: #BBFFBB;
2526 background-color: #BBFFBB;
2530 float: left;
2527 float: left;
2531 text-align: center;
2528 text-align: center;
2532 font-size: 9px;
2529 font-size: 9px;
2533 padding: 2px 0px 2px 0px;
2530 padding: 2px 0px 2px 0px;
2534 }
2531 }
2535
2532
2536 /*deleted binary*/
2533 /*deleted binary*/
2537 .cs_files .changes .bin2 {
2534 .cs_files .changes .bin2 {
2538 background-color: #FF8888;
2535 background-color: #FF8888;
2539 float: left;
2536 float: left;
2540 text-align: center;
2537 text-align: center;
2541 font-size: 9px;
2538 font-size: 9px;
2542 padding: 2px 0px 2px 0px;
2539 padding: 2px 0px 2px 0px;
2543 }
2540 }
2544
2541
2545 /*mod binary*/
2542 /*mod binary*/
2546 .cs_files .changes .bin3 {
2543 .cs_files .changes .bin3 {
2547 background-color: #DDDDDD;
2544 background-color: #DDDDDD;
2548 float: left;
2545 float: left;
2549 text-align: center;
2546 text-align: center;
2550 font-size: 9px;
2547 font-size: 9px;
2551 padding: 2px 0px 2px 0px;
2548 padding: 2px 0px 2px 0px;
2552 }
2549 }
2553
2550
2554 /*rename file*/
2551 /*rename file*/
2555 .cs_files .changes .bin4 {
2552 .cs_files .changes .bin4 {
2556 background-color: #6D99FF;
2553 background-color: #6D99FF;
2557 float: left;
2554 float: left;
2558 text-align: center;
2555 text-align: center;
2559 font-size: 9px;
2556 font-size: 9px;
2560 padding: 2px 0px 2px 0px;
2557 padding: 2px 0px 2px 0px;
2561 }
2558 }
2562
2559
2563
2560
2564 .cs_files .cs_added,.cs_files .cs_A {
2561 .cs_files .cs_added,.cs_files .cs_A {
2565 background: url("../images/icons/page_white_add.png") no-repeat scroll
2562 background: url("../images/icons/page_white_add.png") no-repeat scroll
2566 3px;
2563 3px;
2567 height: 16px;
2564 height: 16px;
2568 padding-left: 20px;
2565 padding-left: 20px;
2569 margin-top: 7px;
2566 margin-top: 7px;
2570 text-align: left;
2567 text-align: left;
2571 }
2568 }
2572
2569
2573 .cs_files .cs_changed,.cs_files .cs_M {
2570 .cs_files .cs_changed,.cs_files .cs_M {
2574 background: url("../images/icons/page_white_edit.png") no-repeat scroll
2571 background: url("../images/icons/page_white_edit.png") no-repeat scroll
2575 3px;
2572 3px;
2576 height: 16px;
2573 height: 16px;
2577 padding-left: 20px;
2574 padding-left: 20px;
2578 margin-top: 7px;
2575 margin-top: 7px;
2579 text-align: left;
2576 text-align: left;
2580 }
2577 }
2581
2578
2582 .cs_files .cs_removed,.cs_files .cs_D {
2579 .cs_files .cs_removed,.cs_files .cs_D {
2583 background: url("../images/icons/page_white_delete.png") no-repeat
2580 background: url("../images/icons/page_white_delete.png") no-repeat
2584 scroll 3px;
2581 scroll 3px;
2585 height: 16px;
2582 height: 16px;
2586 padding-left: 20px;
2583 padding-left: 20px;
2587 margin-top: 7px;
2584 margin-top: 7px;
2588 text-align: left;
2585 text-align: left;
2589 }
2586 }
2590
2587
2591 #graph {
2588 #graph {
2592 overflow: hidden;
2589 overflow: hidden;
2593 }
2590 }
2594
2591
2595 #graph_nodes {
2592 #graph_nodes {
2596 float: left;
2593 float: left;
2597 margin-right: 0px;
2594 margin-right: 0px;
2598 margin-top: 0px;
2595 margin-top: 0px;
2599 }
2596 }
2600
2597
2601 #graph_content {
2598 #graph_content {
2602 width: 80%;
2599 width: 80%;
2603 float: left;
2600 float: left;
2604 }
2601 }
2605
2602
2606 #graph_content .container_header {
2603 #graph_content .container_header {
2607 border-bottom: 1px solid #DDD;
2604 border-bottom: 1px solid #DDD;
2608 padding: 10px;
2605 padding: 10px;
2609 height: 25px;
2606 height: 25px;
2610 }
2607 }
2611
2608
2612 #graph_content #rev_range_container {
2609 #graph_content #rev_range_container {
2613 float: left;
2610 float: left;
2614 margin: 0px 0px 0px 3px;
2611 margin: 0px 0px 0px 3px;
2615 }
2612 }
2616
2613
2617 #graph_content #rev_range_clear {
2614 #graph_content #rev_range_clear {
2618 float: left;
2615 float: left;
2619 margin: 0px 0px 0px 3px;
2616 margin: 0px 0px 0px 3px;
2620 }
2617 }
2621
2618
2622 #graph_content .container {
2619 #graph_content .container {
2623 border-bottom: 1px solid #DDD;
2620 border-bottom: 1px solid #DDD;
2624 height: 56px;
2621 height: 56px;
2625 overflow: hidden;
2622 overflow: hidden;
2626 }
2623 }
2627
2624
2628 #graph_content .container .right {
2625 #graph_content .container .right {
2629 float: right;
2626 float: right;
2630 width: 23%;
2627 width: 23%;
2631 text-align: right;
2628 text-align: right;
2632 }
2629 }
2633
2630
2634 #graph_content .container .left {
2631 #graph_content .container .left {
2635 float: left;
2632 float: left;
2636 width: 25%;
2633 width: 25%;
2637 padding-left: 5px;
2634 padding-left: 5px;
2638 }
2635 }
2639
2636
2640 #graph_content .container .mid {
2637 #graph_content .container .mid {
2641 float: left;
2638 float: left;
2642 width: 49%;
2639 width: 49%;
2643 }
2640 }
2644
2641
2645
2642
2646 #graph_content .container .left .date {
2643 #graph_content .container .left .date {
2647 color: #666;
2644 color: #666;
2648 padding-left: 22px;
2645 padding-left: 22px;
2649 font-size: 10px;
2646 font-size: 10px;
2650 }
2647 }
2651
2648
2652 #graph_content .container .left .author {
2649 #graph_content .container .left .author {
2653 height: 22px;
2650 height: 22px;
2654 }
2651 }
2655
2652
2656 #graph_content .container .left .author .user {
2653 #graph_content .container .left .author .user {
2657 color: #444444;
2654 color: #444444;
2658 float: left;
2655 float: left;
2659 margin-left: -4px;
2656 margin-left: -4px;
2660 margin-top: 4px;
2657 margin-top: 4px;
2661 }
2658 }
2662
2659
2663 #graph_content .container .mid .message {
2660 #graph_content .container .mid .message {
2664 white-space: pre-wrap;
2661 white-space: pre-wrap;
2665 }
2662 }
2666
2663
2667 #graph_content .container .mid .message a:hover{
2664 #graph_content .container .mid .message a:hover{
2668 text-decoration: none;
2665 text-decoration: none;
2669 }
2666 }
2670
2667
2671 .revision-link
2668 .revision-link
2672 {
2669 {
2673 color:#3F6F9F;
2670 color:#3F6F9F;
2674 font-weight: bold !important;
2671 font-weight: bold !important;
2675 }
2672 }
2676
2673
2677 .issue-tracker-link{
2674 .issue-tracker-link{
2678 color:#3F6F9F;
2675 color:#3F6F9F;
2679 font-weight: bold !important;
2676 font-weight: bold !important;
2680 }
2677 }
2681
2678
2682 .changeset-status-container{
2679 .changeset-status-container{
2683 padding-right: 5px;
2680 padding-right: 5px;
2684 margin-top:1px;
2681 margin-top:1px;
2685 float:right;
2682 float:right;
2686 height:14px;
2683 height:14px;
2687 }
2684 }
2688 .code-header .changeset-status-container{
2685 .code-header .changeset-status-container{
2689 float:left;
2686 float:left;
2690 padding:2px 0px 0px 2px;
2687 padding:2px 0px 0px 2px;
2691 }
2688 }
2692 .changeset-status-container .changeset-status-lbl{
2689 .changeset-status-container .changeset-status-lbl{
2693 color: rgb(136, 136, 136);
2690 color: rgb(136, 136, 136);
2694 float: left;
2691 float: left;
2695 padding: 3px 4px 0px 0px
2692 padding: 3px 4px 0px 0px
2696 }
2693 }
2697 .code-header .changeset-status-container .changeset-status-lbl{
2694 .code-header .changeset-status-container .changeset-status-lbl{
2698 float: left;
2695 float: left;
2699 padding: 0px 4px 0px 0px;
2696 padding: 0px 4px 0px 0px;
2700 }
2697 }
2701 .changeset-status-container .changeset-status-ico{
2698 .changeset-status-container .changeset-status-ico{
2702 float: left;
2699 float: left;
2703 }
2700 }
2704 .code-header .changeset-status-container .changeset-status-ico, .container .changeset-status-ico{
2701 .code-header .changeset-status-container .changeset-status-ico, .container .changeset-status-ico{
2705 float: left;
2702 float: left;
2706 }
2703 }
2707 .right .comments-container{
2704 .right .comments-container{
2708 padding-right: 5px;
2705 padding-right: 5px;
2709 margin-top:1px;
2706 margin-top:1px;
2710 float:right;
2707 float:right;
2711 height:14px;
2708 height:14px;
2712 }
2709 }
2713
2710
2714 .right .comments-cnt{
2711 .right .comments-cnt{
2715 float: left;
2712 float: left;
2716 color: rgb(136, 136, 136);
2713 color: rgb(136, 136, 136);
2717 padding-right: 2px;
2714 padding-right: 2px;
2718 }
2715 }
2719
2716
2720 .right .changes{
2717 .right .changes{
2721 clear: both;
2718 clear: both;
2722 }
2719 }
2723
2720
2724 .right .changes .changed_total {
2721 .right .changes .changed_total {
2725 display: block;
2722 display: block;
2726 float: right;
2723 float: right;
2727 text-align: center;
2724 text-align: center;
2728 min-width: 45px;
2725 min-width: 45px;
2729 cursor: pointer;
2726 cursor: pointer;
2730 color: #444444;
2727 color: #444444;
2731 background: #FEA;
2728 background: #FEA;
2732 -webkit-border-radius: 0px 0px 0px 6px;
2729 -webkit-border-radius: 0px 0px 0px 6px;
2733 -moz-border-radius: 0px 0px 0px 6px;
2730 -moz-border-radius: 0px 0px 0px 6px;
2734 border-radius: 0px 0px 0px 6px;
2731 border-radius: 0px 0px 0px 6px;
2735 padding: 1px;
2732 padding: 1px;
2736 }
2733 }
2737
2734
2738 .right .changes .added,.changed,.removed {
2735 .right .changes .added,.changed,.removed {
2739 display: block;
2736 display: block;
2740 padding: 1px;
2737 padding: 1px;
2741 color: #444444;
2738 color: #444444;
2742 float: right;
2739 float: right;
2743 text-align: center;
2740 text-align: center;
2744 min-width: 15px;
2741 min-width: 15px;
2745 }
2742 }
2746
2743
2747 .right .changes .added {
2744 .right .changes .added {
2748 background: #CFC;
2745 background: #CFC;
2749 }
2746 }
2750
2747
2751 .right .changes .changed {
2748 .right .changes .changed {
2752 background: #FEA;
2749 background: #FEA;
2753 }
2750 }
2754
2751
2755 .right .changes .removed {
2752 .right .changes .removed {
2756 background: #FAA;
2753 background: #FAA;
2757 }
2754 }
2758
2755
2759 .right .merge {
2756 .right .merge {
2760 padding: 1px 3px 1px 3px;
2757 padding: 1px 3px 1px 3px;
2761 background-color: #fca062;
2758 background-color: #fca062;
2762 font-size: 10px;
2759 font-size: 10px;
2763 font-weight: bold;
2760 font-weight: bold;
2764 color: #ffffff;
2761 color: #ffffff;
2765 text-transform: uppercase;
2762 text-transform: uppercase;
2766 white-space: nowrap;
2763 white-space: nowrap;
2767 -webkit-border-radius: 3px;
2764 -webkit-border-radius: 3px;
2768 -moz-border-radius: 3px;
2765 -moz-border-radius: 3px;
2769 border-radius: 3px;
2766 border-radius: 3px;
2770 margin-right: 2px;
2767 margin-right: 2px;
2771 }
2768 }
2772
2769
2773 .right .parent {
2770 .right .parent {
2774 color: #666666;
2771 color: #666666;
2775 clear:both;
2772 clear:both;
2776 }
2773 }
2777 .right .logtags{
2774 .right .logtags{
2778 padding: 2px 2px 2px 2px;
2775 padding: 2px 2px 2px 2px;
2779 }
2776 }
2780 .right .logtags .branchtag,.right .logtags .tagtag,.right .logtags .booktag{
2777 .right .logtags .branchtag,.right .logtags .tagtag,.right .logtags .booktag{
2781 margin: 0px 2px;
2778 margin: 0px 2px;
2782 }
2779 }
2783
2780
2784 .right .logtags .branchtag,.logtags .branchtag {
2781 .right .logtags .branchtag,.logtags .branchtag {
2785 padding: 1px 3px 1px 3px;
2782 padding: 1px 3px 1px 3px;
2786 background-color: #bfbfbf;
2783 background-color: #bfbfbf;
2787 font-size: 10px;
2784 font-size: 10px;
2788 font-weight: bold;
2785 font-weight: bold;
2789 color: #ffffff;
2786 color: #ffffff;
2790 text-transform: uppercase;
2787 text-transform: uppercase;
2791 white-space: nowrap;
2788 white-space: nowrap;
2792 -webkit-border-radius: 3px;
2789 -webkit-border-radius: 3px;
2793 -moz-border-radius: 3px;
2790 -moz-border-radius: 3px;
2794 border-radius: 3px;
2791 border-radius: 3px;
2795 }
2792 }
2796 .right .logtags .branchtag a:hover,.logtags .branchtag a{
2793 .right .logtags .branchtag a:hover,.logtags .branchtag a{
2797 color: #ffffff;
2794 color: #ffffff;
2798 }
2795 }
2799 .right .logtags .branchtag a:hover,.logtags .branchtag a:hover{
2796 .right .logtags .branchtag a:hover,.logtags .branchtag a:hover{
2800 text-decoration: none;
2797 text-decoration: none;
2801 color: #ffffff;
2798 color: #ffffff;
2802 }
2799 }
2803 .right .logtags .tagtag,.logtags .tagtag {
2800 .right .logtags .tagtag,.logtags .tagtag {
2804 padding: 1px 3px 1px 3px;
2801 padding: 1px 3px 1px 3px;
2805 background-color: #62cffc;
2802 background-color: #62cffc;
2806 font-size: 10px;
2803 font-size: 10px;
2807 font-weight: bold;
2804 font-weight: bold;
2808 color: #ffffff;
2805 color: #ffffff;
2809 text-transform: uppercase;
2806 text-transform: uppercase;
2810 white-space: nowrap;
2807 white-space: nowrap;
2811 -webkit-border-radius: 3px;
2808 -webkit-border-radius: 3px;
2812 -moz-border-radius: 3px;
2809 -moz-border-radius: 3px;
2813 border-radius: 3px;
2810 border-radius: 3px;
2814 }
2811 }
2815 .right .logtags .tagtag a:hover,.logtags .tagtag a{
2812 .right .logtags .tagtag a:hover,.logtags .tagtag a{
2816 color: #ffffff;
2813 color: #ffffff;
2817 }
2814 }
2818 .right .logtags .tagtag a:hover,.logtags .tagtag a:hover{
2815 .right .logtags .tagtag a:hover,.logtags .tagtag a:hover{
2819 text-decoration: none;
2816 text-decoration: none;
2820 color: #ffffff;
2817 color: #ffffff;
2821 }
2818 }
2822 .right .logbooks .bookbook,.logbooks .bookbook,.right .logtags .bookbook,.logtags .bookbook {
2819 .right .logbooks .bookbook,.logbooks .bookbook,.right .logtags .bookbook,.logtags .bookbook {
2823 padding: 1px 3px 1px 3px;
2820 padding: 1px 3px 1px 3px;
2824 background-color: #46A546;
2821 background-color: #46A546;
2825 font-size: 10px;
2822 font-size: 10px;
2826 font-weight: bold;
2823 font-weight: bold;
2827 color: #ffffff;
2824 color: #ffffff;
2828 text-transform: uppercase;
2825 text-transform: uppercase;
2829 white-space: nowrap;
2826 white-space: nowrap;
2830 -webkit-border-radius: 3px;
2827 -webkit-border-radius: 3px;
2831 -moz-border-radius: 3px;
2828 -moz-border-radius: 3px;
2832 border-radius: 3px;
2829 border-radius: 3px;
2833 }
2830 }
2834 .right .logbooks .bookbook,.logbooks .bookbook a,.right .logtags .bookbook,.logtags .bookbook a{
2831 .right .logbooks .bookbook,.logbooks .bookbook a,.right .logtags .bookbook,.logtags .bookbook a{
2835 color: #ffffff;
2832 color: #ffffff;
2836 }
2833 }
2837 .right .logbooks .bookbook,.logbooks .bookbook a:hover,.right .logtags .bookbook,.logtags .bookbook a:hover{
2834 .right .logbooks .bookbook,.logbooks .bookbook a:hover,.right .logtags .bookbook,.logtags .bookbook a:hover{
2838 text-decoration: none;
2835 text-decoration: none;
2839 color: #ffffff;
2836 color: #ffffff;
2840 }
2837 }
2841 div.browserblock {
2838 div.browserblock {
2842 overflow: hidden;
2839 overflow: hidden;
2843 border: 1px solid #ccc;
2840 border: 1px solid #ccc;
2844 background: #f8f8f8;
2841 background: #f8f8f8;
2845 font-size: 100%;
2842 font-size: 100%;
2846 line-height: 125%;
2843 line-height: 125%;
2847 padding: 0;
2844 padding: 0;
2848 -webkit-border-radius: 6px 6px 0px 0px;
2845 -webkit-border-radius: 6px 6px 0px 0px;
2849 -moz-border-radius: 6px 6px 0px 0px;
2846 -moz-border-radius: 6px 6px 0px 0px;
2850 border-radius: 6px 6px 0px 0px;
2847 border-radius: 6px 6px 0px 0px;
2851 }
2848 }
2852
2849
2853 div.browserblock .browser-header {
2850 div.browserblock .browser-header {
2854 background: #FFF;
2851 background: #FFF;
2855 padding: 10px 0px 15px 0px;
2852 padding: 10px 0px 15px 0px;
2856 width: 100%;
2853 width: 100%;
2857 }
2854 }
2858
2855
2859 div.browserblock .browser-nav {
2856 div.browserblock .browser-nav {
2860 float: left
2857 float: left
2861 }
2858 }
2862
2859
2863 div.browserblock .browser-branch {
2860 div.browserblock .browser-branch {
2864 float: left;
2861 float: left;
2865 }
2862 }
2866
2863
2867 div.browserblock .browser-branch label {
2864 div.browserblock .browser-branch label {
2868 color: #4A4A4A;
2865 color: #4A4A4A;
2869 vertical-align: text-top;
2866 vertical-align: text-top;
2870 }
2867 }
2871
2868
2872 div.browserblock .browser-header span {
2869 div.browserblock .browser-header span {
2873 margin-left: 5px;
2870 margin-left: 5px;
2874 font-weight: 700;
2871 font-weight: 700;
2875 }
2872 }
2876
2873
2877 div.browserblock .browser-search {
2874 div.browserblock .browser-search {
2878 clear: both;
2875 clear: both;
2879 padding: 8px 8px 0px 5px;
2876 padding: 8px 8px 0px 5px;
2880 height: 20px;
2877 height: 20px;
2881 }
2878 }
2882
2879
2883 div.browserblock #node_filter_box {
2880 div.browserblock #node_filter_box {
2884
2881
2885 }
2882 }
2886
2883
2887 div.browserblock .search_activate {
2884 div.browserblock .search_activate {
2888 float: left
2885 float: left
2889 }
2886 }
2890
2887
2891 div.browserblock .add_node {
2888 div.browserblock .add_node {
2892 float: left;
2889 float: left;
2893 padding-left: 5px;
2890 padding-left: 5px;
2894 }
2891 }
2895
2892
2896 div.browserblock .search_activate a:hover,div.browserblock .add_node a:hover
2893 div.browserblock .search_activate a:hover,div.browserblock .add_node a:hover
2897 {
2894 {
2898 text-decoration: none !important;
2895 text-decoration: none !important;
2899 }
2896 }
2900
2897
2901 div.browserblock .browser-body {
2898 div.browserblock .browser-body {
2902 background: #EEE;
2899 background: #EEE;
2903 border-top: 1px solid #CCC;
2900 border-top: 1px solid #CCC;
2904 }
2901 }
2905
2902
2906 table.code-browser {
2903 table.code-browser {
2907 border-collapse: collapse;
2904 border-collapse: collapse;
2908 width: 100%;
2905 width: 100%;
2909 }
2906 }
2910
2907
2911 table.code-browser tr {
2908 table.code-browser tr {
2912 margin: 3px;
2909 margin: 3px;
2913 }
2910 }
2914
2911
2915 table.code-browser thead th {
2912 table.code-browser thead th {
2916 background-color: #EEE;
2913 background-color: #EEE;
2917 height: 20px;
2914 height: 20px;
2918 font-size: 1.1em;
2915 font-size: 1.1em;
2919 font-weight: 700;
2916 font-weight: 700;
2920 text-align: left;
2917 text-align: left;
2921 padding-left: 10px;
2918 padding-left: 10px;
2922 }
2919 }
2923
2920
2924 table.code-browser tbody td {
2921 table.code-browser tbody td {
2925 padding-left: 10px;
2922 padding-left: 10px;
2926 height: 20px;
2923 height: 20px;
2927 }
2924 }
2928
2925
2929 table.code-browser .browser-file {
2926 table.code-browser .browser-file {
2930 background: url("../images/icons/document_16.png") no-repeat scroll 3px;
2927 background: url("../images/icons/document_16.png") no-repeat scroll 3px;
2931 height: 16px;
2928 height: 16px;
2932 padding-left: 20px;
2929 padding-left: 20px;
2933 text-align: left;
2930 text-align: left;
2934 }
2931 }
2935 .diffblock .changeset_header {
2932 .diffblock .changeset_header {
2936 height: 16px;
2933 height: 16px;
2937 }
2934 }
2938 .diffblock .changeset_file {
2935 .diffblock .changeset_file {
2939 background: url("../images/icons/file.png") no-repeat scroll 3px;
2936 background: url("../images/icons/file.png") no-repeat scroll 3px;
2940 text-align: left;
2937 text-align: left;
2941 float: left;
2938 float: left;
2942 padding: 2px 0px 2px 22px;
2939 padding: 2px 0px 2px 22px;
2943 }
2940 }
2944 .diffblock .diff-menu-wrapper{
2941 .diffblock .diff-menu-wrapper{
2945 float: left;
2942 float: left;
2946 }
2943 }
2947
2944
2948 .diffblock .diff-menu{
2945 .diffblock .diff-menu{
2949 position: absolute;
2946 position: absolute;
2950 background: none repeat scroll 0 0 #FFFFFF;
2947 background: none repeat scroll 0 0 #FFFFFF;
2951 border-color: #003367 #666666 #666666;
2948 border-color: #003367 #666666 #666666;
2952 border-right: 1px solid #666666;
2949 border-right: 1px solid #666666;
2953 border-style: solid solid solid;
2950 border-style: solid solid solid;
2954 border-width: 1px;
2951 border-width: 1px;
2955 box-shadow: 2px 8px 4px rgba(0, 0, 0, 0.2);
2952 box-shadow: 2px 8px 4px rgba(0, 0, 0, 0.2);
2956 margin-top:5px;
2953 margin-top:5px;
2957 margin-left:1px;
2954 margin-left:1px;
2958
2955
2959 }
2956 }
2960 .diffblock .diff-actions {
2957 .diffblock .diff-actions {
2961 padding: 2px 0px 0px 2px;
2958 padding: 2px 0px 0px 2px;
2962 float: left;
2959 float: left;
2963 }
2960 }
2964 .diffblock .diff-menu ul li {
2961 .diffblock .diff-menu ul li {
2965 padding: 0px 0px 0px 0px !important;
2962 padding: 0px 0px 0px 0px !important;
2966 }
2963 }
2967 .diffblock .diff-menu ul li a{
2964 .diffblock .diff-menu ul li a{
2968 display: block;
2965 display: block;
2969 padding: 3px 8px 3px 8px !important;
2966 padding: 3px 8px 3px 8px !important;
2970 }
2967 }
2971 .diffblock .diff-menu ul li a:hover{
2968 .diffblock .diff-menu ul li a:hover{
2972 text-decoration: none;
2969 text-decoration: none;
2973 background-color: #EEEEEE;
2970 background-color: #EEEEEE;
2974 }
2971 }
2975 table.code-browser .browser-dir {
2972 table.code-browser .browser-dir {
2976 background: url("../images/icons/folder_16.png") no-repeat scroll 3px;
2973 background: url("../images/icons/folder_16.png") no-repeat scroll 3px;
2977 height: 16px;
2974 height: 16px;
2978 padding-left: 20px;
2975 padding-left: 20px;
2979 text-align: left;
2976 text-align: left;
2980 }
2977 }
2981
2978
2982 table.code-browser .submodule-dir {
2979 table.code-browser .submodule-dir {
2983 background: url("../images/icons/disconnect.png") no-repeat scroll 3px;
2980 background: url("../images/icons/disconnect.png") no-repeat scroll 3px;
2984 height: 16px;
2981 height: 16px;
2985 padding-left: 20px;
2982 padding-left: 20px;
2986 text-align: left;
2983 text-align: left;
2987 }
2984 }
2988
2985
2989
2986
2990 .box .search {
2987 .box .search {
2991 clear: both;
2988 clear: both;
2992 overflow: hidden;
2989 overflow: hidden;
2993 margin: 0;
2990 margin: 0;
2994 padding: 0 20px 10px;
2991 padding: 0 20px 10px;
2995 }
2992 }
2996
2993
2997 .box .search div.search_path {
2994 .box .search div.search_path {
2998 background: none repeat scroll 0 0 #EEE;
2995 background: none repeat scroll 0 0 #EEE;
2999 border: 1px solid #CCC;
2996 border: 1px solid #CCC;
3000 color: blue;
2997 color: blue;
3001 margin-bottom: 10px;
2998 margin-bottom: 10px;
3002 padding: 10px 0;
2999 padding: 10px 0;
3003 }
3000 }
3004
3001
3005 .box .search div.search_path div.link {
3002 .box .search div.search_path div.link {
3006 font-weight: 700;
3003 font-weight: 700;
3007 margin-left: 25px;
3004 margin-left: 25px;
3008 }
3005 }
3009
3006
3010 .box .search div.search_path div.link a {
3007 .box .search div.search_path div.link a {
3011 color: #003367;
3008 color: #003367;
3012 cursor: pointer;
3009 cursor: pointer;
3013 text-decoration: none;
3010 text-decoration: none;
3014 }
3011 }
3015
3012
3016 #path_unlock {
3013 #path_unlock {
3017 color: red;
3014 color: red;
3018 font-size: 1.2em;
3015 font-size: 1.2em;
3019 padding-left: 4px;
3016 padding-left: 4px;
3020 }
3017 }
3021
3018
3022 .info_box span {
3019 .info_box span {
3023 margin-left: 3px;
3020 margin-left: 3px;
3024 margin-right: 3px;
3021 margin-right: 3px;
3025 }
3022 }
3026
3023
3027 .info_box .rev {
3024 .info_box .rev {
3028 color: #003367;
3025 color: #003367;
3029 font-size: 1.6em;
3026 font-size: 1.6em;
3030 font-weight: bold;
3027 font-weight: bold;
3031 vertical-align: sub;
3028 vertical-align: sub;
3032 }
3029 }
3033
3030
3034 .info_box input#at_rev,.info_box input#size {
3031 .info_box input#at_rev,.info_box input#size {
3035 background: #FFF;
3032 background: #FFF;
3036 border-top: 1px solid #b3b3b3;
3033 border-top: 1px solid #b3b3b3;
3037 border-left: 1px solid #b3b3b3;
3034 border-left: 1px solid #b3b3b3;
3038 border-right: 1px solid #eaeaea;
3035 border-right: 1px solid #eaeaea;
3039 border-bottom: 1px solid #eaeaea;
3036 border-bottom: 1px solid #eaeaea;
3040 color: #000;
3037 color: #000;
3041 font-size: 12px;
3038 font-size: 12px;
3042 margin: 0;
3039 margin: 0;
3043 padding: 1px 5px 1px;
3040 padding: 1px 5px 1px;
3044 }
3041 }
3045
3042
3046 .info_box input#view {
3043 .info_box input#view {
3047 text-align: center;
3044 text-align: center;
3048 padding: 4px 3px 2px 2px;
3045 padding: 4px 3px 2px 2px;
3049 }
3046 }
3050
3047
3051 .yui-overlay,.yui-panel-container {
3048 .yui-overlay,.yui-panel-container {
3052 visibility: hidden;
3049 visibility: hidden;
3053 position: absolute;
3050 position: absolute;
3054 z-index: 2;
3051 z-index: 2;
3055 }
3052 }
3056
3053
3057 #tip-box {
3054 #tip-box {
3058 position: absolute;
3055 position: absolute;
3059
3056
3060 background-color: #FFF;
3057 background-color: #FFF;
3061 border: 2px solid #003367;
3058 border: 2px solid #003367;
3062 font: 100% sans-serif;
3059 font: 100% sans-serif;
3063 width: auto;
3060 width: auto;
3064 opacity: 1px;
3061 opacity: 1px;
3065 padding: 8px;
3062 padding: 8px;
3066
3063
3067 white-space: pre-wrap;
3064 white-space: pre-wrap;
3068 -webkit-border-radius: 8px 8px 8px 8px;
3065 -webkit-border-radius: 8px 8px 8px 8px;
3069 -khtml-border-radius: 8px 8px 8px 8px;
3066 -khtml-border-radius: 8px 8px 8px 8px;
3070 -moz-border-radius: 8px 8px 8px 8px;
3067 -moz-border-radius: 8px 8px 8px 8px;
3071 border-radius: 8px 8px 8px 8px;
3068 border-radius: 8px 8px 8px 8px;
3072 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
3069 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
3073 -moz-box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
3070 -moz-box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
3074 -webkit-box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
3071 -webkit-box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
3075 }
3072 }
3076
3073
3077 .hl-tip-box {
3074 .hl-tip-box {
3078 visibility: hidden;
3075 visibility: hidden;
3079 position: absolute;
3076 position: absolute;
3080 color: #666;
3077 color: #666;
3081 background-color: #FFF;
3078 background-color: #FFF;
3082 border: 2px solid #003367;
3079 border: 2px solid #003367;
3083 font: 100% sans-serif;
3080 font: 100% sans-serif;
3084 width: auto;
3081 width: auto;
3085 opacity: 1px;
3082 opacity: 1px;
3086 padding: 8px;
3083 padding: 8px;
3087 white-space: pre-wrap;
3084 white-space: pre-wrap;
3088 -webkit-border-radius: 8px 8px 8px 8px;
3085 -webkit-border-radius: 8px 8px 8px 8px;
3089 -khtml-border-radius: 8px 8px 8px 8px;
3086 -khtml-border-radius: 8px 8px 8px 8px;
3090 -moz-border-radius: 8px 8px 8px 8px;
3087 -moz-border-radius: 8px 8px 8px 8px;
3091 border-radius: 8px 8px 8px 8px;
3088 border-radius: 8px 8px 8px 8px;
3092 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
3089 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
3093 }
3090 }
3094
3091
3095
3092
3096 .mentions-container{
3093 .mentions-container{
3097 width: 90% !important;
3094 width: 90% !important;
3098 }
3095 }
3099 .mentions-container .yui-ac-content{
3096 .mentions-container .yui-ac-content{
3100 width: 100% !important;
3097 width: 100% !important;
3101 }
3098 }
3102
3099
3103 .ac {
3100 .ac {
3104 vertical-align: top;
3101 vertical-align: top;
3105 }
3102 }
3106
3103
3107 .ac .yui-ac {
3104 .ac .yui-ac {
3108 position: inherit;
3105 position: inherit;
3109 font-size: 100%;
3106 font-size: 100%;
3110 }
3107 }
3111
3108
3112 .ac .perm_ac {
3109 .ac .perm_ac {
3113 width: 20em;
3110 width: 20em;
3114 }
3111 }
3115
3112
3116 .ac .yui-ac-input {
3113 .ac .yui-ac-input {
3117 width: 100%;
3114 width: 100%;
3118 }
3115 }
3119
3116
3120 .ac .yui-ac-container {
3117 .ac .yui-ac-container {
3121 position: absolute;
3118 position: absolute;
3122 top: 1.6em;
3119 top: 1.6em;
3123 width: auto;
3120 width: auto;
3124 }
3121 }
3125
3122
3126 .ac .yui-ac-content {
3123 .ac .yui-ac-content {
3127 position: absolute;
3124 position: absolute;
3128 border: 1px solid gray;
3125 border: 1px solid gray;
3129 background: #fff;
3126 background: #fff;
3130 z-index: 9050;
3127 z-index: 9050;
3131
3128
3132 }
3129 }
3133
3130
3134 .ac .yui-ac-shadow {
3131 .ac .yui-ac-shadow {
3135 position: absolute;
3132 position: absolute;
3136 width: 100%;
3133 width: 100%;
3137 background: #000;
3134 background: #000;
3138 -moz-opacity: 0.1px;
3135 -moz-opacity: 0.1px;
3139 opacity: .10;
3136 opacity: .10;
3140 filter: alpha(opacity = 10);
3137 filter: alpha(opacity = 10);
3141 z-index: 9049;
3138 z-index: 9049;
3142 margin: .3em;
3139 margin: .3em;
3143 }
3140 }
3144
3141
3145 .ac .yui-ac-content ul {
3142 .ac .yui-ac-content ul {
3146 width: 100%;
3143 width: 100%;
3147 margin: 0;
3144 margin: 0;
3148 padding: 0;
3145 padding: 0;
3149 z-index: 9050;
3146 z-index: 9050;
3150 }
3147 }
3151
3148
3152 .ac .yui-ac-content li {
3149 .ac .yui-ac-content li {
3153 cursor: default;
3150 cursor: default;
3154 white-space: nowrap;
3151 white-space: nowrap;
3155 margin: 0;
3152 margin: 0;
3156 padding: 2px 5px;
3153 padding: 2px 5px;
3157 height: 18px;
3154 height: 18px;
3158 z-index: 9050;
3155 z-index: 9050;
3159 display: block;
3156 display: block;
3160 width: auto !important;
3157 width: auto !important;
3161 }
3158 }
3162
3159
3163 .ac .yui-ac-content li .ac-container-wrap{
3160 .ac .yui-ac-content li .ac-container-wrap{
3164 width: auto;
3161 width: auto;
3165 }
3162 }
3166
3163
3167 .ac .yui-ac-content li.yui-ac-prehighlight {
3164 .ac .yui-ac-content li.yui-ac-prehighlight {
3168 background: #B3D4FF;
3165 background: #B3D4FF;
3169 z-index: 9050;
3166 z-index: 9050;
3170 }
3167 }
3171
3168
3172 .ac .yui-ac-content li.yui-ac-highlight {
3169 .ac .yui-ac-content li.yui-ac-highlight {
3173 background: #556CB5;
3170 background: #556CB5;
3174 color: #FFF;
3171 color: #FFF;
3175 z-index: 9050;
3172 z-index: 9050;
3176 }
3173 }
3177 .ac .yui-ac-bd{
3174 .ac .yui-ac-bd{
3178 z-index: 9050;
3175 z-index: 9050;
3179 }
3176 }
3180
3177
3181 .follow {
3178 .follow {
3182 background: url("../images/icons/heart_add.png") no-repeat scroll 3px;
3179 background: url("../images/icons/heart_add.png") no-repeat scroll 3px;
3183 height: 16px;
3180 height: 16px;
3184 width: 20px;
3181 width: 20px;
3185 cursor: pointer;
3182 cursor: pointer;
3186 display: block;
3183 display: block;
3187 float: right;
3184 float: right;
3188 margin-top: 2px;
3185 margin-top: 2px;
3189 }
3186 }
3190
3187
3191 .following {
3188 .following {
3192 background: url("../images/icons/heart_delete.png") no-repeat scroll 3px;
3189 background: url("../images/icons/heart_delete.png") no-repeat scroll 3px;
3193 height: 16px;
3190 height: 16px;
3194 width: 20px;
3191 width: 20px;
3195 cursor: pointer;
3192 cursor: pointer;
3196 display: block;
3193 display: block;
3197 float: right;
3194 float: right;
3198 margin-top: 2px;
3195 margin-top: 2px;
3199 }
3196 }
3200
3197
3201 .locking_locked{
3198 .locking_locked{
3202 background: #FFF url("../images/icons/block_16.png") no-repeat scroll 3px;
3199 background: #FFF url("../images/icons/block_16.png") no-repeat scroll 3px;
3203 height: 16px;
3200 height: 16px;
3204 width: 20px;
3201 width: 20px;
3205 cursor: pointer;
3202 cursor: pointer;
3206 display: block;
3203 display: block;
3207 float: right;
3204 float: right;
3208 margin-top: 2px;
3205 margin-top: 2px;
3209 }
3206 }
3210
3207
3211 .locking_unlocked{
3208 .locking_unlocked{
3212 background: #FFF url("../images/icons/accept.png") no-repeat scroll 3px;
3209 background: #FFF url("../images/icons/accept.png") no-repeat scroll 3px;
3213 height: 16px;
3210 height: 16px;
3214 width: 20px;
3211 width: 20px;
3215 cursor: pointer;
3212 cursor: pointer;
3216 display: block;
3213 display: block;
3217 float: right;
3214 float: right;
3218 margin-top: 2px;
3215 margin-top: 2px;
3219 }
3216 }
3220
3217
3221 .currently_following {
3218 .currently_following {
3222 padding-left: 10px;
3219 padding-left: 10px;
3223 padding-bottom: 5px;
3220 padding-bottom: 5px;
3224 }
3221 }
3225
3222
3226 .add_icon {
3223 .add_icon {
3227 background: url("../images/icons/add.png") no-repeat scroll 3px;
3224 background: url("../images/icons/add.png") no-repeat scroll 3px;
3228 padding-left: 20px;
3225 padding-left: 20px;
3229 padding-top: 0px;
3226 padding-top: 0px;
3230 text-align: left;
3227 text-align: left;
3231 }
3228 }
3232
3229
3233 .accept_icon {
3230 .accept_icon {
3234 background: url("../images/icons/accept.png") no-repeat scroll 3px;
3231 background: url("../images/icons/accept.png") no-repeat scroll 3px;
3235 padding-left: 20px;
3232 padding-left: 20px;
3236 padding-top: 0px;
3233 padding-top: 0px;
3237 text-align: left;
3234 text-align: left;
3238 }
3235 }
3239
3236
3240 .edit_icon {
3237 .edit_icon {
3241 background: url("../images/icons/folder_edit.png") no-repeat scroll 3px;
3238 background: url("../images/icons/folder_edit.png") no-repeat scroll 3px;
3242 padding-left: 20px;
3239 padding-left: 20px;
3243 padding-top: 0px;
3240 padding-top: 0px;
3244 text-align: left;
3241 text-align: left;
3245 }
3242 }
3246
3243
3247 .delete_icon {
3244 .delete_icon {
3248 background: url("../images/icons/delete.png") no-repeat scroll 3px;
3245 background: url("../images/icons/delete.png") no-repeat scroll 3px;
3249 padding-left: 20px;
3246 padding-left: 20px;
3250 padding-top: 0px;
3247 padding-top: 0px;
3251 text-align: left;
3248 text-align: left;
3252 }
3249 }
3253
3250
3254 .refresh_icon {
3251 .refresh_icon {
3255 background: url("../images/icons/arrow_refresh.png") no-repeat scroll
3252 background: url("../images/icons/arrow_refresh.png") no-repeat scroll
3256 3px;
3253 3px;
3257 padding-left: 20px;
3254 padding-left: 20px;
3258 padding-top: 0px;
3255 padding-top: 0px;
3259 text-align: left;
3256 text-align: left;
3260 }
3257 }
3261
3258
3262 .pull_icon {
3259 .pull_icon {
3263 background: url("../images/icons/connect.png") no-repeat scroll 3px;
3260 background: url("../images/icons/connect.png") no-repeat scroll 3px;
3264 padding-left: 20px;
3261 padding-left: 20px;
3265 padding-top: 0px;
3262 padding-top: 0px;
3266 text-align: left;
3263 text-align: left;
3267 }
3264 }
3268
3265
3269 .rss_icon {
3266 .rss_icon {
3270 background: url("../images/icons/rss_16.png") no-repeat scroll 3px;
3267 background: url("../images/icons/rss_16.png") no-repeat scroll 3px;
3271 padding-left: 20px;
3268 padding-left: 20px;
3272 padding-top: 4px;
3269 padding-top: 4px;
3273 text-align: left;
3270 text-align: left;
3274 font-size: 8px
3271 font-size: 8px
3275 }
3272 }
3276
3273
3277 .atom_icon {
3274 .atom_icon {
3278 background: url("../images/icons/atom.png") no-repeat scroll 3px;
3275 background: url("../images/icons/atom.png") no-repeat scroll 3px;
3279 padding-left: 20px;
3276 padding-left: 20px;
3280 padding-top: 4px;
3277 padding-top: 4px;
3281 text-align: left;
3278 text-align: left;
3282 font-size: 8px
3279 font-size: 8px
3283 }
3280 }
3284
3281
3285 .archive_icon {
3282 .archive_icon {
3286 background: url("../images/icons/compress.png") no-repeat scroll 3px;
3283 background: url("../images/icons/compress.png") no-repeat scroll 3px;
3287 padding-left: 20px;
3284 padding-left: 20px;
3288 text-align: left;
3285 text-align: left;
3289 padding-top: 1px;
3286 padding-top: 1px;
3290 }
3287 }
3291
3288
3292 .start_following_icon {
3289 .start_following_icon {
3293 background: url("../images/icons/heart_add.png") no-repeat scroll 3px;
3290 background: url("../images/icons/heart_add.png") no-repeat scroll 3px;
3294 padding-left: 20px;
3291 padding-left: 20px;
3295 text-align: left;
3292 text-align: left;
3296 padding-top: 0px;
3293 padding-top: 0px;
3297 }
3294 }
3298
3295
3299 .stop_following_icon {
3296 .stop_following_icon {
3300 background: url("../images/icons/heart_delete.png") no-repeat scroll 3px;
3297 background: url("../images/icons/heart_delete.png") no-repeat scroll 3px;
3301 padding-left: 20px;
3298 padding-left: 20px;
3302 text-align: left;
3299 text-align: left;
3303 padding-top: 0px;
3300 padding-top: 0px;
3304 }
3301 }
3305
3302
3306 .action_button {
3303 .action_button {
3307 border: 0;
3304 border: 0;
3308 display: inline;
3305 display: inline;
3309 }
3306 }
3310
3307
3311 .action_button:hover {
3308 .action_button:hover {
3312 border: 0;
3309 border: 0;
3313 text-decoration: underline;
3310 text-decoration: underline;
3314 cursor: pointer;
3311 cursor: pointer;
3315 }
3312 }
3316
3313
3317 #switch_repos {
3314 #switch_repos {
3318 position: absolute;
3315 position: absolute;
3319 height: 25px;
3316 height: 25px;
3320 z-index: 1;
3317 z-index: 1;
3321 }
3318 }
3322
3319
3323 #switch_repos select {
3320 #switch_repos select {
3324 min-width: 150px;
3321 min-width: 150px;
3325 max-height: 250px;
3322 max-height: 250px;
3326 z-index: 1;
3323 z-index: 1;
3327 }
3324 }
3328
3325
3329 .breadcrumbs {
3326 .breadcrumbs {
3330 border: medium none;
3327 border: medium none;
3331 color: #FFF;
3328 color: #FFF;
3332 float: left;
3329 float: left;
3333 text-transform: uppercase;
3330 text-transform: uppercase;
3334 font-weight: 700;
3331 font-weight: 700;
3335 font-size: 14px;
3332 font-size: 14px;
3336 margin: 0;
3333 margin: 0;
3337 padding: 11px 0 11px 10px;
3334 padding: 11px 0 11px 10px;
3338 }
3335 }
3339
3336
3340 .breadcrumbs .hash {
3337 .breadcrumbs .hash {
3341 text-transform: none;
3338 text-transform: none;
3342 color: #fff;
3339 color: #fff;
3343 }
3340 }
3344
3341
3345 .breadcrumbs a {
3342 .breadcrumbs a {
3346 color: #FFF;
3343 color: #FFF;
3347 }
3344 }
3348
3345
3349 .flash_msg {
3346 .flash_msg {
3350
3347
3351 }
3348 }
3352
3349
3353 .flash_msg ul {
3350 .flash_msg ul {
3354
3351
3355 }
3352 }
3356
3353
3357 .error_red {
3354 .error_red {
3358 color:red;
3355 color:red;
3359 }
3356 }
3360
3357
3361 .error_msg {
3358 .error_msg {
3362 background-color: #c43c35;
3359 background-color: #c43c35;
3363 background-repeat: repeat-x;
3360 background-repeat: repeat-x;
3364 background-image: -khtml-gradient(linear, left top, left bottom, from(#ee5f5b), to(#c43c35) );
3361 background-image: -khtml-gradient(linear, left top, left bottom, from(#ee5f5b), to(#c43c35) );
3365 background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35);
3362 background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35);
3366 background-image: -ms-linear-gradient(top, #ee5f5b, #c43c35);
3363 background-image: -ms-linear-gradient(top, #ee5f5b, #c43c35);
3367 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #ee5f5b), color-stop(100%, #c43c35) );
3364 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #ee5f5b), color-stop(100%, #c43c35) );
3368 background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35);
3365 background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35);
3369 background-image: -o-linear-gradient(top, #ee5f5b, #c43c35);
3366 background-image: -o-linear-gradient(top, #ee5f5b, #c43c35);
3370 background-image: linear-gradient(top, #ee5f5b, #c43c35);
3367 background-image: linear-gradient(top, #ee5f5b, #c43c35);
3371 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b',endColorstr='#c43c35', GradientType=0 );
3368 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b',endColorstr='#c43c35', GradientType=0 );
3372 border-color: #c43c35 #c43c35 #882a25;
3369 border-color: #c43c35 #c43c35 #882a25;
3373 }
3370 }
3374
3371
3375 .warning_msg {
3372 .warning_msg {
3376 color: #404040 !important;
3373 color: #404040 !important;
3377 background-color: #eedc94;
3374 background-color: #eedc94;
3378 background-repeat: repeat-x;
3375 background-repeat: repeat-x;
3379 background-image: -khtml-gradient(linear, left top, left bottom, from(#fceec1), to(#eedc94) );
3376 background-image: -khtml-gradient(linear, left top, left bottom, from(#fceec1), to(#eedc94) );
3380 background-image: -moz-linear-gradient(top, #fceec1, #eedc94);
3377 background-image: -moz-linear-gradient(top, #fceec1, #eedc94);
3381 background-image: -ms-linear-gradient(top, #fceec1, #eedc94);
3378 background-image: -ms-linear-gradient(top, #fceec1, #eedc94);
3382 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #fceec1), color-stop(100%, #eedc94) );
3379 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #fceec1), color-stop(100%, #eedc94) );
3383 background-image: -webkit-linear-gradient(top, #fceec1, #eedc94);
3380 background-image: -webkit-linear-gradient(top, #fceec1, #eedc94);
3384 background-image: -o-linear-gradient(top, #fceec1, #eedc94);
3381 background-image: -o-linear-gradient(top, #fceec1, #eedc94);
3385 background-image: linear-gradient(top, #fceec1, #eedc94);
3382 background-image: linear-gradient(top, #fceec1, #eedc94);
3386 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fceec1', endColorstr='#eedc94', GradientType=0 );
3383 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fceec1', endColorstr='#eedc94', GradientType=0 );
3387 border-color: #eedc94 #eedc94 #e4c652;
3384 border-color: #eedc94 #eedc94 #e4c652;
3388 }
3385 }
3389
3386
3390 .success_msg {
3387 .success_msg {
3391 background-color: #57a957;
3388 background-color: #57a957;
3392 background-repeat: repeat-x !important;
3389 background-repeat: repeat-x !important;
3393 background-image: -khtml-gradient(linear, left top, left bottom, from(#62c462), to(#57a957) );
3390 background-image: -khtml-gradient(linear, left top, left bottom, from(#62c462), to(#57a957) );
3394 background-image: -moz-linear-gradient(top, #62c462, #57a957);
3391 background-image: -moz-linear-gradient(top, #62c462, #57a957);
3395 background-image: -ms-linear-gradient(top, #62c462, #57a957);
3392 background-image: -ms-linear-gradient(top, #62c462, #57a957);
3396 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #62c462), color-stop(100%, #57a957) );
3393 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #62c462), color-stop(100%, #57a957) );
3397 background-image: -webkit-linear-gradient(top, #62c462, #57a957);
3394 background-image: -webkit-linear-gradient(top, #62c462, #57a957);
3398 background-image: -o-linear-gradient(top, #62c462, #57a957);
3395 background-image: -o-linear-gradient(top, #62c462, #57a957);
3399 background-image: linear-gradient(top, #62c462, #57a957);
3396 background-image: linear-gradient(top, #62c462, #57a957);
3400 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#57a957', GradientType=0 );
3397 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#57a957', GradientType=0 );
3401 border-color: #57a957 #57a957 #3d773d;
3398 border-color: #57a957 #57a957 #3d773d;
3402 }
3399 }
3403
3400
3404 .notice_msg {
3401 .notice_msg {
3405 background-color: #339bb9;
3402 background-color: #339bb9;
3406 background-repeat: repeat-x;
3403 background-repeat: repeat-x;
3407 background-image: -khtml-gradient(linear, left top, left bottom, from(#5bc0de), to(#339bb9) );
3404 background-image: -khtml-gradient(linear, left top, left bottom, from(#5bc0de), to(#339bb9) );
3408 background-image: -moz-linear-gradient(top, #5bc0de, #339bb9);
3405 background-image: -moz-linear-gradient(top, #5bc0de, #339bb9);
3409 background-image: -ms-linear-gradient(top, #5bc0de, #339bb9);
3406 background-image: -ms-linear-gradient(top, #5bc0de, #339bb9);
3410 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #5bc0de), color-stop(100%, #339bb9) );
3407 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #5bc0de), color-stop(100%, #339bb9) );
3411 background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9);
3408 background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9);
3412 background-image: -o-linear-gradient(top, #5bc0de, #339bb9);
3409 background-image: -o-linear-gradient(top, #5bc0de, #339bb9);
3413 background-image: linear-gradient(top, #5bc0de, #339bb9);
3410 background-image: linear-gradient(top, #5bc0de, #339bb9);
3414 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#339bb9', GradientType=0 );
3411 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#339bb9', GradientType=0 );
3415 border-color: #339bb9 #339bb9 #22697d;
3412 border-color: #339bb9 #339bb9 #22697d;
3416 }
3413 }
3417
3414
3418 .success_msg,.error_msg,.notice_msg,.warning_msg {
3415 .success_msg,.error_msg,.notice_msg,.warning_msg {
3419 font-size: 12px;
3416 font-size: 12px;
3420 font-weight: 700;
3417 font-weight: 700;
3421 min-height: 14px;
3418 min-height: 14px;
3422 line-height: 14px;
3419 line-height: 14px;
3423 margin-bottom: 10px;
3420 margin-bottom: 10px;
3424 margin-top: 0;
3421 margin-top: 0;
3425 display: block;
3422 display: block;
3426 overflow: auto;
3423 overflow: auto;
3427 padding: 6px 10px 6px 10px;
3424 padding: 6px 10px 6px 10px;
3428 border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
3425 border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
3429 position: relative;
3426 position: relative;
3430 color: #FFF;
3427 color: #FFF;
3431 border-width: 1px;
3428 border-width: 1px;
3432 border-style: solid;
3429 border-style: solid;
3433 -webkit-border-radius: 4px;
3430 -webkit-border-radius: 4px;
3434 -moz-border-radius: 4px;
3431 -moz-border-radius: 4px;
3435 border-radius: 4px;
3432 border-radius: 4px;
3436 -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
3433 -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
3437 -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
3434 -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
3438 box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
3435 box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
3439 }
3436 }
3440
3437
3441 #msg_close {
3438 #msg_close {
3442 background: transparent url("../icons/cross_grey_small.png") no-repeat scroll 0 0;
3439 background: transparent url("../icons/cross_grey_small.png") no-repeat scroll 0 0;
3443 cursor: pointer;
3440 cursor: pointer;
3444 height: 16px;
3441 height: 16px;
3445 position: absolute;
3442 position: absolute;
3446 right: 5px;
3443 right: 5px;
3447 top: 5px;
3444 top: 5px;
3448 width: 16px;
3445 width: 16px;
3449 }
3446 }
3450 div#legend_data{
3447 div#legend_data{
3451 padding-left:10px;
3448 padding-left:10px;
3452 }
3449 }
3453 div#legend_container table{
3450 div#legend_container table{
3454 border: none !important;
3451 border: none !important;
3455 }
3452 }
3456 div#legend_container table,div#legend_choices table {
3453 div#legend_container table,div#legend_choices table {
3457 width: auto !important;
3454 width: auto !important;
3458 }
3455 }
3459
3456
3460 table#permissions_manage {
3457 table#permissions_manage {
3461 width: 0 !important;
3458 width: 0 !important;
3462 }
3459 }
3463
3460
3464 table#permissions_manage span.private_repo_msg {
3461 table#permissions_manage span.private_repo_msg {
3465 font-size: 0.8em;
3462 font-size: 0.8em;
3466 opacity: 0.6px;
3463 opacity: 0.6px;
3467 }
3464 }
3468
3465
3469 table#permissions_manage td.private_repo_msg {
3466 table#permissions_manage td.private_repo_msg {
3470 font-size: 0.8em;
3467 font-size: 0.8em;
3471 }
3468 }
3472
3469
3473 table#permissions_manage tr#add_perm_input td {
3470 table#permissions_manage tr#add_perm_input td {
3474 vertical-align: middle;
3471 vertical-align: middle;
3475 }
3472 }
3476
3473
3477 div.gravatar {
3474 div.gravatar {
3478 background-color: #FFF;
3475 background-color: #FFF;
3479 float: left;
3476 float: left;
3480 margin-right: 0.7em;
3477 margin-right: 0.7em;
3481 padding: 1px 1px 1px 1px;
3478 padding: 1px 1px 1px 1px;
3482 line-height:0;
3479 line-height:0;
3483 -webkit-border-radius: 3px;
3480 -webkit-border-radius: 3px;
3484 -khtml-border-radius: 3px;
3481 -khtml-border-radius: 3px;
3485 -moz-border-radius: 3px;
3482 -moz-border-radius: 3px;
3486 border-radius: 3px;
3483 border-radius: 3px;
3487 }
3484 }
3488
3485
3489 div.gravatar img {
3486 div.gravatar img {
3490 -webkit-border-radius: 2px;
3487 -webkit-border-radius: 2px;
3491 -khtml-border-radius: 2px;
3488 -khtml-border-radius: 2px;
3492 -moz-border-radius: 2px;
3489 -moz-border-radius: 2px;
3493 border-radius: 2px;
3490 border-radius: 2px;
3494 }
3491 }
3495
3492
3496 #header,#content,#footer {
3493 #header,#content,#footer {
3497 min-width: 978px;
3494 min-width: 978px;
3498 }
3495 }
3499
3496
3500 #content {
3497 #content {
3501 clear: both;
3498 clear: both;
3502 overflow: hidden;
3499 overflow: hidden;
3503 padding: 54px 10px 14px 10px;
3500 padding: 54px 10px 14px 10px;
3504 }
3501 }
3505
3502
3506 #content div.box div.title div.search {
3503 #content div.box div.title div.search {
3507
3504
3508 border-left: 1px solid #316293;
3505 border-left: 1px solid #316293;
3509 }
3506 }
3510
3507
3511 #content div.box div.title div.search div.input input {
3508 #content div.box div.title div.search div.input input {
3512 border: 1px solid #316293;
3509 border: 1px solid #316293;
3513 }
3510 }
3514
3511
3515 .ui-btn{
3512 .ui-btn{
3516 color: #515151;
3513 color: #515151;
3517 background-color: #DADADA;
3514 background-color: #DADADA;
3518 background-repeat: repeat-x;
3515 background-repeat: repeat-x;
3519 background-image: -khtml-gradient(linear, left top, left bottom, from(#F4F4F4),to(#DADADA) );
3516 background-image: -khtml-gradient(linear, left top, left bottom, from(#F4F4F4),to(#DADADA) );
3520 background-image: -moz-linear-gradient(top, #F4F4F4, #DADADA);
3517 background-image: -moz-linear-gradient(top, #F4F4F4, #DADADA);
3521 background-image: -ms-linear-gradient(top, #F4F4F4, #DADADA);
3518 background-image: -ms-linear-gradient(top, #F4F4F4, #DADADA);
3522 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #F4F4F4),color-stop(100%, #DADADA) );
3519 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #F4F4F4),color-stop(100%, #DADADA) );
3523 background-image: -webkit-linear-gradient(top, #F4F4F4, #DADADA) );
3520 background-image: -webkit-linear-gradient(top, #F4F4F4, #DADADA) );
3524 background-image: -o-linear-gradient(top, #F4F4F4, #DADADA) );
3521 background-image: -o-linear-gradient(top, #F4F4F4, #DADADA) );
3525 background-image: linear-gradient(top, #F4F4F4, #DADADA);
3522 background-image: linear-gradient(top, #F4F4F4, #DADADA);
3526 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#F4F4F4', endColorstr='#DADADA', GradientType=0);
3523 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#F4F4F4', endColorstr='#DADADA', GradientType=0);
3527
3524
3528 border-top: 1px solid #DDD;
3525 border-top: 1px solid #DDD;
3529 border-left: 1px solid #c6c6c6;
3526 border-left: 1px solid #c6c6c6;
3530 border-right: 1px solid #DDD;
3527 border-right: 1px solid #DDD;
3531 border-bottom: 1px solid #c6c6c6;
3528 border-bottom: 1px solid #c6c6c6;
3532 color: #515151;
3529 color: #515151;
3533 outline: none;
3530 outline: none;
3534 margin: 0px 3px 3px 0px;
3531 margin: 0px 3px 3px 0px;
3535 -webkit-border-radius: 4px 4px 4px 4px !important;
3532 -webkit-border-radius: 4px 4px 4px 4px !important;
3536 -khtml-border-radius: 4px 4px 4px 4px !important;
3533 -khtml-border-radius: 4px 4px 4px 4px !important;
3537 -moz-border-radius: 4px 4px 4px 4px !important;
3534 -moz-border-radius: 4px 4px 4px 4px !important;
3538 border-radius: 4px 4px 4px 4px !important;
3535 border-radius: 4px 4px 4px 4px !important;
3539 cursor: pointer !important;
3536 cursor: pointer !important;
3540 padding: 3px 3px 3px 3px;
3537 padding: 3px 3px 3px 3px;
3541 background-position: 0 -15px;
3538 background-position: 0 -15px;
3542
3539
3543 }
3540 }
3544 .ui-btn.xsmall{
3541 .ui-btn.xsmall{
3545 padding: 1px 2px 1px 1px;
3542 padding: 1px 2px 1px 1px;
3546 }
3543 }
3547
3544
3548 .ui-btn.large{
3545 .ui-btn.large{
3549 padding: 6px 12px;
3546 padding: 6px 12px;
3550 }
3547 }
3551
3548
3552 .ui-btn.clone{
3549 .ui-btn.clone{
3553 padding: 5px 2px 6px 1px;
3550 padding: 5px 2px 6px 1px;
3554 margin: 0px -4px 3px 0px;
3551 margin: 0px -4px 3px 0px;
3555 -webkit-border-radius: 4px 0px 0px 4px !important;
3552 -webkit-border-radius: 4px 0px 0px 4px !important;
3556 -khtml-border-radius: 4px 0px 0px 4px !important;
3553 -khtml-border-radius: 4px 0px 0px 4px !important;
3557 -moz-border-radius: 4px 0px 0px 4px !important;
3554 -moz-border-radius: 4px 0px 0px 4px !important;
3558 border-radius: 4px 0px 0px 4px !important;
3555 border-radius: 4px 0px 0px 4px !important;
3559 width: 100px;
3556 width: 100px;
3560 text-align: center;
3557 text-align: center;
3561 float: left;
3558 float: left;
3562 position: absolute;
3559 position: absolute;
3563 }
3560 }
3564 .ui-btn:focus {
3561 .ui-btn:focus {
3565 outline: none;
3562 outline: none;
3566 }
3563 }
3567 .ui-btn:hover{
3564 .ui-btn:hover{
3568 background-position: 0 0px;
3565 background-position: 0 0px;
3569 text-decoration: none;
3566 text-decoration: none;
3570 color: #515151;
3567 color: #515151;
3571 box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25), 0 0 3px #FFFFFF !important;
3568 box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25), 0 0 3px #FFFFFF !important;
3572 }
3569 }
3573
3570
3574 .ui-btn.red{
3571 .ui-btn.red{
3575 color:#fff;
3572 color:#fff;
3576 background-color: #c43c35;
3573 background-color: #c43c35;
3577 background-repeat: repeat-x;
3574 background-repeat: repeat-x;
3578 background-image: -khtml-gradient(linear, left top, left bottom, from(#ee5f5b), to(#c43c35));
3575 background-image: -khtml-gradient(linear, left top, left bottom, from(#ee5f5b), to(#c43c35));
3579 background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35);
3576 background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35);
3580 background-image: -ms-linear-gradient(top, #ee5f5b, #c43c35);
3577 background-image: -ms-linear-gradient(top, #ee5f5b, #c43c35);
3581 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #ee5f5b), color-stop(100%, #c43c35));
3578 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #ee5f5b), color-stop(100%, #c43c35));
3582 background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35);
3579 background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35);
3583 background-image: -o-linear-gradient(top, #ee5f5b, #c43c35);
3580 background-image: -o-linear-gradient(top, #ee5f5b, #c43c35);
3584 background-image: linear-gradient(top, #ee5f5b, #c43c35);
3581 background-image: linear-gradient(top, #ee5f5b, #c43c35);
3585 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#c43c35', GradientType=0);
3582 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#c43c35', GradientType=0);
3586 border-color: #c43c35 #c43c35 #882a25;
3583 border-color: #c43c35 #c43c35 #882a25;
3587 border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
3584 border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
3588 }
3585 }
3589
3586
3590
3587
3591 .ui-btn.blue{
3588 .ui-btn.blue{
3592 color:#fff;
3589 color:#fff;
3593 background-color: #339bb9;
3590 background-color: #339bb9;
3594 background-repeat: repeat-x;
3591 background-repeat: repeat-x;
3595 background-image: -khtml-gradient(linear, left top, left bottom, from(#5bc0de), to(#339bb9));
3592 background-image: -khtml-gradient(linear, left top, left bottom, from(#5bc0de), to(#339bb9));
3596 background-image: -moz-linear-gradient(top, #5bc0de, #339bb9);
3593 background-image: -moz-linear-gradient(top, #5bc0de, #339bb9);
3597 background-image: -ms-linear-gradient(top, #5bc0de, #339bb9);
3594 background-image: -ms-linear-gradient(top, #5bc0de, #339bb9);
3598 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #5bc0de), color-stop(100%, #339bb9));
3595 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #5bc0de), color-stop(100%, #339bb9));
3599 background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9);
3596 background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9);
3600 background-image: -o-linear-gradient(top, #5bc0de, #339bb9);
3597 background-image: -o-linear-gradient(top, #5bc0de, #339bb9);
3601 background-image: linear-gradient(top, #5bc0de, #339bb9);
3598 background-image: linear-gradient(top, #5bc0de, #339bb9);
3602 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#339bb9', GradientType=0);
3599 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#339bb9', GradientType=0);
3603 border-color: #339bb9 #339bb9 #22697d;
3600 border-color: #339bb9 #339bb9 #22697d;
3604 border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
3601 border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
3605 }
3602 }
3606
3603
3607 .ui-btn.green{
3604 .ui-btn.green{
3608 background-color: #57a957;
3605 background-color: #57a957;
3609 background-repeat: repeat-x;
3606 background-repeat: repeat-x;
3610 background-image: -khtml-gradient(linear, left top, left bottom, from(#62c462), to(#57a957));
3607 background-image: -khtml-gradient(linear, left top, left bottom, from(#62c462), to(#57a957));
3611 background-image: -moz-linear-gradient(top, #62c462, #57a957);
3608 background-image: -moz-linear-gradient(top, #62c462, #57a957);
3612 background-image: -ms-linear-gradient(top, #62c462, #57a957);
3609 background-image: -ms-linear-gradient(top, #62c462, #57a957);
3613 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #62c462), color-stop(100%, #57a957));
3610 background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #62c462), color-stop(100%, #57a957));
3614 background-image: -webkit-linear-gradient(top, #62c462, #57a957);
3611 background-image: -webkit-linear-gradient(top, #62c462, #57a957);
3615 background-image: -o-linear-gradient(top, #62c462, #57a957);
3612 background-image: -o-linear-gradient(top, #62c462, #57a957);
3616 background-image: linear-gradient(top, #62c462, #57a957);
3613 background-image: linear-gradient(top, #62c462, #57a957);
3617 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#57a957', GradientType=0);
3614 filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#57a957', GradientType=0);
3618 border-color: #57a957 #57a957 #3d773d;
3615 border-color: #57a957 #57a957 #3d773d;
3619 border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
3616 border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
3620 }
3617 }
3621
3618
3622 .ui-btn.blue.hidden{
3619 .ui-btn.blue.hidden{
3623 display: none;
3620 display: none;
3624 }
3621 }
3625
3622
3626 .ui-btn.active{
3623 .ui-btn.active{
3627 font-weight: bold;
3624 font-weight: bold;
3628 }
3625 }
3629
3626
3630 ins,div.options a:hover {
3627 ins,div.options a:hover {
3631 text-decoration: none;
3628 text-decoration: none;
3632 }
3629 }
3633
3630
3634 img,
3631 img,
3635 #header #header-inner #quick li a:hover span.normal,
3632 #header #header-inner #quick li a:hover span.normal,
3636 #header #header-inner #quick li ul li.last,
3633 #header #header-inner #quick li ul li.last,
3637 #content div.box div.form div.fields div.field div.textarea table td table td a,
3634 #content div.box div.form div.fields div.field div.textarea table td table td a,
3638 #clone_url,
3635 #clone_url,
3639 #clone_url_id
3636 #clone_url_id
3640 {
3637 {
3641 border: none;
3638 border: none;
3642 }
3639 }
3643
3640
3644 img.icon,.right .merge img {
3641 img.icon,.right .merge img {
3645 vertical-align: bottom;
3642 vertical-align: bottom;
3646 }
3643 }
3647
3644
3648 #header ul#logged-user,#content div.box div.title ul.links,
3645 #header ul#logged-user,#content div.box div.title ul.links,
3649 #content div.box div.message div.dismiss,
3646 #content div.box div.message div.dismiss,
3650 #content div.box div.traffic div.legend ul
3647 #content div.box div.traffic div.legend ul
3651 {
3648 {
3652 float: right;
3649 float: right;
3653 margin: 0;
3650 margin: 0;
3654 padding: 0;
3651 padding: 0;
3655 }
3652 }
3656
3653
3657 #header #header-inner #home,#header #header-inner #logo,
3654 #header #header-inner #home,#header #header-inner #logo,
3658 #content div.box ul.left,#content div.box ol.left,
3655 #content div.box ul.left,#content div.box ol.left,
3659 #content div.box div.pagination-left,div#commit_history,
3656 #content div.box div.pagination-left,div#commit_history,
3660 div#legend_data,div#legend_container,div#legend_choices
3657 div#legend_data,div#legend_container,div#legend_choices
3661 {
3658 {
3662 float: left;
3659 float: left;
3663 }
3660 }
3664
3661
3665 #header #header-inner #quick li:hover ul ul,
3662 #header #header-inner #quick li:hover ul ul,
3666 #header #header-inner #quick li:hover ul ul ul,
3663 #header #header-inner #quick li:hover ul ul ul,
3667 #header #header-inner #quick li:hover ul ul ul ul,
3664 #header #header-inner #quick li:hover ul ul ul ul,
3668 #content #left #menu ul.closed,#content #left #menu li ul.collapsed,.yui-tt-shadow
3665 #content #left #menu ul.closed,#content #left #menu li ul.collapsed,.yui-tt-shadow
3669 {
3666 {
3670 display: none;
3667 display: none;
3671 }
3668 }
3672
3669
3673 #header #header-inner #quick li:hover ul,#header #header-inner #quick li li:hover ul,#header #header-inner #quick li li li:hover ul,#header #header-inner #quick li li li li:hover ul,#content #left #menu ul.opened,#content #left #menu li ul.expanded
3670 #header #header-inner #quick li:hover ul,#header #header-inner #quick li li:hover ul,#header #header-inner #quick li li li:hover ul,#header #header-inner #quick li li li li:hover ul,#content #left #menu ul.opened,#content #left #menu li ul.expanded
3674 {
3671 {
3675 display: block;
3672 display: block;
3676 }
3673 }
3677
3674
3678 #content div.graph {
3675 #content div.graph {
3679 padding: 0 10px 10px;
3676 padding: 0 10px 10px;
3680 }
3677 }
3681
3678
3682 #content div.box div.title ul.links li a:hover,#content div.box div.title ul.links li.ui-tabs-selected a
3679 #content div.box div.title ul.links li a:hover,#content div.box div.title ul.links li.ui-tabs-selected a
3683 {
3680 {
3684 color: #bfe3ff;
3681 color: #bfe3ff;
3685 }
3682 }
3686
3683
3687 #content div.box ol.lower-roman,#content div.box ol.upper-roman,#content div.box ol.lower-alpha,#content div.box ol.upper-alpha,#content div.box ol.decimal
3684 #content div.box ol.lower-roman,#content div.box ol.upper-roman,#content div.box ol.lower-alpha,#content div.box ol.upper-alpha,#content div.box ol.decimal
3688 {
3685 {
3689 margin: 10px 24px 10px 44px;
3686 margin: 10px 24px 10px 44px;
3690 }
3687 }
3691
3688
3692 #content div.box div.form,#content div.box div.table,#content div.box div.traffic
3689 #content div.box div.form,#content div.box div.table,#content div.box div.traffic
3693 {
3690 {
3694 clear: both;
3691 clear: both;
3695 overflow: hidden;
3692 overflow: hidden;
3696 margin: 0;
3693 margin: 0;
3697 padding: 0 20px 10px;
3694 padding: 0 20px 10px;
3698 }
3695 }
3699
3696
3700 #content div.box div.form div.fields,#login div.form,#login div.form div.fields,#register div.form,#register div.form div.fields
3697 #content div.box div.form div.fields,#login div.form,#login div.form div.fields,#register div.form,#register div.form div.fields
3701 {
3698 {
3702 clear: both;
3699 clear: both;
3703 overflow: hidden;
3700 overflow: hidden;
3704 margin: 0;
3701 margin: 0;
3705 padding: 0;
3702 padding: 0;
3706 }
3703 }
3707
3704
3708 #content div.box div.form div.fields div.field div.label span,#login div.form div.fields div.field div.label span,#register div.form div.fields div.field div.label span
3705 #content div.box div.form div.fields div.field div.label span,#login div.form div.fields div.field div.label span,#register div.form div.fields div.field div.label span
3709 {
3706 {
3710 height: 1%;
3707 height: 1%;
3711 display: block;
3708 display: block;
3712 color: #363636;
3709 color: #363636;
3713 margin: 0;
3710 margin: 0;
3714 padding: 2px 0 0;
3711 padding: 2px 0 0;
3715 }
3712 }
3716
3713
3717 #content div.box div.form div.fields div.field div.input input.error,#login div.form div.fields div.field div.input input.error,#register div.form div.fields div.field div.input input.error
3714 #content div.box div.form div.fields div.field div.input input.error,#login div.form div.fields div.field div.input input.error,#register div.form div.fields div.field div.input input.error
3718 {
3715 {
3719 background: #FBE3E4;
3716 background: #FBE3E4;
3720 border-top: 1px solid #e1b2b3;
3717 border-top: 1px solid #e1b2b3;
3721 border-left: 1px solid #e1b2b3;
3718 border-left: 1px solid #e1b2b3;
3722 border-right: 1px solid #FBC2C4;
3719 border-right: 1px solid #FBC2C4;
3723 border-bottom: 1px solid #FBC2C4;
3720 border-bottom: 1px solid #FBC2C4;
3724 }
3721 }
3725
3722
3726 #content div.box div.form div.fields div.field div.input input.success,#login div.form div.fields div.field div.input input.success,#register div.form div.fields div.field div.input input.success
3723 #content div.box div.form div.fields div.field div.input input.success,#login div.form div.fields div.field div.input input.success,#register div.form div.fields div.field div.input input.success
3727 {
3724 {
3728 background: #E6EFC2;
3725 background: #E6EFC2;
3729 border-top: 1px solid #cebb98;
3726 border-top: 1px solid #cebb98;
3730 border-left: 1px solid #cebb98;
3727 border-left: 1px solid #cebb98;
3731 border-right: 1px solid #c6d880;
3728 border-right: 1px solid #c6d880;
3732 border-bottom: 1px solid #c6d880;
3729 border-bottom: 1px solid #c6d880;
3733 }
3730 }
3734
3731
3735 #content div.box-left div.form div.fields div.field div.textarea,#content div.box-right div.form div.fields div.field div.textarea,#content div.box div.form div.fields div.field div.select select,#content div.box table th.selected input,#content div.box table td.selected input
3732 #content div.box-left div.form div.fields div.field div.textarea,#content div.box-right div.form div.fields div.field div.textarea,#content div.box div.form div.fields div.field div.select select,#content div.box table th.selected input,#content div.box table td.selected input
3736 {
3733 {
3737 margin: 0;
3734 margin: 0;
3738 }
3735 }
3739
3736
3740 #content div.box-left div.form div.fields div.field div.select,#content div.box-left div.form div.fields div.field div.checkboxes,#content div.box-left div.form div.fields div.field div.radios,#content div.box-right div.form div.fields div.field div.select,#content div.box-right div.form div.fields div.field div.checkboxes,#content div.box-right div.form div.fields div.field div.radios
3737 #content div.box-left div.form div.fields div.field div.select,#content div.box-left div.form div.fields div.field div.checkboxes,#content div.box-left div.form div.fields div.field div.radios,#content div.box-right div.form div.fields div.field div.select,#content div.box-right div.form div.fields div.field div.checkboxes,#content div.box-right div.form div.fields div.field div.radios
3741 {
3738 {
3742 margin: 0 0 0 0px !important;
3739 margin: 0 0 0 0px !important;
3743 padding: 0;
3740 padding: 0;
3744 }
3741 }
3745
3742
3746 #content div.box div.form div.fields div.field div.select,#content div.box div.form div.fields div.field div.checkboxes,#content div.box div.form div.fields div.field div.radios
3743 #content div.box div.form div.fields div.field div.select,#content div.box div.form div.fields div.field div.checkboxes,#content div.box div.form div.fields div.field div.radios
3747 {
3744 {
3748 margin: 0 0 0 200px;
3745 margin: 0 0 0 200px;
3749 padding: 0;
3746 padding: 0;
3750 }
3747 }
3751
3748
3752 #content div.box div.form div.fields div.field div.select a:hover,#content div.box div.form div.fields div.field div.select a.ui-selectmenu:hover,#content div.box div.action a:hover
3749 #content div.box div.form div.fields div.field div.select a:hover,#content div.box div.form div.fields div.field div.select a.ui-selectmenu:hover,#content div.box div.action a:hover
3753 {
3750 {
3754 color: #000;
3751 color: #000;
3755 text-decoration: none;
3752 text-decoration: none;
3756 }
3753 }
3757
3754
3758 #content div.box div.form div.fields div.field div.select a.ui-selectmenu-focus,#content div.box div.action a.ui-selectmenu-focus
3755 #content div.box div.form div.fields div.field div.select a.ui-selectmenu-focus,#content div.box div.action a.ui-selectmenu-focus
3759 {
3756 {
3760 border: 1px solid #666;
3757 border: 1px solid #666;
3761 }
3758 }
3762
3759
3763 #content div.box div.form div.fields div.field div.checkboxes div.checkbox,#content div.box div.form div.fields div.field div.radios div.radio
3760 #content div.box div.form div.fields div.field div.checkboxes div.checkbox,#content div.box div.form div.fields div.field div.radios div.radio
3764 {
3761 {
3765 clear: both;
3762 clear: both;
3766 overflow: hidden;
3763 overflow: hidden;
3767 margin: 0;
3764 margin: 0;
3768 padding: 8px 0 2px;
3765 padding: 8px 0 2px;
3769 }
3766 }
3770
3767
3771 #content div.box div.form div.fields div.field div.checkboxes div.checkbox input,#content div.box div.form div.fields div.field div.radios div.radio input
3768 #content div.box div.form div.fields div.field div.checkboxes div.checkbox input,#content div.box div.form div.fields div.field div.radios div.radio input
3772 {
3769 {
3773 float: left;
3770 float: left;
3774 margin: 0;
3771 margin: 0;
3775 }
3772 }
3776
3773
3777 #content div.box div.form div.fields div.field div.checkboxes div.checkbox label,#content div.box div.form div.fields div.field div.radios div.radio label
3774 #content div.box div.form div.fields div.field div.checkboxes div.checkbox label,#content div.box div.form div.fields div.field div.radios div.radio label
3778 {
3775 {
3779 height: 1%;
3776 height: 1%;
3780 display: block;
3777 display: block;
3781 float: left;
3778 float: left;
3782 margin: 2px 0 0 4px;
3779 margin: 2px 0 0 4px;
3783 }
3780 }
3784
3781
3785 div.form div.fields div.field div.button input,
3782 div.form div.fields div.field div.button input,
3786 #content div.box div.form div.fields div.buttons input
3783 #content div.box div.form div.fields div.buttons input
3787 div.form div.fields div.buttons input,
3784 div.form div.fields div.buttons input,
3788 #content div.box div.action div.button input {
3785 #content div.box div.action div.button input {
3789 /*color: #000;*/
3786 /*color: #000;*/
3790 font-size: 11px;
3787 font-size: 11px;
3791 font-weight: 700;
3788 font-weight: 700;
3792 margin: 0;
3789 margin: 0;
3793 }
3790 }
3794
3791
3795 input.ui-button {
3792 input.ui-button {
3796 background: #e5e3e3 url("../images/button.png") repeat-x;
3793 background: #e5e3e3 url("../images/button.png") repeat-x;
3797 border-top: 1px solid #DDD;
3794 border-top: 1px solid #DDD;
3798 border-left: 1px solid #c6c6c6;
3795 border-left: 1px solid #c6c6c6;
3799 border-right: 1px solid #DDD;
3796 border-right: 1px solid #DDD;
3800 border-bottom: 1px solid #c6c6c6;
3797 border-bottom: 1px solid #c6c6c6;
3801 color: #515151 !important;
3798 color: #515151 !important;
3802 outline: none;
3799 outline: none;
3803 margin: 0;
3800 margin: 0;
3804 padding: 6px 12px;
3801 padding: 6px 12px;
3805 -webkit-border-radius: 4px 4px 4px 4px;
3802 -webkit-border-radius: 4px 4px 4px 4px;
3806 -khtml-border-radius: 4px 4px 4px 4px;
3803 -khtml-border-radius: 4px 4px 4px 4px;
3807 -moz-border-radius: 4px 4px 4px 4px;
3804 -moz-border-radius: 4px 4px 4px 4px;
3808 border-radius: 4px 4px 4px 4px;
3805 border-radius: 4px 4px 4px 4px;
3809 box-shadow: 0 1px 0 #ececec;
3806 box-shadow: 0 1px 0 #ececec;
3810 cursor: pointer;
3807 cursor: pointer;
3811 }
3808 }
3812
3809
3813 input.ui-button:hover {
3810 input.ui-button:hover {
3814 background: #b4b4b4 url("../images/button_selected.png") repeat-x;
3811 background: #b4b4b4 url("../images/button_selected.png") repeat-x;
3815 border-top: 1px solid #ccc;
3812 border-top: 1px solid #ccc;
3816 border-left: 1px solid #bebebe;
3813 border-left: 1px solid #bebebe;
3817 border-right: 1px solid #b1b1b1;
3814 border-right: 1px solid #b1b1b1;
3818 border-bottom: 1px solid #afafaf;
3815 border-bottom: 1px solid #afafaf;
3819 }
3816 }
3820
3817
3821 div.form div.fields div.field div.highlight,#content div.box div.form div.fields div.buttons div.highlight
3818 div.form div.fields div.field div.highlight,#content div.box div.form div.fields div.buttons div.highlight
3822 {
3819 {
3823 display: inline;
3820 display: inline;
3824 }
3821 }
3825
3822
3826 #content div.box div.form div.fields div.buttons,div.form div.fields div.buttons
3823 #content div.box div.form div.fields div.buttons,div.form div.fields div.buttons
3827 {
3824 {
3828 margin: 10px 0 0 200px;
3825 margin: 10px 0 0 200px;
3829 padding: 0;
3826 padding: 0;
3830 }
3827 }
3831
3828
3832 #content div.box-left div.form div.fields div.buttons,#content div.box-right div.form div.fields div.buttons,div.box-left div.form div.fields div.buttons,div.box-right div.form div.fields div.buttons
3829 #content div.box-left div.form div.fields div.buttons,#content div.box-right div.form div.fields div.buttons,div.box-left div.form div.fields div.buttons,div.box-right div.form div.fields div.buttons
3833 {
3830 {
3834 margin: 10px 0 0;
3831 margin: 10px 0 0;
3835 }
3832 }
3836
3833
3837 #content div.box table td.user,#content div.box table td.address {
3834 #content div.box table td.user,#content div.box table td.address {
3838 width: 10%;
3835 width: 10%;
3839 text-align: center;
3836 text-align: center;
3840 }
3837 }
3841
3838
3842 #content div.box div.action div.button,#login div.form div.fields div.field div.input div.link,#register div.form div.fields div.field div.input div.link
3839 #content div.box div.action div.button,#login div.form div.fields div.field div.input div.link,#register div.form div.fields div.field div.input div.link
3843 {
3840 {
3844 text-align: right;
3841 text-align: right;
3845 margin: 6px 0 0;
3842 margin: 6px 0 0;
3846 padding: 0;
3843 padding: 0;
3847 }
3844 }
3848
3845
3849 #content div.box div.action div.button input.ui-state-hover,#login div.form div.fields div.buttons input.ui-state-hover,#register div.form div.fields div.buttons input.ui-state-hover
3846 #content div.box div.action div.button input.ui-state-hover,#login div.form div.fields div.buttons input.ui-state-hover,#register div.form div.fields div.buttons input.ui-state-hover
3850 {
3847 {
3851 background: #b4b4b4 url("../images/button_selected.png") repeat-x;
3848 background: #b4b4b4 url("../images/button_selected.png") repeat-x;
3852 border-top: 1px solid #ccc;
3849 border-top: 1px solid #ccc;
3853 border-left: 1px solid #bebebe;
3850 border-left: 1px solid #bebebe;
3854 border-right: 1px solid #b1b1b1;
3851 border-right: 1px solid #b1b1b1;
3855 border-bottom: 1px solid #afafaf;
3852 border-bottom: 1px solid #afafaf;
3856 color: #515151;
3853 color: #515151;
3857 margin: 0;
3854 margin: 0;
3858 padding: 6px 12px;
3855 padding: 6px 12px;
3859 }
3856 }
3860
3857
3861 #content div.box div.pagination div.results,#content div.box div.pagination-wh div.results
3858 #content div.box div.pagination div.results,#content div.box div.pagination-wh div.results
3862 {
3859 {
3863 text-align: left;
3860 text-align: left;
3864 float: left;
3861 float: left;
3865 margin: 0;
3862 margin: 0;
3866 padding: 0;
3863 padding: 0;
3867 }
3864 }
3868
3865
3869 #content div.box div.pagination div.results span,#content div.box div.pagination-wh div.results span
3866 #content div.box div.pagination div.results span,#content div.box div.pagination-wh div.results span
3870 {
3867 {
3871 height: 1%;
3868 height: 1%;
3872 display: block;
3869 display: block;
3873 float: left;
3870 float: left;
3874 background: #ebebeb url("../images/pager.png") repeat-x;
3871 background: #ebebeb url("../images/pager.png") repeat-x;
3875 border-top: 1px solid #dedede;
3872 border-top: 1px solid #dedede;
3876 border-left: 1px solid #cfcfcf;
3873 border-left: 1px solid #cfcfcf;
3877 border-right: 1px solid #c4c4c4;
3874 border-right: 1px solid #c4c4c4;
3878 border-bottom: 1px solid #c4c4c4;
3875 border-bottom: 1px solid #c4c4c4;
3879 color: #4A4A4A;
3876 color: #4A4A4A;
3880 font-weight: 700;
3877 font-weight: 700;
3881 margin: 0;
3878 margin: 0;
3882 padding: 6px 8px;
3879 padding: 6px 8px;
3883 }
3880 }
3884
3881
3885 #content div.box div.pagination ul.pager li.disabled,#content div.box div.pagination-wh a.disabled
3882 #content div.box div.pagination ul.pager li.disabled,#content div.box div.pagination-wh a.disabled
3886 {
3883 {
3887 color: #B4B4B4;
3884 color: #B4B4B4;
3888 padding: 6px;
3885 padding: 6px;
3889 }
3886 }
3890
3887
3891 #login,#register {
3888 #login,#register {
3892 width: 520px;
3889 width: 520px;
3893 margin: 10% auto 0;
3890 margin: 10% auto 0;
3894 padding: 0;
3891 padding: 0;
3895 }
3892 }
3896
3893
3897 #login div.color,#register div.color {
3894 #login div.color,#register div.color {
3898 clear: both;
3895 clear: both;
3899 overflow: hidden;
3896 overflow: hidden;
3900 background: #FFF;
3897 background: #FFF;
3901 margin: 10px auto 0;
3898 margin: 10px auto 0;
3902 padding: 3px 3px 3px 0;
3899 padding: 3px 3px 3px 0;
3903 }
3900 }
3904
3901
3905 #login div.color a,#register div.color a {
3902 #login div.color a,#register div.color a {
3906 width: 20px;
3903 width: 20px;
3907 height: 20px;
3904 height: 20px;
3908 display: block;
3905 display: block;
3909 float: left;
3906 float: left;
3910 margin: 0 0 0 3px;
3907 margin: 0 0 0 3px;
3911 padding: 0;
3908 padding: 0;
3912 }
3909 }
3913
3910
3914 #login div.title h5,#register div.title h5 {
3911 #login div.title h5,#register div.title h5 {
3915 color: #fff;
3912 color: #fff;
3916 margin: 10px;
3913 margin: 10px;
3917 padding: 0;
3914 padding: 0;
3918 }
3915 }
3919
3916
3920 #login div.form div.fields div.field,#register div.form div.fields div.field
3917 #login div.form div.fields div.field,#register div.form div.fields div.field
3921 {
3918 {
3922 clear: both;
3919 clear: both;
3923 overflow: hidden;
3920 overflow: hidden;
3924 margin: 0;
3921 margin: 0;
3925 padding: 0 0 10px;
3922 padding: 0 0 10px;
3926 }
3923 }
3927
3924
3928 #login div.form div.fields div.field span.error-message,#register div.form div.fields div.field span.error-message
3925 #login div.form div.fields div.field span.error-message,#register div.form div.fields div.field span.error-message
3929 {
3926 {
3930 height: 1%;
3927 height: 1%;
3931 display: block;
3928 display: block;
3932 color: red;
3929 color: red;
3933 margin: 8px 0 0;
3930 margin: 8px 0 0;
3934 padding: 0;
3931 padding: 0;
3935 max-width: 320px;
3932 max-width: 320px;
3936 }
3933 }
3937
3934
3938 #login div.form div.fields div.field div.label label,#register div.form div.fields div.field div.label label
3935 #login div.form div.fields div.field div.label label,#register div.form div.fields div.field div.label label
3939 {
3936 {
3940 color: #000;
3937 color: #000;
3941 font-weight: 700;
3938 font-weight: 700;
3942 }
3939 }
3943
3940
3944 #login div.form div.fields div.field div.input,#register div.form div.fields div.field div.input
3941 #login div.form div.fields div.field div.input,#register div.form div.fields div.field div.input
3945 {
3942 {
3946 float: left;
3943 float: left;
3947 margin: 0;
3944 margin: 0;
3948 padding: 0;
3945 padding: 0;
3949 }
3946 }
3950
3947
3951 #login div.form div.fields div.field div.checkbox,#register div.form div.fields div.field div.checkbox
3948 #login div.form div.fields div.field div.checkbox,#register div.form div.fields div.field div.checkbox
3952 {
3949 {
3953 margin: 0 0 0 184px;
3950 margin: 0 0 0 184px;
3954 padding: 0;
3951 padding: 0;
3955 }
3952 }
3956
3953
3957 #login div.form div.fields div.field div.checkbox label,#register div.form div.fields div.field div.checkbox label
3954 #login div.form div.fields div.field div.checkbox label,#register div.form div.fields div.field div.checkbox label
3958 {
3955 {
3959 color: #565656;
3956 color: #565656;
3960 font-weight: 700;
3957 font-weight: 700;
3961 }
3958 }
3962
3959
3963 #login div.form div.fields div.buttons input,#register div.form div.fields div.buttons input
3960 #login div.form div.fields div.buttons input,#register div.form div.fields div.buttons input
3964 {
3961 {
3965 color: #000;
3962 color: #000;
3966 font-size: 1em;
3963 font-size: 1em;
3967 font-weight: 700;
3964 font-weight: 700;
3968 margin: 0;
3965 margin: 0;
3969 }
3966 }
3970
3967
3971 #changeset_content .container .wrapper,#graph_content .container .wrapper
3968 #changeset_content .container .wrapper,#graph_content .container .wrapper
3972 {
3969 {
3973 width: 600px;
3970 width: 600px;
3974 }
3971 }
3975
3972
3976 #changeset_content .container .left {
3973 #changeset_content .container .left {
3977 float: left;
3974 float: left;
3978 width: 75%;
3975 width: 75%;
3979 padding-left: 5px;
3976 padding-left: 5px;
3980 }
3977 }
3981
3978
3982 #changeset_content .container .left .date,.ac .match {
3979 #changeset_content .container .left .date,.ac .match {
3983 font-weight: 700;
3980 font-weight: 700;
3984 padding-top: 5px;
3981 padding-top: 5px;
3985 padding-bottom: 5px;
3982 padding-bottom: 5px;
3986 }
3983 }
3987
3984
3988 div#legend_container table td,div#legend_choices table td {
3985 div#legend_container table td,div#legend_choices table td {
3989 border: none !important;
3986 border: none !important;
3990 height: 20px !important;
3987 height: 20px !important;
3991 padding: 0 !important;
3988 padding: 0 !important;
3992 }
3989 }
3993
3990
3994 .q_filter_box {
3991 .q_filter_box {
3995 -webkit-box-shadow: rgba(0,0,0,0.07) 0 1px 2px inset;
3992 -webkit-box-shadow: rgba(0,0,0,0.07) 0 1px 2px inset;
3996 -webkit-border-radius: 4px;
3993 -webkit-border-radius: 4px;
3997 -moz-border-radius: 4px;
3994 -moz-border-radius: 4px;
3998 border-radius: 4px;
3995 border-radius: 4px;
3999 border: 0 none;
3996 border: 0 none;
4000 color: #AAAAAA;
3997 color: #AAAAAA;
4001 margin-bottom: -4px;
3998 margin-bottom: -4px;
4002 margin-top: -4px;
3999 margin-top: -4px;
4003 padding-left: 3px;
4000 padding-left: 3px;
4004 }
4001 }
4005
4002
4006 #node_filter {
4003 #node_filter {
4007 border: 0px solid #545454;
4004 border: 0px solid #545454;
4008 color: #AAAAAA;
4005 color: #AAAAAA;
4009 padding-left: 3px;
4006 padding-left: 3px;
4010 }
4007 }
4011
4008
4012
4009
4013 .group_members_wrap{
4010 .group_members_wrap{
4014 min-height: 85px;
4011 min-height: 85px;
4015 padding-left: 20px;
4012 padding-left: 20px;
4016 }
4013 }
4017
4014
4018 .group_members .group_member{
4015 .group_members .group_member{
4019 height: 30px;
4016 height: 30px;
4020 padding:0px 0px 0px 0px;
4017 padding:0px 0px 0px 0px;
4021 }
4018 }
4022
4019
4023 .reviewers_member{
4020 .reviewers_member{
4024 height: 15px;
4021 height: 15px;
4025 padding:0px 0px 0px 10px;
4022 padding:0px 0px 0px 10px;
4026 }
4023 }
4027
4024
4028 .emails_wrap{
4025 .emails_wrap{
4029 padding: 0px 20px;
4026 padding: 0px 20px;
4030 }
4027 }
4031
4028
4032 .emails_wrap .email_entry{
4029 .emails_wrap .email_entry{
4033 height: 30px;
4030 height: 30px;
4034 padding:0px 0px 0px 10px;
4031 padding:0px 0px 0px 10px;
4035 }
4032 }
4036 .emails_wrap .email_entry .email{
4033 .emails_wrap .email_entry .email{
4037 float: left
4034 float: left
4038 }
4035 }
4039 .emails_wrap .email_entry .email_action{
4036 .emails_wrap .email_entry .email_action{
4040 float: left
4037 float: left
4041 }
4038 }
4042
4039
4043 .ips_wrap{
4040 .ips_wrap{
4044 padding: 0px 20px;
4041 padding: 0px 20px;
4045 }
4042 }
4046
4043
4047 .ips_wrap .ip_entry{
4044 .ips_wrap .ip_entry{
4048 height: 30px;
4045 height: 30px;
4049 padding:0px 0px 0px 10px;
4046 padding:0px 0px 0px 10px;
4050 }
4047 }
4051 .ips_wrap .ip_entry .ip{
4048 .ips_wrap .ip_entry .ip{
4052 float: left
4049 float: left
4053 }
4050 }
4054 .ips_wrap .ip_entry .ip_action{
4051 .ips_wrap .ip_entry .ip_action{
4055 float: left
4052 float: left
4056 }
4053 }
4057
4054
4058
4055
4059 /*README STYLE*/
4056 /*README STYLE*/
4060
4057
4061 div.readme {
4058 div.readme {
4062 padding:0px;
4059 padding:0px;
4063 }
4060 }
4064
4061
4065 div.readme h2 {
4062 div.readme h2 {
4066 font-weight: normal;
4063 font-weight: normal;
4067 }
4064 }
4068
4065
4069 div.readme .readme_box {
4066 div.readme .readme_box {
4070 background-color: #fafafa;
4067 background-color: #fafafa;
4071 }
4068 }
4072
4069
4073 div.readme .readme_box {
4070 div.readme .readme_box {
4074 clear:both;
4071 clear:both;
4075 overflow:hidden;
4072 overflow:hidden;
4076 margin:0;
4073 margin:0;
4077 padding:0 20px 10px;
4074 padding:0 20px 10px;
4078 }
4075 }
4079
4076
4080 div.readme .readme_box h1, div.readme .readme_box h2, div.readme .readme_box h3, div.readme .readme_box h4, div.readme .readme_box h5, div.readme .readme_box h6 {
4077 div.readme .readme_box h1, div.readme .readme_box h2, div.readme .readme_box h3, div.readme .readme_box h4, div.readme .readme_box h5, div.readme .readme_box h6 {
4081 border-bottom: 0 !important;
4078 border-bottom: 0 !important;
4082 margin: 0 !important;
4079 margin: 0 !important;
4083 padding: 0 !important;
4080 padding: 0 !important;
4084 line-height: 1.5em !important;
4081 line-height: 1.5em !important;
4085 }
4082 }
4086
4083
4087
4084
4088 div.readme .readme_box h1:first-child {
4085 div.readme .readme_box h1:first-child {
4089 padding-top: .25em !important;
4086 padding-top: .25em !important;
4090 }
4087 }
4091
4088
4092 div.readme .readme_box h2, div.readme .readme_box h3 {
4089 div.readme .readme_box h2, div.readme .readme_box h3 {
4093 margin: 1em 0 !important;
4090 margin: 1em 0 !important;
4094 }
4091 }
4095
4092
4096 div.readme .readme_box h2 {
4093 div.readme .readme_box h2 {
4097 margin-top: 1.5em !important;
4094 margin-top: 1.5em !important;
4098 border-top: 4px solid #e0e0e0 !important;
4095 border-top: 4px solid #e0e0e0 !important;
4099 padding-top: .5em !important;
4096 padding-top: .5em !important;
4100 }
4097 }
4101
4098
4102 div.readme .readme_box p {
4099 div.readme .readme_box p {
4103 color: black !important;
4100 color: black !important;
4104 margin: 1em 0 !important;
4101 margin: 1em 0 !important;
4105 line-height: 1.5em !important;
4102 line-height: 1.5em !important;
4106 }
4103 }
4107
4104
4108 div.readme .readme_box ul {
4105 div.readme .readme_box ul {
4109 list-style: disc !important;
4106 list-style: disc !important;
4110 margin: 1em 0 1em 2em !important;
4107 margin: 1em 0 1em 2em !important;
4111 }
4108 }
4112
4109
4113 div.readme .readme_box ol {
4110 div.readme .readme_box ol {
4114 list-style: decimal;
4111 list-style: decimal;
4115 margin: 1em 0 1em 2em !important;
4112 margin: 1em 0 1em 2em !important;
4116 }
4113 }
4117
4114
4118 div.readme .readme_box pre, code {
4115 div.readme .readme_box pre, code {
4119 font: 12px "Bitstream Vera Sans Mono","Courier",monospace;
4116 font: 12px "Bitstream Vera Sans Mono","Courier",monospace;
4120 }
4117 }
4121
4118
4122 div.readme .readme_box code {
4119 div.readme .readme_box code {
4123 font-size: 12px !important;
4120 font-size: 12px !important;
4124 background-color: ghostWhite !important;
4121 background-color: ghostWhite !important;
4125 color: #444 !important;
4122 color: #444 !important;
4126 padding: 0 .2em !important;
4123 padding: 0 .2em !important;
4127 border: 1px solid #dedede !important;
4124 border: 1px solid #dedede !important;
4128 }
4125 }
4129
4126
4130 div.readme .readme_box pre code {
4127 div.readme .readme_box pre code {
4131 padding: 0 !important;
4128 padding: 0 !important;
4132 font-size: 12px !important;
4129 font-size: 12px !important;
4133 background-color: #eee !important;
4130 background-color: #eee !important;
4134 border: none !important;
4131 border: none !important;
4135 }
4132 }
4136
4133
4137 div.readme .readme_box pre {
4134 div.readme .readme_box pre {
4138 margin: 1em 0;
4135 margin: 1em 0;
4139 font-size: 12px;
4136 font-size: 12px;
4140 background-color: #eee;
4137 background-color: #eee;
4141 border: 1px solid #ddd;
4138 border: 1px solid #ddd;
4142 padding: 5px;
4139 padding: 5px;
4143 color: #444;
4140 color: #444;
4144 overflow: auto;
4141 overflow: auto;
4145 -webkit-box-shadow: rgba(0,0,0,0.07) 0 1px 2px inset;
4142 -webkit-box-shadow: rgba(0,0,0,0.07) 0 1px 2px inset;
4146 -webkit-border-radius: 3px;
4143 -webkit-border-radius: 3px;
4147 -moz-border-radius: 3px;
4144 -moz-border-radius: 3px;
4148 border-radius: 3px;
4145 border-radius: 3px;
4149 }
4146 }
4150
4147
4151 div.readme .readme_box table {
4148 div.readme .readme_box table {
4152 display: table;
4149 display: table;
4153 border-collapse: separate;
4150 border-collapse: separate;
4154 border-spacing: 2px;
4151 border-spacing: 2px;
4155 border-color: gray;
4152 border-color: gray;
4156 width: auto !important;
4153 width: auto !important;
4157 }
4154 }
4158
4155
4159
4156
4160 /** RST STYLE **/
4157 /** RST STYLE **/
4161
4158
4162
4159
4163 div.rst-block {
4160 div.rst-block {
4164 padding:0px;
4161 padding:0px;
4165 }
4162 }
4166
4163
4167 div.rst-block h2 {
4164 div.rst-block h2 {
4168 font-weight: normal;
4165 font-weight: normal;
4169 }
4166 }
4170
4167
4171 div.rst-block {
4168 div.rst-block {
4172 background-color: #fafafa;
4169 background-color: #fafafa;
4173 }
4170 }
4174
4171
4175 div.rst-block {
4172 div.rst-block {
4176 clear:both;
4173 clear:both;
4177 overflow:hidden;
4174 overflow:hidden;
4178 margin:0;
4175 margin:0;
4179 padding:0 20px 10px;
4176 padding:0 20px 10px;
4180 }
4177 }
4181
4178
4182 div.rst-block h1, div.rst-block h2, div.rst-block h3, div.rst-block h4, div.rst-block h5, div.rst-block h6 {
4179 div.rst-block h1, div.rst-block h2, div.rst-block h3, div.rst-block h4, div.rst-block h5, div.rst-block h6 {
4183 border-bottom: 0 !important;
4180 border-bottom: 0 !important;
4184 margin: 0 !important;
4181 margin: 0 !important;
4185 padding: 0 !important;
4182 padding: 0 !important;
4186 line-height: 1.5em !important;
4183 line-height: 1.5em !important;
4187 }
4184 }
4188
4185
4189
4186
4190 div.rst-block h1:first-child {
4187 div.rst-block h1:first-child {
4191 padding-top: .25em !important;
4188 padding-top: .25em !important;
4192 }
4189 }
4193
4190
4194 div.rst-block h2, div.rst-block h3 {
4191 div.rst-block h2, div.rst-block h3 {
4195 margin: 1em 0 !important;
4192 margin: 1em 0 !important;
4196 }
4193 }
4197
4194
4198 div.rst-block h2 {
4195 div.rst-block h2 {
4199 margin-top: 1.5em !important;
4196 margin-top: 1.5em !important;
4200 border-top: 4px solid #e0e0e0 !important;
4197 border-top: 4px solid #e0e0e0 !important;
4201 padding-top: .5em !important;
4198 padding-top: .5em !important;
4202 }
4199 }
4203
4200
4204 div.rst-block p {
4201 div.rst-block p {
4205 color: black !important;
4202 color: black !important;
4206 margin: 1em 0 !important;
4203 margin: 1em 0 !important;
4207 line-height: 1.5em !important;
4204 line-height: 1.5em !important;
4208 }
4205 }
4209
4206
4210 div.rst-block ul {
4207 div.rst-block ul {
4211 list-style: disc !important;
4208 list-style: disc !important;
4212 margin: 1em 0 1em 2em !important;
4209 margin: 1em 0 1em 2em !important;
4213 }
4210 }
4214
4211
4215 div.rst-block ol {
4212 div.rst-block ol {
4216 list-style: decimal;
4213 list-style: decimal;
4217 margin: 1em 0 1em 2em !important;
4214 margin: 1em 0 1em 2em !important;
4218 }
4215 }
4219
4216
4220 div.rst-block pre, code {
4217 div.rst-block pre, code {
4221 font: 12px "Bitstream Vera Sans Mono","Courier",monospace;
4218 font: 12px "Bitstream Vera Sans Mono","Courier",monospace;
4222 }
4219 }
4223
4220
4224 div.rst-block code {
4221 div.rst-block code {
4225 font-size: 12px !important;
4222 font-size: 12px !important;
4226 background-color: ghostWhite !important;
4223 background-color: ghostWhite !important;
4227 color: #444 !important;
4224 color: #444 !important;
4228 padding: 0 .2em !important;
4225 padding: 0 .2em !important;
4229 border: 1px solid #dedede !important;
4226 border: 1px solid #dedede !important;
4230 }
4227 }
4231
4228
4232 div.rst-block pre code {
4229 div.rst-block pre code {
4233 padding: 0 !important;
4230 padding: 0 !important;
4234 font-size: 12px !important;
4231 font-size: 12px !important;
4235 background-color: #eee !important;
4232 background-color: #eee !important;
4236 border: none !important;
4233 border: none !important;
4237 }
4234 }
4238
4235
4239 div.rst-block pre {
4236 div.rst-block pre {
4240 margin: 1em 0;
4237 margin: 1em 0;
4241 font-size: 12px;
4238 font-size: 12px;
4242 background-color: #eee;
4239 background-color: #eee;
4243 border: 1px solid #ddd;
4240 border: 1px solid #ddd;
4244 padding: 5px;
4241 padding: 5px;
4245 color: #444;
4242 color: #444;
4246 overflow: auto;
4243 overflow: auto;
4247 -webkit-box-shadow: rgba(0,0,0,0.07) 0 1px 2px inset;
4244 -webkit-box-shadow: rgba(0,0,0,0.07) 0 1px 2px inset;
4248 -webkit-border-radius: 3px;
4245 -webkit-border-radius: 3px;
4249 -moz-border-radius: 3px;
4246 -moz-border-radius: 3px;
4250 border-radius: 3px;
4247 border-radius: 3px;
4251 }
4248 }
4252
4249
4253
4250
4254 /** comment main **/
4251 /** comment main **/
4255 .comments {
4252 .comments {
4256 padding:10px 20px;
4253 padding:10px 20px;
4257 }
4254 }
4258
4255
4259 .comments .comment {
4256 .comments .comment {
4260 border: 1px solid #ddd;
4257 border: 1px solid #ddd;
4261 margin-top: 10px;
4258 margin-top: 10px;
4262 -webkit-border-radius: 4px;
4259 -webkit-border-radius: 4px;
4263 -moz-border-radius: 4px;
4260 -moz-border-radius: 4px;
4264 border-radius: 4px;
4261 border-radius: 4px;
4265 }
4262 }
4266
4263
4267 .comments .comment .meta {
4264 .comments .comment .meta {
4268 background: #f8f8f8;
4265 background: #f8f8f8;
4269 padding: 4px;
4266 padding: 4px;
4270 border-bottom: 1px solid #ddd;
4267 border-bottom: 1px solid #ddd;
4271 height: 18px;
4268 height: 18px;
4272 }
4269 }
4273
4270
4274 .comments .comment .meta img {
4271 .comments .comment .meta img {
4275 vertical-align: middle;
4272 vertical-align: middle;
4276 }
4273 }
4277
4274
4278 .comments .comment .meta .user {
4275 .comments .comment .meta .user {
4279 font-weight: bold;
4276 font-weight: bold;
4280 float: left;
4277 float: left;
4281 padding: 4px 2px 2px 2px;
4278 padding: 4px 2px 2px 2px;
4282 }
4279 }
4283
4280
4284 .comments .comment .meta .date {
4281 .comments .comment .meta .date {
4285 float: left;
4282 float: left;
4286 padding:4px 4px 0px 4px;
4283 padding:4px 4px 0px 4px;
4287 }
4284 }
4288
4285
4289 .comments .comment .text {
4286 .comments .comment .text {
4290 background-color: #FAFAFA;
4287 background-color: #FAFAFA;
4291 }
4288 }
4292 .comment .text div.rst-block p {
4289 .comment .text div.rst-block p {
4293 margin: 0.5em 0px !important;
4290 margin: 0.5em 0px !important;
4294 }
4291 }
4295
4292
4296 .comments .comments-number{
4293 .comments .comments-number{
4297 padding:0px 0px 10px 0px;
4294 padding:0px 0px 10px 0px;
4298 font-weight: bold;
4295 font-weight: bold;
4299 color: #666;
4296 color: #666;
4300 font-size: 16px;
4297 font-size: 16px;
4301 }
4298 }
4302
4299
4303 /** comment form **/
4300 /** comment form **/
4304
4301
4305 .status-block{
4302 .status-block{
4306 height:80px;
4303 height:80px;
4307 clear:both
4304 clear:both
4308 }
4305 }
4309
4306
4310 .comment-form .clearfix{
4307 .comment-form .clearfix{
4311 background: #EEE;
4308 background: #EEE;
4312 -webkit-border-radius: 4px;
4309 -webkit-border-radius: 4px;
4313 -moz-border-radius: 4px;
4310 -moz-border-radius: 4px;
4314 border-radius: 4px;
4311 border-radius: 4px;
4315 padding: 10px;
4312 padding: 10px;
4316 }
4313 }
4317
4314
4318 div.comment-form {
4315 div.comment-form {
4319 margin-top: 20px;
4316 margin-top: 20px;
4320 }
4317 }
4321
4318
4322 .comment-form strong {
4319 .comment-form strong {
4323 display: block;
4320 display: block;
4324 margin-bottom: 15px;
4321 margin-bottom: 15px;
4325 }
4322 }
4326
4323
4327 .comment-form textarea {
4324 .comment-form textarea {
4328 width: 100%;
4325 width: 100%;
4329 height: 100px;
4326 height: 100px;
4330 font-family: 'Monaco', 'Courier', 'Courier New', monospace;
4327 font-family: 'Monaco', 'Courier', 'Courier New', monospace;
4331 }
4328 }
4332
4329
4333 form.comment-form {
4330 form.comment-form {
4334 margin-top: 10px;
4331 margin-top: 10px;
4335 margin-left: 10px;
4332 margin-left: 10px;
4336 }
4333 }
4337
4334
4338 .comment-form-submit {
4335 .comment-form-submit {
4339 margin-top: 5px;
4336 margin-top: 5px;
4340 margin-left: 525px;
4337 margin-left: 525px;
4341 }
4338 }
4342
4339
4343 .file-comments {
4340 .file-comments {
4344 display: none;
4341 display: none;
4345 }
4342 }
4346
4343
4347 .comment-form .comment {
4344 .comment-form .comment {
4348 margin-left: 10px;
4345 margin-left: 10px;
4349 }
4346 }
4350
4347
4351 .comment-form .comment-help{
4348 .comment-form .comment-help{
4352 padding: 0px 0px 5px 0px;
4349 padding: 0px 0px 5px 0px;
4353 color: #666;
4350 color: #666;
4354 }
4351 }
4355
4352
4356 .comment-form .comment-button{
4353 .comment-form .comment-button{
4357 padding-top:5px;
4354 padding-top:5px;
4358 }
4355 }
4359
4356
4360 .add-another-button {
4357 .add-another-button {
4361 margin-left: 10px;
4358 margin-left: 10px;
4362 margin-top: 10px;
4359 margin-top: 10px;
4363 margin-bottom: 10px;
4360 margin-bottom: 10px;
4364 }
4361 }
4365
4362
4366 .comment .buttons {
4363 .comment .buttons {
4367 float: right;
4364 float: right;
4368 padding:2px 2px 0px 0px;
4365 padding:2px 2px 0px 0px;
4369 }
4366 }
4370
4367
4371
4368
4372 .show-inline-comments{
4369 .show-inline-comments{
4373 position: relative;
4370 position: relative;
4374 top:1px
4371 top:1px
4375 }
4372 }
4376
4373
4377 /** comment inline form **/
4374 /** comment inline form **/
4378 .comment-inline-form .overlay{
4375 .comment-inline-form .overlay{
4379 display: none;
4376 display: none;
4380 }
4377 }
4381 .comment-inline-form .overlay.submitting{
4378 .comment-inline-form .overlay.submitting{
4382 display:block;
4379 display:block;
4383 background: none repeat scroll 0 0 white;
4380 background: none repeat scroll 0 0 white;
4384 font-size: 16px;
4381 font-size: 16px;
4385 opacity: 0.5;
4382 opacity: 0.5;
4386 position: absolute;
4383 position: absolute;
4387 text-align: center;
4384 text-align: center;
4388 vertical-align: top;
4385 vertical-align: top;
4389
4386
4390 }
4387 }
4391 .comment-inline-form .overlay.submitting .overlay-text{
4388 .comment-inline-form .overlay.submitting .overlay-text{
4392 width:100%;
4389 width:100%;
4393 margin-top:5%;
4390 margin-top:5%;
4394 }
4391 }
4395
4392
4396 .comment-inline-form .clearfix{
4393 .comment-inline-form .clearfix{
4397 background: #EEE;
4394 background: #EEE;
4398 -webkit-border-radius: 4px;
4395 -webkit-border-radius: 4px;
4399 -moz-border-radius: 4px;
4396 -moz-border-radius: 4px;
4400 border-radius: 4px;
4397 border-radius: 4px;
4401 padding: 5px;
4398 padding: 5px;
4402 }
4399 }
4403
4400
4404 div.comment-inline-form {
4401 div.comment-inline-form {
4405 padding:4px 0px 6px 0px;
4402 padding:4px 0px 6px 0px;
4406 }
4403 }
4407
4404
4408
4405
4409 tr.hl-comment{
4406 tr.hl-comment{
4410 /*
4407 /*
4411 background-color: #FFFFCC !important;
4408 background-color: #FFFFCC !important;
4412 */
4409 */
4413 }
4410 }
4414
4411
4415 /*
4412 /*
4416 tr.hl-comment pre {
4413 tr.hl-comment pre {
4417 border-top: 2px solid #FFEE33;
4414 border-top: 2px solid #FFEE33;
4418 border-left: 2px solid #FFEE33;
4415 border-left: 2px solid #FFEE33;
4419 border-right: 2px solid #FFEE33;
4416 border-right: 2px solid #FFEE33;
4420 }
4417 }
4421 */
4418 */
4422
4419
4423 .comment-inline-form strong {
4420 .comment-inline-form strong {
4424 display: block;
4421 display: block;
4425 margin-bottom: 15px;
4422 margin-bottom: 15px;
4426 }
4423 }
4427
4424
4428 .comment-inline-form textarea {
4425 .comment-inline-form textarea {
4429 width: 100%;
4426 width: 100%;
4430 height: 100px;
4427 height: 100px;
4431 font-family: 'Monaco', 'Courier', 'Courier New', monospace;
4428 font-family: 'Monaco', 'Courier', 'Courier New', monospace;
4432 }
4429 }
4433
4430
4434 form.comment-inline-form {
4431 form.comment-inline-form {
4435 margin-top: 10px;
4432 margin-top: 10px;
4436 margin-left: 10px;
4433 margin-left: 10px;
4437 }
4434 }
4438
4435
4439 .comment-inline-form-submit {
4436 .comment-inline-form-submit {
4440 margin-top: 5px;
4437 margin-top: 5px;
4441 margin-left: 525px;
4438 margin-left: 525px;
4442 }
4439 }
4443
4440
4444 .file-comments {
4441 .file-comments {
4445 display: none;
4442 display: none;
4446 }
4443 }
4447
4444
4448 .comment-inline-form .comment {
4445 .comment-inline-form .comment {
4449 margin-left: 10px;
4446 margin-left: 10px;
4450 }
4447 }
4451
4448
4452 .comment-inline-form .comment-help{
4449 .comment-inline-form .comment-help{
4453 padding: 0px 0px 2px 0px;
4450 padding: 0px 0px 2px 0px;
4454 color: #666666;
4451 color: #666666;
4455 font-size: 10px;
4452 font-size: 10px;
4456 }
4453 }
4457
4454
4458 .comment-inline-form .comment-button{
4455 .comment-inline-form .comment-button{
4459 padding-top:5px;
4456 padding-top:5px;
4460 }
4457 }
4461
4458
4462 /** comment inline **/
4459 /** comment inline **/
4463 .inline-comments {
4460 .inline-comments {
4464 padding:10px 20px;
4461 padding:10px 20px;
4465 }
4462 }
4466
4463
4467 .inline-comments div.rst-block {
4464 .inline-comments div.rst-block {
4468 clear:both;
4465 clear:both;
4469 overflow:hidden;
4466 overflow:hidden;
4470 margin:0;
4467 margin:0;
4471 padding:0 20px 0px;
4468 padding:0 20px 0px;
4472 }
4469 }
4473 .inline-comments .comment {
4470 .inline-comments .comment {
4474 border: 1px solid #ddd;
4471 border: 1px solid #ddd;
4475 -webkit-border-radius: 4px;
4472 -webkit-border-radius: 4px;
4476 -moz-border-radius: 4px;
4473 -moz-border-radius: 4px;
4477 border-radius: 4px;
4474 border-radius: 4px;
4478 margin: 3px 3px 5px 5px;
4475 margin: 3px 3px 5px 5px;
4479 background-color: #FAFAFA;
4476 background-color: #FAFAFA;
4480 }
4477 }
4481 .inline-comments .add-comment {
4478 .inline-comments .add-comment {
4482 padding: 2px 4px 8px 5px;
4479 padding: 2px 4px 8px 5px;
4483 }
4480 }
4484
4481
4485 .inline-comments .comment-wrapp{
4482 .inline-comments .comment-wrapp{
4486 padding:1px;
4483 padding:1px;
4487 }
4484 }
4488 .inline-comments .comment .meta {
4485 .inline-comments .comment .meta {
4489 background: #f8f8f8;
4486 background: #f8f8f8;
4490 padding: 4px;
4487 padding: 4px;
4491 border-bottom: 1px solid #ddd;
4488 border-bottom: 1px solid #ddd;
4492 height: 20px;
4489 height: 20px;
4493 }
4490 }
4494
4491
4495 .inline-comments .comment .meta img {
4492 .inline-comments .comment .meta img {
4496 vertical-align: middle;
4493 vertical-align: middle;
4497 }
4494 }
4498
4495
4499 .inline-comments .comment .meta .user {
4496 .inline-comments .comment .meta .user {
4500 font-weight: bold;
4497 font-weight: bold;
4501 float:left;
4498 float:left;
4502 padding: 3px;
4499 padding: 3px;
4503 }
4500 }
4504
4501
4505 .inline-comments .comment .meta .date {
4502 .inline-comments .comment .meta .date {
4506 float:left;
4503 float:left;
4507 padding: 3px;
4504 padding: 3px;
4508 }
4505 }
4509
4506
4510 .inline-comments .comment .text {
4507 .inline-comments .comment .text {
4511 background-color: #FAFAFA;
4508 background-color: #FAFAFA;
4512 }
4509 }
4513
4510
4514 .inline-comments .comments-number{
4511 .inline-comments .comments-number{
4515 padding:0px 0px 10px 0px;
4512 padding:0px 0px 10px 0px;
4516 font-weight: bold;
4513 font-weight: bold;
4517 color: #666;
4514 color: #666;
4518 font-size: 16px;
4515 font-size: 16px;
4519 }
4516 }
4520 .inline-comments-button .add-comment{
4517 .inline-comments-button .add-comment{
4521 margin:2px 0px 8px 5px !important
4518 margin:2px 0px 8px 5px !important
4522 }
4519 }
4523
4520
4524
4521
4525 .notification-paginator{
4522 .notification-paginator{
4526 padding: 0px 0px 4px 16px;
4523 padding: 0px 0px 4px 16px;
4527 float: left;
4524 float: left;
4528 }
4525 }
4529
4526
4530 .notifications{
4527 .notifications{
4531 border-radius: 4px 4px 4px 4px;
4528 border-radius: 4px 4px 4px 4px;
4532 -webkit-border-radius: 4px;
4529 -webkit-border-radius: 4px;
4533 -moz-border-radius: 4px;
4530 -moz-border-radius: 4px;
4534 float: right;
4531 float: right;
4535 margin: 20px 0px 0px 0px;
4532 margin: 20px 0px 0px 0px;
4536 position: absolute;
4533 position: absolute;
4537 text-align: center;
4534 text-align: center;
4538 width: 26px;
4535 width: 26px;
4539 z-index: 1000;
4536 z-index: 1000;
4540 }
4537 }
4541 .notifications a{
4538 .notifications a{
4542 color:#888 !important;
4539 color:#888 !important;
4543 display: block;
4540 display: block;
4544 font-size: 10px;
4541 font-size: 10px;
4545 background-color: #DEDEDE !important;
4542 background-color: #DEDEDE !important;
4546 border-radius: 2px !important;
4543 border-radius: 2px !important;
4547 -webkit-border-radius: 2px !important;
4544 -webkit-border-radius: 2px !important;
4548 -moz-border-radius: 2px !important;
4545 -moz-border-radius: 2px !important;
4549 }
4546 }
4550 .notifications a:hover{
4547 .notifications a:hover{
4551 text-decoration: none !important;
4548 text-decoration: none !important;
4552 background-color: #EEEFFF !important;
4549 background-color: #EEEFFF !important;
4553 }
4550 }
4554 .notification-header{
4551 .notification-header{
4555 padding-top:6px;
4552 padding-top:6px;
4556 }
4553 }
4557 .notification-header .desc{
4554 .notification-header .desc{
4558 font-size: 16px;
4555 font-size: 16px;
4559 height: 24px;
4556 height: 24px;
4560 float: left
4557 float: left
4561 }
4558 }
4562 .notification-list .container.unread{
4559 .notification-list .container.unread{
4563 background: none repeat scroll 0 0 rgba(255, 255, 180, 0.6);
4560 background: none repeat scroll 0 0 rgba(255, 255, 180, 0.6);
4564 }
4561 }
4565 .notification-header .gravatar{
4562 .notification-header .gravatar{
4566 background: none repeat scroll 0 0 transparent;
4563 background: none repeat scroll 0 0 transparent;
4567 padding: 0px 0px 0px 8px;
4564 padding: 0px 0px 0px 8px;
4568 }
4565 }
4569 .notification-list .container .notification-header .desc{
4566 .notification-list .container .notification-header .desc{
4570 font-weight: bold;
4567 font-weight: bold;
4571 font-size: 17px;
4568 font-size: 17px;
4572 }
4569 }
4573 .notification-table{
4570 .notification-table{
4574 border: 1px solid #ccc;
4571 border: 1px solid #ccc;
4575 -webkit-border-radius: 6px 6px 6px 6px;
4572 -webkit-border-radius: 6px 6px 6px 6px;
4576 -moz-border-radius: 6px 6px 6px 6px;
4573 -moz-border-radius: 6px 6px 6px 6px;
4577 border-radius: 6px 6px 6px 6px;
4574 border-radius: 6px 6px 6px 6px;
4578 clear: both;
4575 clear: both;
4579 margin: 0px 20px 0px 20px;
4576 margin: 0px 20px 0px 20px;
4580 }
4577 }
4581 .notification-header .delete-notifications{
4578 .notification-header .delete-notifications{
4582 float: right;
4579 float: right;
4583 padding-top: 8px;
4580 padding-top: 8px;
4584 cursor: pointer;
4581 cursor: pointer;
4585 }
4582 }
4586 .notification-header .read-notifications{
4583 .notification-header .read-notifications{
4587 float: right;
4584 float: right;
4588 padding-top: 8px;
4585 padding-top: 8px;
4589 cursor: pointer;
4586 cursor: pointer;
4590 }
4587 }
4591 .notification-subject{
4588 .notification-subject{
4592 clear:both;
4589 clear:both;
4593 border-bottom: 1px solid #eee;
4590 border-bottom: 1px solid #eee;
4594 padding:5px 0px 5px 38px;
4591 padding:5px 0px 5px 38px;
4595 }
4592 }
4596
4593
4597 .notification-body{
4594 .notification-body{
4598 clear:both;
4595 clear:both;
4599 margin: 34px 2px 2px 8px
4596 margin: 34px 2px 2px 8px
4600 }
4597 }
4601
4598
4602 /****
4599 /****
4603 PULL REQUESTS
4600 PULL REQUESTS
4604 *****/
4601 *****/
4605 .pullrequests_section_head {
4602 .pullrequests_section_head {
4606 padding:10px 10px 10px 0px;
4603 padding:10px 10px 10px 0px;
4607 font-size:16px;
4604 font-size:16px;
4608 font-weight: bold;
4605 font-weight: bold;
4609 }
4606 }
4610
4607
4611 /****
4608 /****
4612 PERMS
4609 PERMS
4613 *****/
4610 *****/
4614 #perms .perms_section_head {
4611 #perms .perms_section_head {
4615 padding:10px 10px 10px 0px;
4612 padding:10px 10px 10px 0px;
4616 font-size:16px;
4613 font-size:16px;
4617 font-weight: bold;
4614 font-weight: bold;
4618 }
4615 }
4619
4616
4620 #perms .perm_tag{
4617 #perms .perm_tag{
4621 padding: 1px 3px 1px 3px;
4618 padding: 1px 3px 1px 3px;
4622 font-size: 10px;
4619 font-size: 10px;
4623 font-weight: bold;
4620 font-weight: bold;
4624 text-transform: uppercase;
4621 text-transform: uppercase;
4625 white-space: nowrap;
4622 white-space: nowrap;
4626 -webkit-border-radius: 3px;
4623 -webkit-border-radius: 3px;
4627 -moz-border-radius: 3px;
4624 -moz-border-radius: 3px;
4628 border-radius: 3px;
4625 border-radius: 3px;
4629 }
4626 }
4630
4627
4631 #perms .perm_tag.admin{
4628 #perms .perm_tag.admin{
4632 background-color: #B94A48;
4629 background-color: #B94A48;
4633 color: #ffffff;
4630 color: #ffffff;
4634 }
4631 }
4635
4632
4636 #perms .perm_tag.write{
4633 #perms .perm_tag.write{
4637 background-color: #B94A48;
4634 background-color: #B94A48;
4638 color: #ffffff;
4635 color: #ffffff;
4639 }
4636 }
4640
4637
4641 #perms .perm_tag.read{
4638 #perms .perm_tag.read{
4642 background-color: #468847;
4639 background-color: #468847;
4643 color: #ffffff;
4640 color: #ffffff;
4644 }
4641 }
4645
4642
4646 #perms .perm_tag.none{
4643 #perms .perm_tag.none{
4647 background-color: #bfbfbf;
4644 background-color: #bfbfbf;
4648 color: #ffffff;
4645 color: #ffffff;
4649 }
4646 }
4650
4647
4651 .perm-gravatar{
4648 .perm-gravatar{
4652 vertical-align:middle;
4649 vertical-align:middle;
4653 padding:2px;
4650 padding:2px;
4654 }
4651 }
4655 .perm-gravatar-ac{
4652 .perm-gravatar-ac{
4656 vertical-align:middle;
4653 vertical-align:middle;
4657 padding:2px;
4654 padding:2px;
4658 width: 14px;
4655 width: 14px;
4659 height: 14px;
4656 height: 14px;
4660 }
4657 }
4661
4658
4662 /*****************************************************************************
4659 /*****************************************************************************
4663 DIFFS CSS
4660 DIFFS CSS
4664 ******************************************************************************/
4661 ******************************************************************************/
4665
4662
4666 div.diffblock {
4663 div.diffblock {
4667 overflow: auto;
4664 overflow: auto;
4668 padding: 0px;
4665 padding: 0px;
4669 border: 1px solid #ccc;
4666 border: 1px solid #ccc;
4670 background: #f8f8f8;
4667 background: #f8f8f8;
4671 font-size: 100%;
4668 font-size: 100%;
4672 line-height: 100%;
4669 line-height: 100%;
4673 /* new */
4670 /* new */
4674 line-height: 125%;
4671 line-height: 125%;
4675 -webkit-border-radius: 6px 6px 0px 0px;
4672 -webkit-border-radius: 6px 6px 0px 0px;
4676 -moz-border-radius: 6px 6px 0px 0px;
4673 -moz-border-radius: 6px 6px 0px 0px;
4677 border-radius: 6px 6px 0px 0px;
4674 border-radius: 6px 6px 0px 0px;
4678 }
4675 }
4679 div.diffblock.margined{
4676 div.diffblock.margined{
4680 margin: 0px 20px 0px 20px;
4677 margin: 0px 20px 0px 20px;
4681 }
4678 }
4682 div.diffblock .code-header{
4679 div.diffblock .code-header{
4683 border-bottom: 1px solid #CCCCCC;
4680 border-bottom: 1px solid #CCCCCC;
4684 background: #EEEEEE;
4681 background: #EEEEEE;
4685 padding:10px 0 10px 0;
4682 padding:10px 0 10px 0;
4686 height: 14px;
4683 height: 14px;
4687 }
4684 }
4688
4685
4689 div.diffblock .code-header.banner{
4686 div.diffblock .code-header.banner{
4690 border-bottom: 1px solid #CCCCCC;
4687 border-bottom: 1px solid #CCCCCC;
4691 background: #EEEEEE;
4688 background: #EEEEEE;
4692 height: 14px;
4689 height: 14px;
4693 margin: 0px 95px 0px 95px;
4690 margin: 0px 95px 0px 95px;
4694 padding: 3px 3px 11px 3px;
4691 padding: 3px 3px 11px 3px;
4695 }
4692 }
4696
4693
4697 div.diffblock .code-header.cv{
4694 div.diffblock .code-header.cv{
4698 height: 34px;
4695 height: 34px;
4699 }
4696 }
4700 div.diffblock .code-header-title{
4697 div.diffblock .code-header-title{
4701 padding: 0px 0px 10px 5px !important;
4698 padding: 0px 0px 10px 5px !important;
4702 margin: 0 !important;
4699 margin: 0 !important;
4703 }
4700 }
4704 div.diffblock .code-header .hash{
4701 div.diffblock .code-header .hash{
4705 float: left;
4702 float: left;
4706 padding: 2px 0 0 2px;
4703 padding: 2px 0 0 2px;
4707 }
4704 }
4708 div.diffblock .code-header .date{
4705 div.diffblock .code-header .date{
4709 float:left;
4706 float:left;
4710 text-transform: uppercase;
4707 text-transform: uppercase;
4711 padding: 2px 0px 0px 2px;
4708 padding: 2px 0px 0px 2px;
4712 }
4709 }
4713 div.diffblock .code-header div{
4710 div.diffblock .code-header div{
4714 margin-left:4px;
4711 margin-left:4px;
4715 font-weight: bold;
4712 font-weight: bold;
4716 font-size: 14px;
4713 font-size: 14px;
4717 }
4714 }
4718
4715
4719 div.diffblock .parents {
4716 div.diffblock .parents {
4720 float: left;
4717 float: left;
4721 height: 26px;
4718 height: 26px;
4722 width:100px;
4719 width:100px;
4723 font-size: 10px;
4720 font-size: 10px;
4724 font-weight: 400;
4721 font-weight: 400;
4725 vertical-align: middle;
4722 vertical-align: middle;
4726 padding: 0px 2px 2px 2px;
4723 padding: 0px 2px 2px 2px;
4727 background-color:#eeeeee;
4724 background-color:#eeeeee;
4728 border-bottom: 1px solid #CCCCCC;
4725 border-bottom: 1px solid #CCCCCC;
4729 }
4726 }
4730
4727
4731 div.diffblock .children {
4728 div.diffblock .children {
4732 float: right;
4729 float: right;
4733 height: 26px;
4730 height: 26px;
4734 width:100px;
4731 width:100px;
4735 font-size: 10px;
4732 font-size: 10px;
4736 font-weight: 400;
4733 font-weight: 400;
4737 vertical-align: middle;
4734 vertical-align: middle;
4738 text-align: right;
4735 text-align: right;
4739 padding: 0px 2px 2px 2px;
4736 padding: 0px 2px 2px 2px;
4740 background-color:#eeeeee;
4737 background-color:#eeeeee;
4741 border-bottom: 1px solid #CCCCCC;
4738 border-bottom: 1px solid #CCCCCC;
4742 }
4739 }
4743
4740
4744 div.diffblock .code-body{
4741 div.diffblock .code-body{
4745 background: #FFFFFF;
4742 background: #FFFFFF;
4746 }
4743 }
4747 div.diffblock pre.raw{
4744 div.diffblock pre.raw{
4748 background: #FFFFFF;
4745 background: #FFFFFF;
4749 color:#000000;
4746 color:#000000;
4750 }
4747 }
4751 table.code-difftable{
4748 table.code-difftable{
4752 border-collapse: collapse;
4749 border-collapse: collapse;
4753 width: 99%;
4750 width: 99%;
4754 }
4751 }
4755 table.code-difftable td {
4752 table.code-difftable td {
4756 padding: 0 !important;
4753 padding: 0 !important;
4757 background: none !important;
4754 background: none !important;
4758 border:0 !important;
4755 border:0 !important;
4759 vertical-align: none !important;
4756 vertical-align: none !important;
4760 }
4757 }
4761 table.code-difftable .context{
4758 table.code-difftable .context{
4762 background:none repeat scroll 0 0 #DDE7EF;
4759 background:none repeat scroll 0 0 #DDE7EF;
4763 }
4760 }
4764 table.code-difftable .add{
4761 table.code-difftable .add{
4765 background:none repeat scroll 0 0 #DDFFDD;
4762 background:none repeat scroll 0 0 #DDFFDD;
4766 }
4763 }
4767 table.code-difftable .add ins{
4764 table.code-difftable .add ins{
4768 background:none repeat scroll 0 0 #AAFFAA;
4765 background:none repeat scroll 0 0 #AAFFAA;
4769 text-decoration:none;
4766 text-decoration:none;
4770 }
4767 }
4771 table.code-difftable .del{
4768 table.code-difftable .del{
4772 background:none repeat scroll 0 0 #FFDDDD;
4769 background:none repeat scroll 0 0 #FFDDDD;
4773 }
4770 }
4774 table.code-difftable .del del{
4771 table.code-difftable .del del{
4775 background:none repeat scroll 0 0 #FFAAAA;
4772 background:none repeat scroll 0 0 #FFAAAA;
4776 text-decoration:none;
4773 text-decoration:none;
4777 }
4774 }
4778
4775
4779 /** LINE NUMBERS **/
4776 /** LINE NUMBERS **/
4780 table.code-difftable .lineno{
4777 table.code-difftable .lineno{
4781
4778
4782 padding-left:2px;
4779 padding-left:2px;
4783 padding-right:2px;
4780 padding-right:2px;
4784 text-align:right;
4781 text-align:right;
4785 width:32px;
4782 width:32px;
4786 -moz-user-select:none;
4783 -moz-user-select:none;
4787 -webkit-user-select: none;
4784 -webkit-user-select: none;
4788 border-right: 1px solid #CCC !important;
4785 border-right: 1px solid #CCC !important;
4789 border-left: 0px solid #CCC !important;
4786 border-left: 0px solid #CCC !important;
4790 border-top: 0px solid #CCC !important;
4787 border-top: 0px solid #CCC !important;
4791 border-bottom: none !important;
4788 border-bottom: none !important;
4792 vertical-align: middle !important;
4789 vertical-align: middle !important;
4793
4790
4794 }
4791 }
4795 table.code-difftable .lineno.new {
4792 table.code-difftable .lineno.new {
4796 }
4793 }
4797 table.code-difftable .lineno.old {
4794 table.code-difftable .lineno.old {
4798 }
4795 }
4799 table.code-difftable .lineno a{
4796 table.code-difftable .lineno a{
4800 color:#747474 !important;
4797 color:#747474 !important;
4801 font:11px "Bitstream Vera Sans Mono",Monaco,"Courier New",Courier,monospace !important;
4798 font:11px "Bitstream Vera Sans Mono",Monaco,"Courier New",Courier,monospace !important;
4802 letter-spacing:-1px;
4799 letter-spacing:-1px;
4803 text-align:right;
4800 text-align:right;
4804 padding-right: 2px;
4801 padding-right: 2px;
4805 cursor: pointer;
4802 cursor: pointer;
4806 display: block;
4803 display: block;
4807 width: 32px;
4804 width: 32px;
4808 }
4805 }
4809
4806
4810 table.code-difftable .lineno-inline{
4807 table.code-difftable .lineno-inline{
4811 background:none repeat scroll 0 0 #FFF !important;
4808 background:none repeat scroll 0 0 #FFF !important;
4812 padding-left:2px;
4809 padding-left:2px;
4813 padding-right:2px;
4810 padding-right:2px;
4814 text-align:right;
4811 text-align:right;
4815 width:30px;
4812 width:30px;
4816 -moz-user-select:none;
4813 -moz-user-select:none;
4817 -webkit-user-select: none;
4814 -webkit-user-select: none;
4818 }
4815 }
4819
4816
4820 /** CODE **/
4817 /** CODE **/
4821 table.code-difftable .code {
4818 table.code-difftable .code {
4822 display: block;
4819 display: block;
4823 width: 100%;
4820 width: 100%;
4824 }
4821 }
4825 table.code-difftable .code td{
4822 table.code-difftable .code td{
4826 margin:0;
4823 margin:0;
4827 padding:0;
4824 padding:0;
4828 }
4825 }
4829 table.code-difftable .code pre{
4826 table.code-difftable .code pre{
4830 margin:0;
4827 margin:0;
4831 padding:0;
4828 padding:0;
4832 height: 17px;
4829 height: 17px;
4833 line-height: 17px;
4830 line-height: 17px;
4834 }
4831 }
4835
4832
4836
4833
4837 .diffblock.margined.comm .line .code:hover{
4834 .diffblock.margined.comm .line .code:hover{
4838 background-color:#FFFFCC !important;
4835 background-color:#FFFFCC !important;
4839 cursor: pointer !important;
4836 cursor: pointer !important;
4840 background-image:url("../images/icons/comment_add.png") !important;
4837 background-image:url("../images/icons/comment_add.png") !important;
4841 background-repeat:no-repeat !important;
4838 background-repeat:no-repeat !important;
4842 background-position: right !important;
4839 background-position: right !important;
4843 background-position: 0% 50% !important;
4840 background-position: 0% 50% !important;
4844 }
4841 }
4845 .diffblock.margined.comm .line .code.no-comment:hover{
4842 .diffblock.margined.comm .line .code.no-comment:hover{
4846 background-image: none !important;
4843 background-image: none !important;
4847 cursor: auto !important;
4844 cursor: auto !important;
4848 background-color: inherit !important;
4845 background-color: inherit !important;
4849
4846
4850 }
4847 }
General Comments 0
You need to be logged in to leave comments. Login now