##// END OF EJS Templates
#49 Enabled anonymous access for web interface controllable from permissions pannel
marcink -
r673:dd532af2 beta
parent child Browse files
Show More
@@ -1,162 +1,165 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # permissions controller for pylons
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; version 2
9 9 # of the License or (at your opinion) any later version of the license.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 19 # MA 02110-1301, USA.
20 20 """
21 21 Created on April 27, 2010
22 22 permissions controller for pylons
23 23 @author: marcink
24 24 """
25 25
26 26 from formencode import htmlfill
27 27 from pylons import request, session, tmpl_context as c, url
28 28 from pylons.controllers.util import abort, redirect
29 29 from pylons.i18n.translation import _
30 30 from rhodecode.lib import helpers as h
31 31 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
32 32 from rhodecode.lib.base import BaseController, render
33 33 from rhodecode.model.forms import UserForm, DefaultPermissionsForm
34 34 from rhodecode.model.permission_model import PermissionModel
35 35 from rhodecode.model.user import UserModel
36 36 import formencode
37 37 import logging
38 38 import traceback
39 39
40 40 log = logging.getLogger(__name__)
41 41
42 42 class PermissionsController(BaseController):
43 43 """REST Controller styled on the Atom Publishing Protocol"""
44 44 # To properly map this controller, ensure your config/routing.py
45 45 # file has a resource setup:
46 46 # map.resource('permission', 'permissions')
47 47
48 48 @LoginRequired()
49 49 @HasPermissionAllDecorator('hg.admin')
50 50 def __before__(self):
51 51 c.admin_user = session.get('admin_user')
52 52 c.admin_username = session.get('admin_username')
53 53 super(PermissionsController, self).__before__()
54 54
55 55 self.perms_choices = [('repository.none', _('None'),),
56 56 ('repository.read', _('Read'),),
57 57 ('repository.write', _('Write'),),
58 58 ('repository.admin', _('Admin'),)]
59 59 self.register_choices = [
60 ('hg.register.none', 'disabled'),
60 ('hg.register.none',
61 _('disabled')),
61 62 ('hg.register.manual_activate',
62 _('allowed with manual account activation')),
63 _('allowed with manual account activation')),
63 64 ('hg.register.auto_activate',
64 _('allowed with automatic account activation')), ]
65 _('allowed with automatic account activation')), ]
65 66
66 67 self.create_choices = [('hg.create.none', _('Disabled')),
67 68 ('hg.create.repository', _('Enabled'))]
68 69
69 70
70 71 def index(self, format='html'):
71 72 """GET /permissions: All items in the collection"""
72 73 # url('permissions')
73 74
74 75 def create(self):
75 76 """POST /permissions: Create a new item"""
76 77 # url('permissions')
77 78
78 79 def new(self, format='html'):
79 80 """GET /permissions/new: Form to create a new item"""
80 81 # url('new_permission')
81 82
82 83 def update(self, id):
83 84 """PUT /permissions/id: Update an existing item"""
84 85 # Forms posted to this method should contain a hidden field:
85 86 # <input type="hidden" name="_method" value="PUT" />
86 87 # Or using helpers:
87 88 # h.form(url('permission', id=ID),
88 89 # method='put')
89 90 # url('permission', id=ID)
90 91
91 92 permission_model = PermissionModel()
92 93
93 94 _form = DefaultPermissionsForm([x[0] for x in self.perms_choices],
94 95 [x[0] for x in self.register_choices],
95 96 [x[0] for x in self.create_choices])()
96 97
97 98 try:
98 99 form_result = _form.to_python(dict(request.POST))
99 100 form_result.update({'perm_user_name':id})
100 101 permission_model.update(form_result)
101 102 h.flash(_('Default permissions updated succesfully'),
102 103 category='success')
103 104
104 105 except formencode.Invalid, errors:
105 106 c.perms_choices = self.perms_choices
106 107 c.register_choices = self.register_choices
107 108 c.create_choices = self.create_choices
108 109
109 110 return htmlfill.render(
110 111 render('admin/permissions/permissions.html'),
111 112 defaults=errors.value,
112 113 errors=errors.error_dict or {},
113 114 prefix_error=False,
114 115 encoding="UTF-8")
115 116 except Exception:
116 117 log.error(traceback.format_exc())
117 118 h.flash(_('error occured during update of permissions'),
118 119 category='error')
119 120
120 121 return redirect(url('edit_permission', id=id))
121 122
122 123
123 124
124 125 def delete(self, id):
125 126 """DELETE /permissions/id: Delete an existing item"""
126 127 # Forms posted to this method should contain a hidden field:
127 128 # <input type="hidden" name="_method" value="DELETE" />
128 129 # Or using helpers:
129 130 # h.form(url('permission', id=ID),
130 131 # method='delete')
131 132 # url('permission', id=ID)
132 133
133 134 def show(self, id, format='html'):
134 135 """GET /permissions/id: Show a specific item"""
135 136 # url('permission', id=ID)
136 137
137 138 def edit(self, id, format='html'):
138 139 """GET /permissions/id/edit: Form to edit an existing item"""
139 140 #url('edit_permission', id=ID)
140 141 c.perms_choices = self.perms_choices
141 142 c.register_choices = self.register_choices
142 143 c.create_choices = self.create_choices
143 144
144 145 if id == 'default':
145 defaults = {'_method':'put'}
146 for p in UserModel().get_by_username('default').user_perms:
146 default_user = UserModel().get_by_username('default')
147 defaults = {'_method':'put',
148 'anonymous':default_user.active}
149 for p in default_user.user_perms:
147 150 if p.permission.permission_name.startswith('repository.'):
148 151 defaults['default_perm'] = p.permission.permission_name
149 152
150 153 if p.permission.permission_name.startswith('hg.register.'):
151 154 defaults['default_register'] = p.permission.permission_name
152 155
153 156 if p.permission.permission_name.startswith('hg.create.'):
154 157 defaults['default_create'] = p.permission.permission_name
155 158
156 159 return htmlfill.render(
157 160 render('admin/permissions/permissions.html'),
158 161 defaults=defaults,
159 162 encoding="UTF-8",
160 163 force_defaults=True,)
161 164 else:
162 165 return redirect(url('admin_home'))
@@ -1,312 +1,316 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # settings controller for pylons
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; version 2
9 9 # of the License or (at your opinion) any later version of the license.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 19 # MA 02110-1301, USA.
20 20 """
21 21 Created on July 14, 2010
22 22 settings controller for pylons
23 23 @author: marcink
24 24 """
25 25 from formencode import htmlfill
26 26 from pylons import request, session, tmpl_context as c, url, app_globals as g, \
27 27 config
28 28 from pylons.controllers.util import abort, redirect
29 29 from pylons.i18n.translation import _
30 30 from rhodecode.lib import helpers as h
31 31 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
32 32 HasPermissionAnyDecorator
33 33 from rhodecode.lib.base import BaseController, render
34 34 from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
35 35 set_rhodecode_config, get_hg_settings, get_hg_ui_settings
36 36 from rhodecode.model.db import RhodeCodeSettings, RhodeCodeUi, Repository
37 37 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
38 38 ApplicationUiSettingsForm
39 39 from rhodecode.model.hg import HgModel
40 40 from rhodecode.model.user import UserModel
41 41 from rhodecode.lib.celerylib import tasks, run_task
42 42 from sqlalchemy import func
43 43 import formencode
44 44 import logging
45 45 import traceback
46 46
47 47 log = logging.getLogger(__name__)
48 48
49 49
50 50 class SettingsController(BaseController):
51 51 """REST Controller styled on the Atom Publishing Protocol"""
52 52 # To properly map this controller, ensure your config/routing.py
53 53 # file has a resource setup:
54 54 # map.resource('setting', 'settings', controller='admin/settings',
55 55 # path_prefix='/admin', name_prefix='admin_')
56 56
57 57
58 58 @LoginRequired()
59 59 def __before__(self):
60 60 c.admin_user = session.get('admin_user')
61 61 c.admin_username = session.get('admin_username')
62 62 super(SettingsController, self).__before__()
63 63
64 64
65 65 @HasPermissionAllDecorator('hg.admin')
66 66 def index(self, format='html'):
67 67 """GET /admin/settings: All items in the collection"""
68 68 # url('admin_settings')
69 69
70 70 defaults = get_hg_settings()
71 71 defaults.update(get_hg_ui_settings())
72 72 return htmlfill.render(
73 73 render('admin/settings/settings.html'),
74 74 defaults=defaults,
75 75 encoding="UTF-8",
76 76 force_defaults=False
77 77 )
78 78
79 79 @HasPermissionAllDecorator('hg.admin')
80 80 def create(self):
81 81 """POST /admin/settings: Create a new item"""
82 82 # url('admin_settings')
83 83
84 84 @HasPermissionAllDecorator('hg.admin')
85 85 def new(self, format='html'):
86 86 """GET /admin/settings/new: Form to create a new item"""
87 87 # url('admin_new_setting')
88 88
89 89 @HasPermissionAllDecorator('hg.admin')
90 90 def update(self, setting_id):
91 91 """PUT /admin/settings/setting_id: Update an existing item"""
92 92 # Forms posted to this method should contain a hidden field:
93 93 # <input type="hidden" name="_method" value="PUT" />
94 94 # Or using helpers:
95 95 # h.form(url('admin_setting', setting_id=ID),
96 96 # method='put')
97 97 # url('admin_setting', setting_id=ID)
98 98 if setting_id == 'mapping':
99 99 rm_obsolete = request.POST.get('destroy', False)
100 100 log.debug('Rescanning directories with destroy=%s', rm_obsolete)
101 101
102 102 initial = HgModel().repo_scan(g.paths[0][1], g.baseui)
103 103 for repo_name in initial.keys():
104 104 invalidate_cache('get_repo_cached_%s' % repo_name)
105 105
106 106 repo2db_mapper(initial, rm_obsolete)
107 107
108 108 h.flash(_('Repositories successfully rescanned'), category='success')
109 109
110 110 if setting_id == 'whoosh':
111 111 repo_location = get_hg_ui_settings()['paths_root_path']
112 112 full_index = request.POST.get('full_index', False)
113 113 task = run_task(tasks.whoosh_index, repo_location, full_index)
114 114
115 115 h.flash(_('Whoosh reindex task scheduled'), category='success')
116 116 if setting_id == 'global':
117 117
118 118 application_form = ApplicationSettingsForm()()
119 119 try:
120 120 form_result = application_form.to_python(dict(request.POST))
121 121
122 122 try:
123 123 hgsettings1 = self.sa.query(RhodeCodeSettings)\
124 .filter(RhodeCodeSettings.app_settings_name == 'title').one()
124 .filter(RhodeCodeSettings.app_settings_name \
125 == 'title').one()
126
125 127 hgsettings1.app_settings_value = form_result['rhodecode_title']
126 128
127 129 hgsettings2 = self.sa.query(RhodeCodeSettings)\
128 .filter(RhodeCodeSettings.app_settings_name == 'realm').one()
130 .filter(RhodeCodeSettings.app_settings_name \
131 == 'realm').one()
132
129 133 hgsettings2.app_settings_value = form_result['rhodecode_realm']
130 134
131 135
132 136 self.sa.add(hgsettings1)
133 137 self.sa.add(hgsettings2)
134 138 self.sa.commit()
135 139 set_rhodecode_config(config)
136 140 h.flash(_('Updated application settings'),
137 141 category='success')
138 142
139 143 except:
140 144 log.error(traceback.format_exc())
141 145 h.flash(_('error occurred during updating application settings'),
142 146 category='error')
143 147
144 148 self.sa.rollback()
145 149
146 150
147 151 except formencode.Invalid, errors:
148 152 return htmlfill.render(
149 153 render('admin/settings/settings.html'),
150 154 defaults=errors.value,
151 155 errors=errors.error_dict or {},
152 156 prefix_error=False,
153 157 encoding="UTF-8")
154 158
155 159 if setting_id == 'mercurial':
156 160 application_form = ApplicationUiSettingsForm()()
157 161 try:
158 162 form_result = application_form.to_python(dict(request.POST))
159 163
160 164 try:
161 165
162 166 hgsettings1 = self.sa.query(RhodeCodeUi)\
163 167 .filter(RhodeCodeUi.ui_key == 'push_ssl').one()
164 168 hgsettings1.ui_value = form_result['web_push_ssl']
165 169
166 170 hgsettings2 = self.sa.query(RhodeCodeUi)\
167 171 .filter(RhodeCodeUi.ui_key == '/').one()
168 172 hgsettings2.ui_value = form_result['paths_root_path']
169 173
170 174
171 175 #HOOKS
172 176 hgsettings3 = self.sa.query(RhodeCodeUi)\
173 177 .filter(RhodeCodeUi.ui_key == 'changegroup.update').one()
174 178 hgsettings3.ui_active = bool(form_result['hooks_changegroup_update'])
175 179
176 180 hgsettings4 = self.sa.query(RhodeCodeUi)\
177 181 .filter(RhodeCodeUi.ui_key == 'changegroup.repo_size').one()
178 182 hgsettings4.ui_active = bool(form_result['hooks_changegroup_repo_size'])
179 183
180 184 hgsettings5 = self.sa.query(RhodeCodeUi)\
181 185 .filter(RhodeCodeUi.ui_key == 'pretxnchangegroup.push_logger').one()
182 186 hgsettings5.ui_active = bool(form_result['hooks_pretxnchangegroup_push_logger'])
183 187
184 188 hgsettings6 = self.sa.query(RhodeCodeUi)\
185 189 .filter(RhodeCodeUi.ui_key == 'preoutgoing.pull_logger').one()
186 190 hgsettings6.ui_active = bool(form_result['hooks_preoutgoing_pull_logger'])
187 191
188 192
189 193 self.sa.add(hgsettings1)
190 194 self.sa.add(hgsettings2)
191 195 self.sa.add(hgsettings3)
192 196 self.sa.add(hgsettings4)
193 197 self.sa.add(hgsettings5)
194 198 self.sa.add(hgsettings6)
195 199 self.sa.commit()
196 200
197 201 h.flash(_('Updated mercurial settings'),
198 202 category='success')
199 203
200 204 except:
201 205 log.error(traceback.format_exc())
202 206 h.flash(_('error occurred during updating application settings'),
203 207 category='error')
204 208
205 209 self.sa.rollback()
206 210
207 211
208 212 except formencode.Invalid, errors:
209 213 return htmlfill.render(
210 214 render('admin/settings/settings.html'),
211 215 defaults=errors.value,
212 216 errors=errors.error_dict or {},
213 217 prefix_error=False,
214 218 encoding="UTF-8")
215 219
216 220
217 221
218 222 return redirect(url('admin_settings'))
219 223
220 224 @HasPermissionAllDecorator('hg.admin')
221 225 def delete(self, setting_id):
222 226 """DELETE /admin/settings/setting_id: Delete an existing item"""
223 227 # Forms posted to this method should contain a hidden field:
224 228 # <input type="hidden" name="_method" value="DELETE" />
225 229 # Or using helpers:
226 230 # h.form(url('admin_setting', setting_id=ID),
227 231 # method='delete')
228 232 # url('admin_setting', setting_id=ID)
229 233
230 234 @HasPermissionAllDecorator('hg.admin')
231 235 def show(self, setting_id, format='html'):
232 236 """GET /admin/settings/setting_id: Show a specific item"""
233 237 # url('admin_setting', setting_id=ID)
234 238
235 239 @HasPermissionAllDecorator('hg.admin')
236 240 def edit(self, setting_id, format='html'):
237 241 """GET /admin/settings/setting_id/edit: Form to edit an existing item"""
238 242 # url('admin_edit_setting', setting_id=ID)
239 243
240 244
241 245 def my_account(self):
242 246 """
243 247 GET /_admin/my_account Displays info about my account
244 248 """
245 249
246 250 # url('admin_settings_my_account')
247 251 c.user = UserModel(self.sa).get(c.rhodecode_user.user_id, cache=False)
248 252 all_repos = self.sa.query(Repository)\
249 253 .filter(Repository.user_id == c.user.user_id)\
250 254 .order_by(func.lower(Repository.repo_name))\
251 255 .all()
252 256 c.user_repos = HgModel().get_repos(all_repos)
253 257
254 258 if c.user.username == 'default':
255 259 h.flash(_("You can't edit this user since it's"
256 260 " crucial for entire application"), category='warning')
257 261 return redirect(url('users'))
258 262
259 263 defaults = c.user.__dict__
260 264 return htmlfill.render(
261 265 render('admin/users/user_edit_my_account.html'),
262 266 defaults=defaults,
263 267 encoding="UTF-8",
264 268 force_defaults=False
265 269 )
266 270
267 271 def my_account_update(self):
268 272 """PUT /_admin/my_account_update: Update an existing item"""
269 273 # Forms posted to this method should contain a hidden field:
270 274 # <input type="hidden" name="_method" value="PUT" />
271 275 # Or using helpers:
272 276 # h.form(url('admin_settings_my_account_update'),
273 277 # method='put')
274 278 # url('admin_settings_my_account_update', id=ID)
275 279 user_model = UserModel()
276 280 uid = c.rhodecode_user.user_id
277 281 _form = UserForm(edit=True, old_data={'user_id':uid,
278 282 'email':c.rhodecode_user.email})()
279 283 form_result = {}
280 284 try:
281 285 form_result = _form.to_python(dict(request.POST))
282 286 user_model.update_my_account(uid, form_result)
283 287 h.flash(_('Your account was updated succesfully'),
284 288 category='success')
285 289
286 290 except formencode.Invalid, errors:
287 291 c.user = user_model.get(c.rhodecode_user.user_id, cache=False)
288 292 c.user_repos = []
289 293 for repo in c.cached_repo_list.values():
290 294 if repo.dbrepo.user.username == c.user.username:
291 295 c.user_repos.append(repo)
292 296 return htmlfill.render(
293 297 render('admin/users/user_edit_my_account.html'),
294 298 defaults=errors.value,
295 299 errors=errors.error_dict or {},
296 300 prefix_error=False,
297 301 encoding="UTF-8")
298 302 except Exception:
299 303 log.error(traceback.format_exc())
300 304 h.flash(_('error occured during update of user %s') \
301 305 % form_result.get('username'), category='error')
302 306
303 307 return redirect(url('my_account'))
304 308
305 309 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
306 310 def create_repository(self):
307 311 """GET /_admin/create_repository: Form to create a new item"""
308 312 new_repo = request.GET.get('repo', '')
309 313 c.new_repo = h.repo_name_slug(new_repo)
310 314
311 315 return render('admin/repos/repo_add_create_repository.html')
312 316
@@ -1,168 +1,167 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # users controller for pylons
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; version 2
9 9 # of the License or (at your opinion) any later version of the license.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 19 # MA 02110-1301, USA.
20 20 from rhodecode.lib.utils import action_logger
21 21 """
22 22 Created on April 4, 2010
23 23 users controller for pylons
24 24 @author: marcink
25 25 """
26 26
27 27 from formencode import htmlfill
28 28 from pylons import request, session, tmpl_context as c, url
29 29 from pylons.controllers.util import abort, redirect
30 30 from pylons.i18n.translation import _
31 31 from rhodecode.lib import helpers as h
32 32 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
33 33 from rhodecode.lib.base import BaseController, render
34 34 from rhodecode.model.db import User, UserLog
35 35 from rhodecode.model.forms import UserForm
36 36 from rhodecode.model.user import UserModel, DefaultUserException
37 37 import formencode
38 38 import logging
39 39 import traceback
40 40
41 41 log = logging.getLogger(__name__)
42 42
43 43 class UsersController(BaseController):
44 44 """REST Controller styled on the Atom Publishing Protocol"""
45 45 # To properly map this controller, ensure your config/routing.py
46 46 # file has a resource setup:
47 47 # map.resource('user', 'users')
48
48
49 49 @LoginRequired()
50 50 @HasPermissionAllDecorator('hg.admin')
51 51 def __before__(self):
52 52 c.admin_user = session.get('admin_user')
53 53 c.admin_username = session.get('admin_username')
54 54 super(UsersController, self).__before__()
55
55
56 56
57 57 def index(self, format='html'):
58 58 """GET /users: All items in the collection"""
59 59 # url('users')
60
61 c.users_list = self.sa.query(User).all()
60
61 c.users_list = self.sa.query(User).all()
62 62 return render('admin/users/users.html')
63
63
64 64 def create(self):
65 65 """POST /users: Create a new item"""
66 66 # url('users')
67
67
68 68 user_model = UserModel()
69 69 login_form = UserForm()()
70 70 try:
71 71 form_result = login_form.to_python(dict(request.POST))
72 72 user_model.create(form_result)
73 73 h.flash(_('created user %s') % form_result['username'],
74 74 category='success')
75 75 #action_logger(self.rhodecode_user, 'new_user', '', '', self.sa)
76 76 except formencode.Invalid, errors:
77 77 return htmlfill.render(
78 78 render('admin/users/user_add.html'),
79 79 defaults=errors.value,
80 80 errors=errors.error_dict or {},
81 81 prefix_error=False,
82 encoding="UTF-8")
82 encoding="UTF-8")
83 83 except Exception:
84 84 log.error(traceback.format_exc())
85 85 h.flash(_('error occured during creation of user %s') \
86 % request.POST.get('username'), category='error')
86 % request.POST.get('username'), category='error')
87 87 return redirect(url('users'))
88
88
89 89 def new(self, format='html'):
90 90 """GET /users/new: Form to create a new item"""
91 91 # url('new_user')
92 92 return render('admin/users/user_add.html')
93 93
94 94 def update(self, id):
95 95 """PUT /users/id: Update an existing item"""
96 96 # Forms posted to this method should contain a hidden field:
97 97 # <input type="hidden" name="_method" value="PUT" />
98 98 # Or using helpers:
99 99 # h.form(url('user', id=ID),
100 100 # method='put')
101 101 # url('user', id=ID)
102 102 user_model = UserModel()
103 103 c.user = user_model.get(id)
104
104
105 105 _form = UserForm(edit=True, old_data={'user_id':id,
106 106 'email':c.user.email})()
107 107 form_result = {}
108 108 try:
109 109 form_result = _form.to_python(dict(request.POST))
110 110 user_model.update(id, form_result)
111 111 h.flash(_('User updated succesfully'), category='success')
112
112
113 113 except formencode.Invalid, errors:
114 114 return htmlfill.render(
115 115 render('admin/users/user_edit.html'),
116 116 defaults=errors.value,
117 117 errors=errors.error_dict or {},
118 118 prefix_error=False,
119 encoding="UTF-8")
119 encoding="UTF-8")
120 120 except Exception:
121 121 log.error(traceback.format_exc())
122 122 h.flash(_('error occured during update of user %s') \
123 123 % form_result.get('username'), category='error')
124
124
125 125 return redirect(url('users'))
126
126
127 127 def delete(self, id):
128 128 """DELETE /users/id: Delete an existing item"""
129 129 # Forms posted to this method should contain a hidden field:
130 130 # <input type="hidden" name="_method" value="DELETE" />
131 131 # Or using helpers:
132 132 # h.form(url('user', id=ID),
133 133 # method='delete')
134 134 # url('user', id=ID)
135 135 user_model = UserModel()
136 136 try:
137 137 user_model.delete(id)
138 138 h.flash(_('sucessfully deleted user'), category='success')
139 139 except DefaultUserException, e:
140 140 h.flash(str(e), category='warning')
141 141 except Exception:
142 142 h.flash(_('An error occured during deletion of user'),
143 category='error')
143 category='error')
144 144 return redirect(url('users'))
145
145
146 146 def show(self, id, format='html'):
147 147 """GET /users/id: Show a specific item"""
148 148 # url('user', id=ID)
149
150
149
150
151 151 def edit(self, id, format='html'):
152 152 """GET /users/id/edit: Form to edit an existing item"""
153 153 # url('edit_user', id=ID)
154 154 c.user = self.sa.query(User).get(id)
155 155 if not c.user:
156 156 return redirect(url('users'))
157 157 if c.user.username == 'default':
158 h.flash(_("You can't edit this user since it's"
159 " crucial for entire application"), category='warning')
158 h.flash(_("You can't edit this user"), category='warning')
160 159 return redirect(url('users'))
161
160
162 161 defaults = c.user.__dict__
163 162 return htmlfill.render(
164 163 render('admin/users/user_edit.html'),
165 164 defaults=defaults,
166 165 encoding="UTF-8",
167 166 force_defaults=False
168 )
167 )
@@ -1,144 +1,146 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # login controller for pylons
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; version 2
9 9 # of the License or (at your opinion) any later version of the license.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 19 # MA 02110-1301, USA.
20 20
21 21 """
22 22 Created on April 22, 2010
23 23 login controller for pylons
24 24 @author: marcink
25 25 """
26 26 from formencode import htmlfill
27 27 from pylons import request, response, session, tmpl_context as c, url
28 28 from pylons.controllers.util import abort, redirect
29 29 from rhodecode.lib.auth import AuthUser, HasPermissionAnyDecorator
30 30 from rhodecode.lib.base import BaseController, render
31 31 import rhodecode.lib.helpers as h
32 32 from pylons.i18n.translation import _
33 33 from rhodecode.model.forms import LoginForm, RegisterForm, PasswordResetForm
34 34 from rhodecode.model.user import UserModel
35 35 import formencode
36 36 import logging
37 37
38 38 log = logging.getLogger(__name__)
39 39
40 40 class LoginController(BaseController):
41 41
42 42 def __before__(self):
43 43 super(LoginController, self).__before__()
44 44
45 45 def index(self):
46 46 #redirect if already logged in
47 47 c.came_from = request.GET.get('came_from', None)
48 48
49 if c.rhodecode_user.is_authenticated:
49 if c.rhodecode_user.is_authenticated \
50 and c.rhodecode_user.username != 'default':
51
50 52 return redirect(url('home'))
51 53
52 54 if request.POST:
53 55 #import Login Form validator class
54 56 login_form = LoginForm()
55 57 try:
56 58 c.form_result = login_form.to_python(dict(request.POST))
57 59 username = c.form_result['username']
58 60 user = UserModel().get_by_username(username)
59 61 auth_user = AuthUser()
60 62 auth_user.username = user.username
61 63 auth_user.is_authenticated = True
62 64 auth_user.is_admin = user.admin
63 65 auth_user.user_id = user.user_id
64 66 auth_user.name = user.name
65 67 auth_user.lastname = user.lastname
66 68 session['rhodecode_user'] = auth_user
67 69 session.save()
68 70 log.info('user %s is now authenticated', username)
69 71
70 72 user.update_lastlogin()
71 73
72 74 if c.came_from:
73 75 return redirect(c.came_from)
74 76 else:
75 77 return redirect(url('home'))
76 78
77 79 except formencode.Invalid, errors:
78 80 return htmlfill.render(
79 81 render('/login.html'),
80 82 defaults=errors.value,
81 83 errors=errors.error_dict or {},
82 84 prefix_error=False,
83 85 encoding="UTF-8")
84 86
85 87 return render('/login.html')
86 88
87 89 @HasPermissionAnyDecorator('hg.admin', 'hg.register.auto_activate',
88 90 'hg.register.manual_activate')
89 91 def register(self):
90 92 user_model = UserModel()
91 93 c.auto_active = False
92 94 for perm in user_model.get_by_username('default', cache=False).user_perms:
93 95 if perm.permission.permission_name == 'hg.register.auto_activate':
94 96 c.auto_active = True
95 97 break
96 98
97 99 if request.POST:
98 100
99 101 register_form = RegisterForm()()
100 102 try:
101 103 form_result = register_form.to_python(dict(request.POST))
102 104 form_result['active'] = c.auto_active
103 105 user_model.create_registration(form_result)
104 106 h.flash(_('You have successfully registered into rhodecode'),
105 107 category='success')
106 108 return redirect(url('login_home'))
107 109
108 110 except formencode.Invalid, errors:
109 111 return htmlfill.render(
110 112 render('/register.html'),
111 113 defaults=errors.value,
112 114 errors=errors.error_dict or {},
113 115 prefix_error=False,
114 116 encoding="UTF-8")
115 117
116 118 return render('/register.html')
117 119
118 120 def password_reset(self):
119 121 user_model = UserModel()
120 122 if request.POST:
121 123
122 124 password_reset_form = PasswordResetForm()()
123 125 try:
124 126 form_result = password_reset_form.to_python(dict(request.POST))
125 127 user_model.reset_password(form_result)
126 128 h.flash(_('Your new password was sent'),
127 129 category='success')
128 130 return redirect(url('login_home'))
129 131
130 132 except formencode.Invalid, errors:
131 133 return htmlfill.render(
132 134 render('/password_reset.html'),
133 135 defaults=errors.value,
134 136 errors=errors.error_dict or {},
135 137 prefix_error=False,
136 138 encoding="UTF-8")
137 139
138 140 return render('/password_reset.html')
139 141
140 142 def logout(self):
141 143 session['rhodecode_user'] = AuthUser()
142 144 session.save()
143 145 log.info('Logging out and setting user as Empty')
144 146 redirect(url('home'))
@@ -1,486 +1,475 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # authentication and permission libraries
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; version 2
9 9 # of the License or (at your opinion) any later version of the license.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 19 # MA 02110-1301, USA.
20 20 """
21 21 Created on April 4, 2010
22 22
23 23 @author: marcink
24 24 """
25 25 from pylons import config, session, url, request
26 26 from pylons.controllers.util import abort, redirect
27 27 from rhodecode.lib.utils import get_repo_slug
28 28 from rhodecode.model import meta
29 from rhodecode.model.user import UserModel
29 30 from rhodecode.model.caching_query import FromCache
30 31 from rhodecode.model.db import User, RepoToPerm, Repository, Permission, \
31 UserToPerm
32 UserToPerm
32 33 import bcrypt
33 34 from decorator import decorator
34 35 import logging
35 36 import random
36 37
37 log = logging.getLogger(__name__)
38 log = logging.getLogger(__name__)
38 39
39 40 class PasswordGenerator(object):
40 41 """This is a simple class for generating password from
41 42 different sets of characters
42 43 usage:
43 44 passwd_gen = PasswordGenerator()
44 45 #print 8-letter password containing only big and small letters of alphabet
45 46 print passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
46 47 """
47 48 ALPHABETS_NUM = r'''1234567890'''#[0]
48 49 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''#[1]
49 50 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''#[2]
50 51 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?''' #[3]
51 52 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM + ALPHABETS_SPECIAL#[4]
52 53 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM#[5]
53 54 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
54 55 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM#[6]
55 56 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM#[7]
56
57
57 58 def __init__(self, passwd=''):
58 59 self.passwd = passwd
59 60
60 61 def gen_password(self, len, type):
61 62 self.passwd = ''.join([random.choice(type) for _ in xrange(len)])
62 63 return self.passwd
63 64
64
65
65 66 def get_crypt_password(password):
66 67 """Cryptographic function used for password hashing based on sha1
67 68 :param password: password to hash
68 """
69 """
69 70 return bcrypt.hashpw(password, bcrypt.gensalt(10))
70 71
71 72 def check_password(password, hashed):
72 73 return bcrypt.hashpw(password, hashed) == hashed
73 74
74 75 def authfunc(environ, username, password):
75 from rhodecode.model.user import UserModel
76 76 user = UserModel().get_by_username(username, cache=False)
77
77
78 78 if user:
79 79 if user.active:
80 80 if user.username == username and check_password(password, user.password):
81 81 log.info('user %s authenticated correctly', username)
82 82 return True
83 83 else:
84 84 log.error('user %s is disabled', username)
85
85
86 86 return False
87 87
88 88 class AuthUser(object):
89 89 """
90 90 A simple object that handles a mercurial username for authentication
91 91 """
92 92 def __init__(self):
93 93 self.username = 'None'
94 94 self.name = ''
95 95 self.lastname = ''
96 96 self.email = ''
97 97 self.user_id = None
98 98 self.is_authenticated = False
99 99 self.is_admin = False
100 100 self.permissions = {}
101 101
102 def __repr__(self):
103 return "<AuthUser('id:%s:%s')>" % (self.user_id, self.username)
102 104
103 105 def set_available_permissions(config):
104 106 """
105 107 This function will propagate pylons globals with all available defined
106 108 permission given in db. We don't wannt to check each time from db for new
107 109 permissions since adding a new permission also requires application restart
108 110 ie. to decorate new views with the newly created permission
109 111 :param config:
110 112 """
111 113 log.info('getting information about all available permissions')
112 114 try:
113 115 sa = meta.Session()
114 116 all_perms = sa.query(Permission).all()
115 117 except:
116 118 pass
117 119 finally:
118 120 meta.Session.remove()
119
121
120 122 config['available_permissions'] = [x.permission_name for x in all_perms]
121 123
122 124 def set_base_path(config):
123 125 config['base_path'] = config['pylons.app_globals'].base_path
124 126
125 def fill_data(user):
126 """
127 Fills user data with those from database and log out user if not present
128 in database
129 :param user:
130 """
131 sa = meta.Session()
132 try:
133 dbuser = sa.query(User)\
134 .options(FromCache('sql_cache_short', 'getuser_%s' % user.user_id))\
135 .get(user.user_id)
136 except:
137 pass
138 finally:
139 meta.Session.remove()
140
141 if dbuser:
142 user.username = dbuser.username
143 user.is_admin = dbuser.admin
144 user.name = dbuser.name
145 user.lastname = dbuser.lastname
146 user.email = dbuser.email
147 else:
148 user.is_authenticated = False
149
150
151 return user
152
127
153 128 def fill_perms(user):
154 129 """
155 130 Fills user permission attribute with permissions taken from database
156 131 :param user:
157 132 """
158
133
159 134 sa = meta.Session()
160 135 user.permissions['repositories'] = {}
161 136 user.permissions['global'] = set()
162
137
163 138 #===========================================================================
164 139 # fetch default permissions
165 140 #===========================================================================
166 default_user = sa.query(User)\
167 .options(FromCache('sql_cache_short', 'getuser_%s' % 'default'))\
168 .filter(User.username == 'default').scalar()
169
141 default_user = UserModel(sa).get_by_username('default', cache=True)
142
170 143 default_perms = sa.query(RepoToPerm, Repository, Permission)\
171 144 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
172 145 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
173 146 .filter(RepoToPerm.user == default_user).all()
174
147
175 148 if user.is_admin:
176 149 #=======================================================================
177 150 # #admin have all default rights set to admin
178 151 #=======================================================================
179 152 user.permissions['global'].add('hg.admin')
180
153
181 154 for perm in default_perms:
182 155 p = 'repository.admin'
183 156 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
184
157
185 158 else:
186 159 #=======================================================================
187 160 # set default permissions
188 161 #=======================================================================
189
162
190 163 #default global
191 164 default_global_perms = sa.query(UserToPerm)\
192 .filter(UserToPerm.user == sa.query(User).filter(User.username ==
165 .filter(UserToPerm.user == sa.query(User).filter(User.username ==
193 166 'default').one())
194
167
195 168 for perm in default_global_perms:
196 169 user.permissions['global'].add(perm.permission.permission_name)
197
170
198 171 #default repositories
199 172 for perm in default_perms:
200 173 if perm.Repository.private and not perm.Repository.user_id == user.user_id:
201 174 #disable defaults for private repos,
202 175 p = 'repository.none'
203 176 elif perm.Repository.user_id == user.user_id:
204 177 #set admin if owner
205 178 p = 'repository.admin'
206 179 else:
207 180 p = perm.Permission.permission_name
208
181
209 182 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
210
183
211 184 #=======================================================================
212 185 # #overwrite default with user permissions if any
213 186 #=======================================================================
214 187 user_perms = sa.query(RepoToPerm, Permission, Repository)\
215 188 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
216 189 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
217 190 .filter(RepoToPerm.user_id == user.user_id).all()
218
191
219 192 for perm in user_perms:
220 193 if perm.Repository.user_id == user.user_id:#set admin if owner
221 194 p = 'repository.admin'
222 195 else:
223 196 p = perm.Permission.permission_name
224 197 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
225 meta.Session.remove()
198 meta.Session.remove()
226 199 return user
227
200
228 201 def get_user(session):
229 202 """
230 203 Gets user from session, and wraps permissions into user
231 204 :param session:
232 205 """
233 206 user = session.get('rhodecode_user', AuthUser())
207
208
209 #if the user is not logged in we check for anonymous access
210 #if user is logged and it's a default user check if we still have anonymous
211 #access enabled
212 if user.user_id is None or user.username == 'default':
213 anonymous_user = UserModel().get_by_username('default', cache=True)
214 if anonymous_user.active is True:
215 #then we set this user is logged in
216 user.is_authenticated = True
217 else:
218 user.is_authenticated = False
219
234 220 if user.is_authenticated:
235 user = fill_data(user)
221 user = UserModel().fill_data(user)
222
236 223 user = fill_perms(user)
237 224 session['rhodecode_user'] = user
238 225 session.save()
239 226 return user
240
227
241 228 #===============================================================================
242 229 # CHECK DECORATORS
243 230 #===============================================================================
244 231 class LoginRequired(object):
245 232 """Must be logged in to execute this function else redirect to login page"""
246
233
247 234 def __call__(self, func):
248 235 return decorator(self.__wrapper, func)
249
236
250 237 def __wrapper(self, func, *fargs, **fkwargs):
251 238 user = session.get('rhodecode_user', AuthUser())
252 239 log.debug('Checking login required for user:%s', user.username)
253 240 if user.is_authenticated:
254 241 log.debug('user %s is authenticated', user.username)
255 242 return func(*fargs, **fkwargs)
256 243 else:
257 244 log.warn('user %s not authenticated', user.username)
258
245
259 246 p = ''
260 247 if request.environ.get('SCRIPT_NAME') != '/':
261 248 p += request.environ.get('SCRIPT_NAME')
262
249
263 250 p += request.environ.get('PATH_INFO')
264 251 if request.environ.get('QUERY_STRING'):
265 252 p += '?' + request.environ.get('QUERY_STRING')
266
267 log.debug('redirecting to login page with %s', p)
253
254 log.debug('redirecting to login page with %s', p)
268 255 return redirect(url('login_home', came_from=p))
269 256
270 257 class PermsDecorator(object):
271 258 """Base class for decorators"""
272
259
273 260 def __init__(self, *required_perms):
274 261 available_perms = config['available_permissions']
275 262 for perm in required_perms:
276 263 if perm not in available_perms:
277 264 raise Exception("'%s' permission is not defined" % perm)
278 265 self.required_perms = set(required_perms)
279 266 self.user_perms = None
280
267
281 268 def __call__(self, func):
282 269 return decorator(self.__wrapper, func)
283
284
270
271
285 272 def __wrapper(self, func, *fargs, **fkwargs):
286 273 # _wrapper.__name__ = func.__name__
287 274 # _wrapper.__dict__.update(func.__dict__)
288 275 # _wrapper.__doc__ = func.__doc__
276 self.user = session.get('rhodecode_user', AuthUser())
277 self.user_perms = self.user.permissions
278 log.debug('checking %s permissions %s for %s %s',
279 self.__class__.__name__, self.required_perms, func.__name__,
280 self.user)
289 281
290 self.user_perms = session.get('rhodecode_user', AuthUser()).permissions
291 log.debug('checking %s permissions %s for %s',
292 self.__class__.__name__, self.required_perms, func.__name__)
293
294 282 if self.check_permissions():
295 log.debug('Permission granted for %s', func.__name__)
296
283 log.debug('Permission granted for %s %s', func.__name__, self.user)
284
297 285 return func(*fargs, **fkwargs)
298
286
299 287 else:
300 log.warning('Permission denied for %s', func.__name__)
288 log.warning('Permission denied for %s %s', func.__name__, self.user)
301 289 #redirect with forbidden ret code
302 290 return abort(403)
303 291
304
305
292
293
306 294 def check_permissions(self):
307 295 """Dummy function for overriding"""
308 296 raise Exception('You have to write this function in child class')
309 297
310 298 class HasPermissionAllDecorator(PermsDecorator):
311 299 """Checks for access permission for all given predicates. All of them
312 300 have to be meet in order to fulfill the request
313 301 """
314
302
315 303 def check_permissions(self):
316 304 if self.required_perms.issubset(self.user_perms.get('global')):
317 305 return True
318 306 return False
319
307
320 308
321 309 class HasPermissionAnyDecorator(PermsDecorator):
322 310 """Checks for access permission for any of given predicates. In order to
323 311 fulfill the request any of predicates must be meet
324 312 """
325
313
326 314 def check_permissions(self):
327 315 if self.required_perms.intersection(self.user_perms.get('global')):
328 316 return True
329 317 return False
330 318
331 319 class HasRepoPermissionAllDecorator(PermsDecorator):
332 320 """Checks for access permission for all given predicates for specific
333 321 repository. All of them have to be meet in order to fulfill the request
334 322 """
335
323
336 324 def check_permissions(self):
337 325 repo_name = get_repo_slug(request)
338 326 try:
339 327 user_perms = set([self.user_perms['repositories'][repo_name]])
340 328 except KeyError:
341 329 return False
342 330 if self.required_perms.issubset(user_perms):
343 331 return True
344 332 return False
345
333
346 334
347 335 class HasRepoPermissionAnyDecorator(PermsDecorator):
348 336 """Checks for access permission for any of given predicates for specific
349 337 repository. In order to fulfill the request any of predicates must be meet
350 338 """
351
339
352 340 def check_permissions(self):
353 341 repo_name = get_repo_slug(request)
354
342
355 343 try:
356 344 user_perms = set([self.user_perms['repositories'][repo_name]])
357 345 except KeyError:
358 346 return False
359 347 if self.required_perms.intersection(user_perms):
360 348 return True
361 349 return False
362 350 #===============================================================================
363 351 # CHECK FUNCTIONS
364 352 #===============================================================================
365 353
366 354 class PermsFunction(object):
367 355 """Base function for other check functions"""
368
356
369 357 def __init__(self, *perms):
370 358 available_perms = config['available_permissions']
371
359
372 360 for perm in perms:
373 361 if perm not in available_perms:
374 362 raise Exception("'%s' permission in not defined" % perm)
375 363 self.required_perms = set(perms)
376 364 self.user_perms = None
377 365 self.granted_for = ''
378 366 self.repo_name = None
379
367
380 368 def __call__(self, check_Location=''):
381 369 user = session.get('rhodecode_user', False)
382 370 if not user:
383 371 return False
384 372 self.user_perms = user.permissions
385 self.granted_for = user.username
386 log.debug('checking %s %s', self.__class__.__name__, self.required_perms)
387
373 self.granted_for = user.username
374 log.debug('checking %s %s %s', self.__class__.__name__,
375 self.required_perms, user)
376
388 377 if self.check_permissions():
389 log.debug('Permission granted for %s @%s', self.granted_for,
390 check_Location)
378 log.debug('Permission granted for %s @ %s %s', self.granted_for,
379 check_Location, user)
391 380 return True
392
381
393 382 else:
394 log.warning('Permission denied for %s @%s', self.granted_for,
395 check_Location)
396 return False
397
383 log.warning('Permission denied for %s @ %s %s', self.granted_for,
384 check_Location, user)
385 return False
386
398 387 def check_permissions(self):
399 388 """Dummy function for overriding"""
400 389 raise Exception('You have to write this function in child class')
401
390
402 391 class HasPermissionAll(PermsFunction):
403 392 def check_permissions(self):
404 393 if self.required_perms.issubset(self.user_perms.get('global')):
405 394 return True
406 395 return False
407 396
408 397 class HasPermissionAny(PermsFunction):
409 398 def check_permissions(self):
410 399 if self.required_perms.intersection(self.user_perms.get('global')):
411 400 return True
412 401 return False
413 402
414 403 class HasRepoPermissionAll(PermsFunction):
415
404
416 405 def __call__(self, repo_name=None, check_Location=''):
417 406 self.repo_name = repo_name
418 407 return super(HasRepoPermissionAll, self).__call__(check_Location)
419
408
420 409 def check_permissions(self):
421 410 if not self.repo_name:
422 411 self.repo_name = get_repo_slug(request)
423 412
424 413 try:
425 414 self.user_perms = set([self.user_perms['repositories']\
426 415 [self.repo_name]])
427 416 except KeyError:
428 417 return False
429 self.granted_for = self.repo_name
418 self.granted_for = self.repo_name
430 419 if self.required_perms.issubset(self.user_perms):
431 420 return True
432 421 return False
433
422
434 423 class HasRepoPermissionAny(PermsFunction):
435
424
436 425 def __call__(self, repo_name=None, check_Location=''):
437 426 self.repo_name = repo_name
438 427 return super(HasRepoPermissionAny, self).__call__(check_Location)
439
428
440 429 def check_permissions(self):
441 430 if not self.repo_name:
442 431 self.repo_name = get_repo_slug(request)
443 432
444 433 try:
445 434 self.user_perms = set([self.user_perms['repositories']\
446 435 [self.repo_name]])
447 436 except KeyError:
448 437 return False
449 438 self.granted_for = self.repo_name
450 439 if self.required_perms.intersection(self.user_perms):
451 440 return True
452 441 return False
453 442
454 443 #===============================================================================
455 444 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
456 445 #===============================================================================
457 446
458 447 class HasPermissionAnyMiddleware(object):
459 448 def __init__(self, *perms):
460 449 self.required_perms = set(perms)
461
450
462 451 def __call__(self, user, repo_name):
463 452 usr = AuthUser()
464 453 usr.user_id = user.user_id
465 454 usr.username = user.username
466 455 usr.is_admin = user.admin
467
456
468 457 try:
469 458 self.user_perms = set([fill_perms(usr)\
470 459 .permissions['repositories'][repo_name]])
471 460 except:
472 461 self.user_perms = set()
473 462 self.granted_for = ''
474 463 self.username = user.username
475 self.repo_name = repo_name
464 self.repo_name = repo_name
476 465 return self.check_permissions()
477
466
478 467 def check_permissions(self):
479 468 log.debug('checking mercurial protocol '
480 469 'permissions for user:%s repository:%s',
481 470 self.username, self.repo_name)
482 471 if self.required_perms.intersection(self.user_perms):
483 472 log.debug('permission granted')
484 473 return True
485 474 log.debug('permission denied')
486 475 return False
@@ -1,300 +1,300 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # database management for RhodeCode
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; version 2
9 9 # of the License or (at your opinion) any later version of the license.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 19 # MA 02110-1301, USA.
20 20
21 21 """
22 22 Created on April 10, 2010
23 23 database management and creation for RhodeCode
24 24 @author: marcink
25 25 """
26 26
27 27 from os.path import dirname as dn, join as jn
28 28 import os
29 29 import sys
30 30 import uuid
31 31
32 32 from rhodecode.lib.auth import get_crypt_password
33 33 from rhodecode.lib.utils import ask_ok
34 34 from rhodecode.model import init_model
35 35 from rhodecode.model.db import User, Permission, RhodeCodeUi, RhodeCodeSettings, \
36 36 UserToPerm
37 37 from rhodecode.model import meta
38 38 from sqlalchemy.engine import create_engine
39 39 import logging
40 40
41 41 log = logging.getLogger(__name__)
42 42
43 43 class DbManage(object):
44 44 def __init__(self, log_sql, dbname, root, tests=False):
45 45 self.dbname = dbname
46 46 self.tests = tests
47 47 self.root = root
48 48 dburi = 'sqlite:////%s' % jn(self.root, self.dbname)
49 49 engine = create_engine(dburi, echo=log_sql)
50 50 init_model(engine)
51 51 self.sa = meta.Session()
52 52 self.db_exists = False
53 53
54 54 def check_for_db(self, override):
55 55 db_path = jn(self.root, self.dbname)
56 56 log.info('checking for existing db in %s', db_path)
57 57 if os.path.isfile(db_path):
58 58 self.db_exists = True
59 59 if not override:
60 60 raise Exception('database already exists')
61 61
62 62 def create_tables(self, override=False):
63 63 """
64 64 Create a auth database
65 65 """
66 66 self.check_for_db(override)
67 67 if self.db_exists:
68 68 log.info("database exist and it's going to be destroyed")
69 69 if self.tests:
70 70 destroy = True
71 71 else:
72 72 destroy = ask_ok('Are you sure to destroy old database ? [y/n]')
73 73 if not destroy:
74 74 sys.exit()
75 75 if self.db_exists and destroy:
76 76 os.remove(jn(self.root, self.dbname))
77 77 checkfirst = not override
78 78 meta.Base.metadata.create_all(checkfirst=checkfirst)
79 79 log.info('Created tables for %s', self.dbname)
80 80
81 81 def admin_prompt(self, second=False):
82 82 if not self.tests:
83 83 import getpass
84 84
85 85
86 86 def get_password():
87 87 password = getpass.getpass('Specify admin password (min 6 chars):')
88 88 confirm = getpass.getpass('Confirm password:')
89 89
90 90 if password != confirm:
91 91 log.error('passwords mismatch')
92 92 return False
93 93 if len(password) < 6:
94 94 log.error('password is to short use at least 6 characters')
95 95 return False
96 96
97 97 return password
98 98
99 99 username = raw_input('Specify admin username:')
100 100
101 101 password = get_password()
102 102 if not password:
103 103 #second try
104 104 password = get_password()
105 105 if not password:
106 106 sys.exit()
107 107
108 108 email = raw_input('Specify admin email:')
109 109 self.create_user(username, password, email, True)
110 110 else:
111 111 log.info('creating admin and regular test users')
112 112 self.create_user('test_admin', 'test12', 'test_admin@mail.com', True)
113 113 self.create_user('test_regular', 'test12', 'test_regular@mail.com', False)
114 114 self.create_user('test_regular2', 'test12', 'test_regular2@mail.com', False)
115 115
116 116
117 117
118 118 def config_prompt(self, test_repo_path=''):
119 119 log.info('Setting up repositories config')
120 120
121 121 if not self.tests and not test_repo_path:
122 122 path = raw_input('Specify valid full path to your repositories'
123 123 ' you can change this later in application settings:')
124 124 else:
125 125 path = test_repo_path
126 126
127 127 if not os.path.isdir(path):
128 128 log.error('You entered wrong path: %s', path)
129 129 sys.exit()
130 130
131 131 hooks1 = RhodeCodeUi()
132 132 hooks1.ui_section = 'hooks'
133 133 hooks1.ui_key = 'changegroup.update'
134 134 hooks1.ui_value = 'hg update >&2'
135 135 hooks1.ui_active = False
136 136
137 137 hooks2 = RhodeCodeUi()
138 138 hooks2.ui_section = 'hooks'
139 139 hooks2.ui_key = 'changegroup.repo_size'
140 140 hooks2.ui_value = 'python:rhodecode.lib.hooks.repo_size'
141
141
142 142 hooks3 = RhodeCodeUi()
143 143 hooks3.ui_section = 'hooks'
144 144 hooks3.ui_key = 'pretxnchangegroup.push_logger'
145 145 hooks3.ui_value = 'python:rhodecode.lib.hooks.log_push_action'
146
146
147 147 hooks4 = RhodeCodeUi()
148 148 hooks4.ui_section = 'hooks'
149 149 hooks4.ui_key = 'preoutgoing.pull_logger'
150 150 hooks4.ui_value = 'python:rhodecode.lib.hooks.log_pull_action'
151
151
152 152
153 153 web1 = RhodeCodeUi()
154 154 web1.ui_section = 'web'
155 155 web1.ui_key = 'push_ssl'
156 156 web1.ui_value = 'false'
157 157
158 158 web2 = RhodeCodeUi()
159 159 web2.ui_section = 'web'
160 160 web2.ui_key = 'allow_archive'
161 161 web2.ui_value = 'gz zip bz2'
162 162
163 163 web3 = RhodeCodeUi()
164 164 web3.ui_section = 'web'
165 165 web3.ui_key = 'allow_push'
166 166 web3.ui_value = '*'
167 167
168 168 web4 = RhodeCodeUi()
169 169 web4.ui_section = 'web'
170 170 web4.ui_key = 'baseurl'
171 171 web4.ui_value = '/'
172 172
173 173 paths = RhodeCodeUi()
174 174 paths.ui_section = 'paths'
175 175 paths.ui_key = '/'
176 176 paths.ui_value = path
177 177
178 178
179 179 hgsettings1 = RhodeCodeSettings()
180 180
181 181 hgsettings1.app_settings_name = 'realm'
182 182 hgsettings1.app_settings_value = 'RhodeCode authentication'
183 183
184 184 hgsettings2 = RhodeCodeSettings()
185 185 hgsettings2.app_settings_name = 'title'
186 186 hgsettings2.app_settings_value = 'RhodeCode'
187 187
188 188 try:
189 189 self.sa.add(hooks1)
190 190 self.sa.add(hooks2)
191 191 self.sa.add(hooks3)
192 192 self.sa.add(hooks4)
193 193 self.sa.add(web1)
194 194 self.sa.add(web2)
195 195 self.sa.add(web3)
196 196 self.sa.add(web4)
197 197 self.sa.add(paths)
198 198 self.sa.add(hgsettings1)
199 199 self.sa.add(hgsettings2)
200 200 self.sa.commit()
201 201 except:
202 202 self.sa.rollback()
203 203 raise
204 204 log.info('created ui config')
205 205
206 206 def create_user(self, username, password, email='', admin=False):
207 207 log.info('creating administrator user %s', username)
208 208 new_user = User()
209 209 new_user.username = username
210 210 new_user.password = get_crypt_password(password)
211 211 new_user.name = 'RhodeCode'
212 212 new_user.lastname = 'Admin'
213 213 new_user.email = email
214 214 new_user.admin = admin
215 215 new_user.active = True
216 216
217 217 try:
218 218 self.sa.add(new_user)
219 219 self.sa.commit()
220 220 except:
221 221 self.sa.rollback()
222 222 raise
223 223
224 224 def create_default_user(self):
225 225 log.info('creating default user')
226 226 #create default user for handling default permissions.
227 227 def_user = User()
228 228 def_user.username = 'default'
229 229 def_user.password = get_crypt_password(str(uuid.uuid1())[:8])
230 def_user.name = 'default'
231 def_user.lastname = 'default'
232 def_user.email = 'default@default.com'
230 def_user.name = 'Anonymous'
231 def_user.lastname = 'User'
232 def_user.email = 'anonymous@rhodecode.org'
233 233 def_user.admin = False
234 234 def_user.active = False
235 235 try:
236 236 self.sa.add(def_user)
237 237 self.sa.commit()
238 238 except:
239 239 self.sa.rollback()
240 240 raise
241 241
242 242 def create_permissions(self):
243 243 #module.(access|create|change|delete)_[name]
244 244 #module.(read|write|owner)
245 245 perms = [('repository.none', 'Repository no access'),
246 246 ('repository.read', 'Repository read access'),
247 247 ('repository.write', 'Repository write access'),
248 248 ('repository.admin', 'Repository admin access'),
249 249 ('hg.admin', 'Hg Administrator'),
250 250 ('hg.create.repository', 'Repository create'),
251 251 ('hg.create.none', 'Repository creation disabled'),
252 252 ('hg.register.none', 'Register disabled'),
253 253 ('hg.register.manual_activate', 'Register new user with rhodecode without manual activation'),
254 254 ('hg.register.auto_activate', 'Register new user with rhodecode without auto activation'),
255 255 ]
256 256
257 257 for p in perms:
258 258 new_perm = Permission()
259 259 new_perm.permission_name = p[0]
260 260 new_perm.permission_longname = p[1]
261 261 try:
262 262 self.sa.add(new_perm)
263 263 self.sa.commit()
264 264 except:
265 265 self.sa.rollback()
266 266 raise
267 267
268 268 def populate_default_permissions(self):
269 269 log.info('creating default user permissions')
270 270
271 271 default_user = self.sa.query(User)\
272 272 .filter(User.username == 'default').scalar()
273 273
274 274 reg_perm = UserToPerm()
275 275 reg_perm.user = default_user
276 276 reg_perm.permission = self.sa.query(Permission)\
277 277 .filter(Permission.permission_name == 'hg.register.manual_activate')\
278 278 .scalar()
279 279
280 280 create_repo_perm = UserToPerm()
281 281 create_repo_perm.user = default_user
282 282 create_repo_perm.permission = self.sa.query(Permission)\
283 283 .filter(Permission.permission_name == 'hg.create.repository')\
284 284 .scalar()
285 285
286 286 default_repo_perm = UserToPerm()
287 287 default_repo_perm.user = default_user
288 288 default_repo_perm.permission = self.sa.query(Permission)\
289 289 .filter(Permission.permission_name == 'repository.read')\
290 290 .scalar()
291 291
292 292 try:
293 293 self.sa.add(reg_perm)
294 294 self.sa.add(create_repo_perm)
295 295 self.sa.add(default_repo_perm)
296 296 self.sa.commit()
297 297 except:
298 298 self.sa.rollback()
299 299 raise
300 300
@@ -1,365 +1,366 b''
1 1 """ this is forms validation classes
2 2 http://formencode.org/module-formencode.validators.html
3 3 for list off all availible validators
4 4
5 5 we can create our own validators
6 6
7 7 The table below outlines the options which can be used in a schema in addition to the validators themselves
8 8 pre_validators [] These validators will be applied before the schema
9 9 chained_validators [] These validators will be applied after the schema
10 10 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
11 11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
12 12 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
13 13 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
14 14
15 15
16 16 <name> = formencode.validators.<name of validator>
17 17 <name> must equal form name
18 18 list=[1,2,3,4,5]
19 19 for SELECT use formencode.All(OneOf(list), Int())
20 20
21 21 """
22 22 from formencode import All
23 23 from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \
24 24 Email, Bool, StringBoolean
25 25 from pylons import session
26 26 from pylons.i18n.translation import _
27 27 from rhodecode.lib.auth import check_password, get_crypt_password
28 28 from rhodecode.model import meta
29 29 from rhodecode.model.user import UserModel
30 30 from rhodecode.model.repo import RepoModel
31 31 from rhodecode.model.db import User
32 32 from webhelpers.pylonslib.secure_form import authentication_token
33 33 from vcs import BACKENDS
34 34 import formencode
35 35 import logging
36 36 import os
37 37 import rhodecode.lib.helpers as h
38 38
39 39 log = logging.getLogger(__name__)
40 40
41 41 #this is needed to translate the messages using _() in validators
42 42 class State_obj(object):
43 43 _ = staticmethod(_)
44 44
45 45 #===============================================================================
46 46 # VALIDATORS
47 47 #===============================================================================
48 48 class ValidAuthToken(formencode.validators.FancyValidator):
49 49 messages = {'invalid_token':_('Token mismatch')}
50 50
51 51 def validate_python(self, value, state):
52 52
53 53 if value != authentication_token():
54 54 raise formencode.Invalid(self.message('invalid_token', state,
55 55 search_number=value), value, state)
56 56
57 57 def ValidUsername(edit, old_data):
58 58 class _ValidUsername(formencode.validators.FancyValidator):
59 59
60 60 def validate_python(self, value, state):
61 61 if value in ['default', 'new_user']:
62 62 raise formencode.Invalid(_('Invalid username'), value, state)
63 63 #check if user is unique
64 64 old_un = None
65 65 if edit:
66 66 old_un = UserModel().get(old_data.get('user_id')).username
67 67
68 68 if old_un != value or not edit:
69 69 if UserModel().get_by_username(value, cache=False):
70 70 raise formencode.Invalid(_('This username already exists') ,
71 71 value, state)
72 72
73 73 return _ValidUsername
74 74
75 75 class ValidPassword(formencode.validators.FancyValidator):
76 76
77 77 def to_python(self, value, state):
78 78 if value:
79 79 return get_crypt_password(value)
80 80
81 81 class ValidAuth(formencode.validators.FancyValidator):
82 82 messages = {
83 83 'invalid_password':_('invalid password'),
84 84 'invalid_login':_('invalid user name'),
85 85 'disabled_account':_('Your acccount is disabled')
86 86
87 87 }
88 88 #error mapping
89 89 e_dict = {'username':messages['invalid_login'],
90 90 'password':messages['invalid_password']}
91 91 e_dict_disable = {'username':messages['disabled_account']}
92 92
93 93 def validate_python(self, value, state):
94 94 password = value['password']
95 95 username = value['username']
96 96 user = UserModel().get_by_username(username)
97 97 if user is None:
98 98 raise formencode.Invalid(self.message('invalid_password',
99 99 state=State_obj), value, state,
100 100 error_dict=self.e_dict)
101 101 if user:
102 102 if user.active:
103 103 if user.username == username and check_password(password,
104 104 user.password):
105 105 return value
106 106 else:
107 107 log.warning('user %s not authenticated', username)
108 108 raise formencode.Invalid(self.message('invalid_password',
109 109 state=State_obj), value, state,
110 110 error_dict=self.e_dict)
111 111 else:
112 112 log.warning('user %s is disabled', username)
113 113 raise formencode.Invalid(self.message('disabled_account',
114 114 state=State_obj),
115 115 value, state,
116 116 error_dict=self.e_dict_disable)
117 117
118 118 class ValidRepoUser(formencode.validators.FancyValidator):
119 119
120 120 def to_python(self, value, state):
121 121 sa = meta.Session()
122 122 try:
123 123 self.user_db = sa.query(User)\
124 124 .filter(User.active == True)\
125 125 .filter(User.username == value).one()
126 126 except Exception:
127 127 raise formencode.Invalid(_('This username is not valid'),
128 128 value, state)
129 129 finally:
130 130 meta.Session.remove()
131 131
132 132 return self.user_db.user_id
133 133
134 134 def ValidRepoName(edit, old_data):
135 135 class _ValidRepoName(formencode.validators.FancyValidator):
136 136
137 137 def to_python(self, value, state):
138 138 slug = h.repo_name_slug(value)
139 139 if slug in ['_admin']:
140 140 raise formencode.Invalid(_('This repository name is disallowed'),
141 141 value, state)
142 142 if old_data.get('repo_name') != value or not edit:
143 143 if RepoModel().get(slug, cache=False):
144 144 raise formencode.Invalid(_('This repository already exists') ,
145 145 value, state)
146 146 return slug
147 147
148 148
149 149 return _ValidRepoName
150 150
151 151 def ValidForkType(old_data):
152 152 class _ValidForkType(formencode.validators.FancyValidator):
153 153
154 154 def to_python(self, value, state):
155 155 if old_data['repo_type'] != value:
156 156 raise formencode.Invalid(_('Fork have to be the same type as original'), value, state)
157 157 return value
158 158 return _ValidForkType
159 159
160 160 class ValidPerms(formencode.validators.FancyValidator):
161 161 messages = {'perm_new_user_name':_('This username is not valid')}
162 162
163 163 def to_python(self, value, state):
164 164 perms_update = []
165 165 perms_new = []
166 166 #build a list of permission to update and new permission to create
167 167 for k, v in value.items():
168 168 if k.startswith('perm_'):
169 169 if k.startswith('perm_new_user'):
170 170 new_perm = value.get('perm_new_user', False)
171 171 new_user = value.get('perm_new_user_name', False)
172 172 if new_user and new_perm:
173 173 if (new_user, new_perm) not in perms_new:
174 174 perms_new.append((new_user, new_perm))
175 175 else:
176 176 usr = k[5:]
177 177 if usr == 'default':
178 178 if value['private']:
179 179 #set none for default when updating to private repo
180 180 v = 'repository.none'
181 181 perms_update.append((usr, v))
182 182 value['perms_updates'] = perms_update
183 183 value['perms_new'] = perms_new
184 184 sa = meta.Session
185 185 for k, v in perms_new:
186 186 try:
187 187 self.user_db = sa.query(User)\
188 188 .filter(User.active == True)\
189 189 .filter(User.username == k).one()
190 190 except Exception:
191 191 msg = self.message('perm_new_user_name',
192 192 state=State_obj)
193 193 raise formencode.Invalid(msg, value, state, error_dict={'perm_new_user_name':msg})
194 194 return value
195 195
196 196 class ValidSettings(formencode.validators.FancyValidator):
197 197
198 198 def to_python(self, value, state):
199 199 #settings form can't edit user
200 200 if value.has_key('user'):
201 201 del['value']['user']
202 202
203 203 return value
204 204
205 205 class ValidPath(formencode.validators.FancyValidator):
206 206 def to_python(self, value, state):
207 207
208 208 if not os.path.isdir(value):
209 209 msg = _('This is not a valid path')
210 210 raise formencode.Invalid(msg, value, state,
211 211 error_dict={'paths_root_path':msg})
212 212 return value
213 213
214 214 def UniqSystemEmail(old_data):
215 215 class _UniqSystemEmail(formencode.validators.FancyValidator):
216 216 def to_python(self, value, state):
217 217 if old_data.get('email') != value:
218 218 sa = meta.Session()
219 219 try:
220 220 user = sa.query(User).filter(User.email == value).scalar()
221 221 if user:
222 222 raise formencode.Invalid(_("That e-mail address is already taken") ,
223 223 value, state)
224 224 finally:
225 225 meta.Session.remove()
226 226
227 227 return value
228 228
229 229 return _UniqSystemEmail
230 230
231 231 class ValidSystemEmail(formencode.validators.FancyValidator):
232 232 def to_python(self, value, state):
233 233 sa = meta.Session
234 234 try:
235 235 user = sa.query(User).filter(User.email == value).scalar()
236 236 if user is None:
237 237 raise formencode.Invalid(_("That e-mail address doesn't exist.") ,
238 238 value, state)
239 239 finally:
240 240 meta.Session.remove()
241 241
242 242 return value
243 243
244 244 #===============================================================================
245 245 # FORMS
246 246 #===============================================================================
247 247 class LoginForm(formencode.Schema):
248 248 allow_extra_fields = True
249 249 filter_extra_fields = True
250 250 username = UnicodeString(
251 251 strip=True,
252 252 min=1,
253 253 not_empty=True,
254 254 messages={
255 255 'empty':_('Please enter a login'),
256 256 'tooShort':_('Enter a value %(min)i characters long or more')}
257 257 )
258 258
259 259 password = UnicodeString(
260 260 strip=True,
261 261 min=6,
262 262 not_empty=True,
263 263 messages={
264 264 'empty':_('Please enter a password'),
265 265 'tooShort':_('Enter %(min)i characters or more')}
266 266 )
267 267
268 268
269 269 #chained validators have access to all data
270 270 chained_validators = [ValidAuth]
271 271
272 272 def UserForm(edit=False, old_data={}):
273 273 class _UserForm(formencode.Schema):
274 274 allow_extra_fields = True
275 275 filter_extra_fields = True
276 276 username = All(UnicodeString(strip=True, min=1, not_empty=True), ValidUsername(edit, old_data))
277 277 if edit:
278 278 new_password = All(UnicodeString(strip=True, min=6, not_empty=False), ValidPassword)
279 279 admin = StringBoolean(if_missing=False)
280 280 else:
281 281 password = All(UnicodeString(strip=True, min=6, not_empty=True), ValidPassword)
282 282 active = StringBoolean(if_missing=False)
283 283 name = UnicodeString(strip=True, min=1, not_empty=True)
284 284 lastname = UnicodeString(strip=True, min=1, not_empty=True)
285 285 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
286 286
287 287 return _UserForm
288 288
289 289 RegisterForm = UserForm
290 290
291 291 def PasswordResetForm():
292 292 class _PasswordResetForm(formencode.Schema):
293 293 allow_extra_fields = True
294 294 filter_extra_fields = True
295 295 email = All(ValidSystemEmail(), Email(not_empty=True))
296 296 return _PasswordResetForm
297 297
298 298 def RepoForm(edit=False, old_data={}):
299 299 class _RepoForm(formencode.Schema):
300 300 allow_extra_fields = True
301 301 filter_extra_fields = False
302 302 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
303 303 description = UnicodeString(strip=True, min=1, not_empty=True)
304 304 private = StringBoolean(if_missing=False)
305 305 repo_type = OneOf(BACKENDS.keys())
306 306 if edit:
307 307 user = All(Int(not_empty=True), ValidRepoUser)
308 308
309 309 chained_validators = [ValidPerms]
310 310 return _RepoForm
311 311
312 312 def RepoForkForm(edit=False, old_data={}):
313 313 class _RepoForkForm(formencode.Schema):
314 314 allow_extra_fields = True
315 315 filter_extra_fields = False
316 316 fork_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
317 317 description = UnicodeString(strip=True, min=1, not_empty=True)
318 318 private = StringBoolean(if_missing=False)
319 319 repo_type = All(ValidForkType(old_data), OneOf(BACKENDS.keys()))
320 320 return _RepoForkForm
321 321
322 322 def RepoSettingsForm(edit=False, old_data={}):
323 323 class _RepoForm(formencode.Schema):
324 324 allow_extra_fields = True
325 325 filter_extra_fields = False
326 326 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
327 327 description = UnicodeString(strip=True, min=1, not_empty=True)
328 328 private = StringBoolean(if_missing=False)
329 329
330 330 chained_validators = [ValidPerms, ValidSettings]
331 331 return _RepoForm
332 332
333 333
334 334 def ApplicationSettingsForm():
335 335 class _ApplicationSettingsForm(formencode.Schema):
336 336 allow_extra_fields = True
337 337 filter_extra_fields = False
338 338 rhodecode_title = UnicodeString(strip=True, min=1, not_empty=True)
339 339 rhodecode_realm = UnicodeString(strip=True, min=1, not_empty=True)
340 340
341 341 return _ApplicationSettingsForm
342 342
343 343 def ApplicationUiSettingsForm():
344 344 class _ApplicationUiSettingsForm(formencode.Schema):
345 345 allow_extra_fields = True
346 346 filter_extra_fields = False
347 347 web_push_ssl = OneOf(['true', 'false'], if_missing='false')
348 348 paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=1, not_empty=True))
349 349 hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
350 350 hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
351 351 hooks_pretxnchangegroup_push_logger = OneOf(['True', 'False'], if_missing=False)
352 352 hooks_preoutgoing_pull_logger = OneOf(['True', 'False'], if_missing=False)
353 353
354 354 return _ApplicationUiSettingsForm
355 355
356 356 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
357 357 class _DefaultPermissionsForm(formencode.Schema):
358 358 allow_extra_fields = True
359 359 filter_extra_fields = True
360 360 overwrite_default = OneOf(['true', 'false'], if_missing='false')
361 anonymous = OneOf(['True', 'False'], if_missing=False)
361 362 default_perm = OneOf(perms_choices)
362 363 default_register = OneOf(register_choices)
363 364 default_create = OneOf(create_choices)
364 365
365 366 return _DefaultPermissionsForm
@@ -1,95 +1,106 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # Model for permissions
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; version 2
9 9 # of the License or (at your opinion) any later version of the license.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 19 # MA 02110-1301, USA.
20 20 """
21 21 Created on Aug 20, 2010
22 22 Model for permissions
23 23 @author: marcink
24 24 """
25 25
26 26 from rhodecode.model.db import User, Permission, UserToPerm, RepoToPerm
27 27 from rhodecode.model.caching_query import FromCache
28 28 from rhodecode.model.meta import Session
29 29 import logging
30 30 import traceback
31 31 log = logging.getLogger(__name__)
32 32
33 33
34 34 class PermissionModel(object):
35 35
36 36 def __init__(self, sa=None):
37 37 if not sa:
38 38 self.sa = Session()
39 39 else:
40 40 self.sa = sa
41 41
42 42 def get_permission(self, permission_id, cache=False):
43 43 perm = self.sa.query(Permission)
44 44 if cache:
45 45 perm = perm.options(FromCache("sql_cache_short",
46 46 "get_permission_%s" % permission_id))
47 47 return perm.get(permission_id)
48 48
49 49 def get_permission_by_name(self, name, cache=False):
50 50 perm = self.sa.query(Permission)\
51 51 .filter(Permission.permission_name == name)
52 52 if cache:
53 53 perm = perm.options(FromCache("sql_cache_short",
54 54 "get_permission_%s" % name))
55 55 return perm.scalar()
56 56
57 57 def update(self, form_result):
58 58 perm_user = self.sa.query(User)\
59 59 .filter(User.username == form_result['perm_user_name']).scalar()
60 60 u2p = self.sa.query(UserToPerm).filter(UserToPerm.user == perm_user).all()
61 61 if len(u2p) != 3:
62 raise Exception('There is more than 3 defined'
63 ' permissions for default user. This should not happen please verify'
64 ' your database')
62 raise Exception('Defined: %s should be 3 permissions for default'
63 ' user. This should not happen please verify'
64 ' your database' % len(u2p))
65 65
66 66 try:
67 67 #stage 1 change defaults
68 68 for p in u2p:
69 69 if p.permission.permission_name.startswith('repository.'):
70 p.permission = self.get_permission_by_name(form_result['default_perm'])
70 p.permission = self.get_permission_by_name(
71 form_result['default_perm'])
71 72 self.sa.add(p)
72 73
73 74 if p.permission.permission_name.startswith('hg.register.'):
74 p.permission = self.get_permission_by_name(form_result['default_register'])
75 p.permission = self.get_permission_by_name(
76 form_result['default_register'])
75 77 self.sa.add(p)
76 78
77 79 if p.permission.permission_name.startswith('hg.create.'):
78 p.permission = self.get_permission_by_name(form_result['default_create'])
80 p.permission = self.get_permission_by_name(
81 form_result['default_create'])
79 82 self.sa.add(p)
80 83 #stage 2 update all default permissions for repos if checked
81 84 if form_result['overwrite_default'] == 'true':
82 for r2p in self.sa.query(RepoToPerm).filter(RepoToPerm.user == perm_user).all():
83 r2p.permission = self.get_permission_by_name(form_result['default_perm'])
85 for r2p in self.sa.query(RepoToPerm)\
86 .filter(RepoToPerm.user == perm_user).all():
87 r2p.permission = self.get_permission_by_name(
88 form_result['default_perm'])
84 89 self.sa.add(r2p)
85 90
91 #stage 3 set anonymous access
92 if perm_user.username == 'default':
93 perm_user.active = bool(form_result['anonymous'])
94 self.sa.add(perm_user)
95
96
86 97 self.sa.commit()
87 98 except:
88 99 log.error(traceback.format_exc())
89 100 self.sa.rollback()
90 101 raise
91 102
92 103
93 104
94 105
95 106
@@ -1,145 +1,166 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # Model for users
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; version 2
9 9 # of the License or (at your opinion) any later version of the license.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 19 # MA 02110-1301, USA.
20 20 """
21 21 Created on April 9, 2010
22 22 Model for users
23 23 :author: marcink
24 24 """
25 25
26 26 from pylons.i18n.translation import _
27 27 from rhodecode.model.caching_query import FromCache
28 28 from rhodecode.model.db import User
29 29 from rhodecode.model.meta import Session
30 30 import logging
31 31 import traceback
32 32
33 33 log = logging.getLogger(__name__)
34 34
35 35 class DefaultUserException(Exception):pass
36 36
37 37 class UserModel(object):
38 38
39 39 def __init__(self, sa=None):
40 40 if not sa:
41 41 self.sa = Session()
42 42 else:
43 43 self.sa = sa
44 44
45 45 def get(self, user_id, cache=False):
46 46 user = self.sa.query(User)
47 47 if cache:
48 48 user = user.options(FromCache("sql_cache_short",
49 49 "get_user_%s" % user_id))
50 50 return user.get(user_id)
51 51
52 52
53 53 def get_by_username(self, username, cache=False):
54 54 user = self.sa.query(User)\
55 55 .filter(User.username == username)
56 56 if cache:
57 57 user = user.options(FromCache("sql_cache_short",
58 58 "get_user_%s" % username))
59 59 return user.scalar()
60 60
61 61 def create(self, form_data):
62 62 try:
63 63 new_user = User()
64 64 for k, v in form_data.items():
65 65 setattr(new_user, k, v)
66 66
67 67 self.sa.add(new_user)
68 68 self.sa.commit()
69 69 except:
70 70 log.error(traceback.format_exc())
71 71 self.sa.rollback()
72 72 raise
73 73
74 74 def create_registration(self, form_data):
75 75 try:
76 76 new_user = User()
77 77 for k, v in form_data.items():
78 78 if k != 'admin':
79 79 setattr(new_user, k, v)
80 80
81 81 self.sa.add(new_user)
82 82 self.sa.commit()
83 83 except:
84 84 log.error(traceback.format_exc())
85 85 self.sa.rollback()
86 86 raise
87 87
88 88 def update(self, user_id, form_data):
89 89 try:
90 90 new_user = self.get(user_id, cache=False)
91 91 if new_user.username == 'default':
92 92 raise DefaultUserException(
93 93 _("You can't Edit this user since it's"
94 94 " crucial for entire application"))
95 95 for k, v in form_data.items():
96 96 if k == 'new_password' and v != '':
97 97 new_user.password = v
98 98 else:
99 99 setattr(new_user, k, v)
100 100
101 101 self.sa.add(new_user)
102 102 self.sa.commit()
103 103 except:
104 104 log.error(traceback.format_exc())
105 105 self.sa.rollback()
106 106 raise
107 107
108 108 def update_my_account(self, user_id, form_data):
109 109 try:
110 110 new_user = self.get(user_id, cache=False)
111 111 if new_user.username == 'default':
112 112 raise DefaultUserException(
113 113 _("You can't Edit this user since it's"
114 114 " crucial for entire application"))
115 115 for k, v in form_data.items():
116 116 if k == 'new_password' and v != '':
117 117 new_user.password = v
118 118 else:
119 119 if k not in ['admin', 'active']:
120 120 setattr(new_user, k, v)
121 121
122 122 self.sa.add(new_user)
123 123 self.sa.commit()
124 124 except:
125 125 log.error(traceback.format_exc())
126 126 self.sa.rollback()
127 127 raise
128 128
129 129 def delete(self, user_id):
130 130 try:
131 131 user = self.get(user_id, cache=False)
132 132 if user.username == 'default':
133 133 raise DefaultUserException(
134 134 _("You can't remove this user since it's"
135 135 " crucial for entire application"))
136 136 self.sa.delete(user)
137 137 self.sa.commit()
138 138 except:
139 139 log.error(traceback.format_exc())
140 140 self.sa.rollback()
141 141 raise
142 142
143 143 def reset_password(self, data):
144 144 from rhodecode.lib.celerylib import tasks, run_task
145 145 run_task(tasks.reset_user_password, data['email'])
146
147
148 def fill_data(self, user):
149 """
150 Fills user data with those from database and log out user if not
151 present in database
152 :param user:
153 """
154 log.debug('filling auth user data')
155 try:
156 dbuser = self.get(user.user_id)
157 user.username = dbuser.username
158 user.is_admin = dbuser.admin
159 user.name = dbuser.name
160 user.lastname = dbuser.lastname
161 user.email = dbuser.email
162 except:
163 log.error(traceback.format_exc())
164 user.is_authenticated = False
165
166 return user
@@ -1,2321 +1,2321 b''
1 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 border:0;
3 3 outline:0;
4 4 font-size:100%;
5 5 vertical-align:baseline;
6 6 background:transparent;
7 7 margin:0;
8 8 padding:0;
9 9 }
10 10
11 11 body {
12 12 line-height:1;
13 13 height:100%;
14 14 background:url("../images/background.png") repeat scroll 0 0 #B0B0B0;
15 15 font-family:Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
16 16 font-size:12px;
17 17 color:#000;
18 18 margin:0;
19 19 padding:0;
20 20 }
21 21
22 22 ol,ul {
23 23 list-style:none;
24 24 }
25 25
26 26 blockquote,q {
27 27 quotes:none;
28 28 }
29 29
30 30 blockquote:before,blockquote:after,q:before,q:after {
31 31 content:none;
32 32 }
33 33
34 34 :focus {
35 35 outline:0;
36 36 }
37 37
38 38 del {
39 39 text-decoration:line-through;
40 40 }
41 41
42 42 table {
43 43 border-collapse:collapse;
44 44 border-spacing:0;
45 45 }
46 46
47 47 html {
48 48 height:100%;
49 49 }
50 50
51 51 a {
52 52 color:#003367;
53 53 text-decoration:none;
54 54 cursor:pointer;
55 55 font-weight:700;
56 56 }
57 57
58 58 a:hover {
59 59 color:#316293;
60 60 text-decoration:underline;
61 61 }
62 62
63 63 h1,h2,h3,h4,h5,h6 {
64 64 color:#292929;
65 65 font-weight:700;
66 66 }
67 67
68 68 h1 {
69 69 font-size:22px;
70 70 }
71 71
72 72 h2 {
73 73 font-size:20px;
74 74 }
75 75
76 76 h3 {
77 77 font-size:18px;
78 78 }
79 79
80 80 h4 {
81 81 font-size:16px;
82 82 }
83 83
84 84 h5 {
85 85 font-size:14px;
86 86 }
87 87
88 88 h6 {
89 89 font-size:11px;
90 90 }
91 91
92 92 ul.circle {
93 93 list-style-type:circle;
94 94 }
95 95
96 96 ul.disc {
97 97 list-style-type:disc;
98 98 }
99 99
100 100 ul.square {
101 101 list-style-type:square;
102 102 }
103 103
104 104 ol.lower-roman {
105 105 list-style-type:lower-roman;
106 106 }
107 107
108 108 ol.upper-roman {
109 109 list-style-type:upper-roman;
110 110 }
111 111
112 112 ol.lower-alpha {
113 113 list-style-type:lower-alpha;
114 114 }
115 115
116 116 ol.upper-alpha {
117 117 list-style-type:upper-alpha;
118 118 }
119 119
120 120 ol.decimal {
121 121 list-style-type:decimal;
122 122 }
123 123
124 124 div.color {
125 125 clear:both;
126 126 overflow:hidden;
127 127 position:absolute;
128 128 background:#FFF;
129 129 margin:7px 0 0 60px;
130 130 padding:1px 1px 1px 0;
131 131 }
132 132
133 133 div.color a {
134 134 width:15px;
135 135 height:15px;
136 136 display:block;
137 137 float:left;
138 138 margin:0 0 0 1px;
139 139 padding:0;
140 140 }
141 141
142 142 div.options {
143 143 clear:both;
144 144 overflow:hidden;
145 145 position:absolute;
146 146 background:#FFF;
147 147 margin:7px 0 0 162px;
148 148 padding:0;
149 149 }
150 150
151 151 div.options a {
152 152 height:1%;
153 153 display:block;
154 154 text-decoration:none;
155 155 margin:0;
156 156 padding:3px 8px;
157 157 }
158 158
159 159 .top-left-rounded-corner {
160 160 -webkit-border-top-left-radius: 8px;
161 161 -khtml-border-radius-topleft: 8px;
162 162 -moz-border-radius-topleft: 8px;
163 163 border-top-left-radius: 8px;
164 164 }
165 165
166 166 .top-right-rounded-corner {
167 167 -webkit-border-top-right-radius: 8px;
168 168 -khtml-border-radius-topright: 8px;
169 169 -moz-border-radius-topright: 8px;
170 170 border-top-right-radius: 8px;
171 171 }
172 172
173 173 .bottom-left-rounded-corner {
174 174 -webkit-border-bottom-left-radius: 8px;
175 175 -khtml-border-radius-bottomleft: 8px;
176 176 -moz-border-radius-bottomleft: 8px;
177 177 border-bottom-left-radius: 8px;
178 178 }
179 179
180 180 .bottom-right-rounded-corner {
181 181 -webkit-border-bottom-right-radius: 8px;
182 182 -khtml-border-radius-bottomright: 8px;
183 183 -moz-border-radius-bottomright: 8px;
184 184 border-bottom-right-radius: 8px;
185 185 }
186 186
187 187
188 188 #header {
189 189 margin:0;
190 190 padding:0 30px;
191 191 }
192 192
193 193 #header ul#logged-user li {
194 194 list-style:none;
195 195 float:left;
196 196 border-left:1px solid #bbb;
197 197 border-right:1px solid #a5a5a5;
198 198 margin:-2px 0 0;
199 199 padding:10px 12px;
200 200 }
201 201
202 202 #header ul#logged-user li.first {
203 203 border-left:none;
204 204 margin:-6px;
205 205 }
206 206
207 207 #header ul#logged-user li.first div.account {
208 208 padding-top:4px;
209 209 float:left;
210 210 }
211 211
212 212 #header ul#logged-user li.last {
213 213 border-right:none;
214 214 }
215 215
216 216 #header ul#logged-user li a {
217 217 color:#4e4e4e;
218 218 font-weight:700;
219 219 text-decoration:none;
220 220 }
221 221
222 222 #header ul#logged-user li a:hover {
223 223 color:#376ea6;
224 224 text-decoration:underline;
225 225 }
226 226
227 227 #header ul#logged-user li.highlight a {
228 228 color:#fff;
229 229 }
230 230
231 231 #header ul#logged-user li.highlight a:hover {
232 232 color:#376ea6;
233 233 }
234 234
235 235 #header #header-inner {
236 236 height:40px;
237 237 clear:both;
238 238 position:relative;
239 239 background:#003367 url("../images/header_inner.png") repeat-x;
240 240 border-bottom:2px solid #fff;
241 241 margin:0;
242 242 padding:0;
243 243 }
244 244
245 245 #header #header-inner #home a {
246 246 height:40px;
247 247 width:46px;
248 248 display:block;
249 249 background:url("../images/button_home.png");
250 250 background-position:0 0;
251 251 margin:0;
252 252 padding:0;
253 253 }
254 254
255 255 #header #header-inner #home a:hover {
256 256 background-position:0 -40px;
257 257 }
258 258
259 259 #header #header-inner #logo h1 {
260 260 color:#FFF;
261 261 font-size:14px;
262 262 margin:13px 0 0 13px;
263 263 padding:0;
264 264 }
265 265
266 266 #header #header-inner #logo a {
267 267 color:#fff;
268 268 text-decoration:none;
269 269 }
270 270
271 271 #header #header-inner #logo a:hover {
272 272 color:#bfe3ff;
273 273 }
274 274
275 275 #header #header-inner #quick,#header #header-inner #quick ul {
276 276 position:relative;
277 277 float:right;
278 278 list-style-type:none;
279 279 list-style-position:outside;
280 280 margin:10px 5px 0 0;
281 281 padding:0;
282 282 }
283 283
284 284 #header #header-inner #quick li {
285 285 position:relative;
286 286 float:left;
287 287 margin:0 5px 0 0;
288 288 padding:0;
289 289 }
290 290
291 291 #header #header-inner #quick li a {
292 292 top:0;
293 293 left:0;
294 294 height:1%;
295 295 display:block;
296 296 clear:both;
297 297 overflow:hidden;
298 298 color:#FFF;
299 299 font-weight:700;
300 300 text-decoration:none;
301 301 background:#369 url("../../images/quick_l.png") no-repeat top left;
302 302 padding:0;
303 303 }
304 304
305 305 #header #header-inner #quick li span {
306 306 top:0;
307 307 right:0;
308 308 height:1%;
309 309 display:block;
310 310 float:left;
311 311 background:url("../../images/quick_r.png") no-repeat top right;
312 312 border-left:1px solid #3f6f9f;
313 313 margin:0;
314 314 padding:10px 12px 8px 10px;
315 315 }
316 316
317 317 #header #header-inner #quick li span.normal {
318 318 border:none;
319 319 padding:10px 12px 8px;
320 320 }
321 321
322 322 #header #header-inner #quick li span.icon {
323 323 top:0;
324 324 left:0;
325 325 border-left:none;
326 326 background:url("../../images/quick_l.png") no-repeat top left;
327 327 border-right:1px solid #2e5c89;
328 328 padding:8px 8px 4px;
329 329 }
330 330
331 331 #header #header-inner #quick li a:hover {
332 332 background:#4e4e4e url("../../images/quick_l_selected.png") no-repeat top left;
333 333 }
334 334
335 335 #header #header-inner #quick li a:hover span {
336 336 border-left:1px solid #545454;
337 337 background:url("../../images/quick_r_selected.png") no-repeat top right;
338 338 }
339 339
340 340 #header #header-inner #quick li a:hover span.icon {
341 341 border-left:none;
342 342 border-right:1px solid #464646;
343 343 background:url("../../images/quick_l_selected.png") no-repeat top left;
344 344 }
345 345
346 346 #header #header-inner #quick ul {
347 347 top:29px;
348 348 right:0;
349 349 min-width:200px;
350 350 display:none;
351 351 position:absolute;
352 352 background:#FFF;
353 353 border:1px solid #666;
354 354 border-top:1px solid #003367;
355 355 z-index:100;
356 356 margin:0;
357 357 padding:0;
358 358 }
359 359
360 360 #header #header-inner #quick ul.repo_switcher {
361 361 max-height:275px;
362 362 overflow-x:hidden;
363 363 overflow-y:auto;
364 364 }
365 365
366 366 #header #header-inner #quick li ul li {
367 367 border-bottom:1px solid #ddd;
368 368 }
369 369
370 370 #header #header-inner #quick li ul li a {
371 371 width:182px;
372 372 height:auto;
373 373 display:block;
374 374 float:left;
375 375 background:#FFF;
376 376 color:#003367;
377 377 font-weight:400;
378 378 margin:0;
379 379 padding:7px 9px;
380 380 }
381 381
382 382 #header #header-inner #quick li ul li a:hover {
383 383 color:#000;
384 384 background:#FFF;
385 385 }
386 386
387 387 #header #header-inner #quick ul ul {
388 388 top:auto;
389 389 }
390 390
391 391 #header #header-inner #quick li ul ul {
392 392 right:200px;
393 393 max-height:275px;
394 394 overflow:auto;
395 395 overflow-x:hidden;
396 396 white-space:nowrap;
397 397 }
398 398
399 399 #header #header-inner #quick li ul li a.journal,#header #header-inner #quick li ul li a.journal:hover {
400 400 background:url("../images/icons/book.png") no-repeat scroll 4px 9px #FFF;
401 401 width:167px;
402 402 margin:0;
403 403 padding:12px 9px 7px 24px;
404 404 }
405 405
406 406 #header #header-inner #quick li ul li a.private_repo,#header #header-inner #quick li ul li a.private_repo:hover {
407 407 background:url("../images/icons/lock.png") no-repeat scroll 4px 9px #FFF;
408 408 min-width:167px;
409 409 margin:0;
410 410 padding:12px 9px 7px 24px;
411 411 }
412 412
413 413 #header #header-inner #quick li ul li a.public_repo,#header #header-inner #quick li ul li a.public_repo:hover {
414 414 background:url("../images/icons/lock_open.png") no-repeat scroll 4px 9px #FFF;
415 415 min-width:167px;
416 416 margin:0;
417 417 padding:12px 9px 7px 24px;
418 418 }
419 419
420 420 #header #header-inner #quick li ul li a.repos,#header #header-inner #quick li ul li a.repos:hover {
421 421 background:url("../images/icons/database_edit.png") no-repeat scroll 4px 9px #FFF;
422 422 width:167px;
423 423 margin:0;
424 424 padding:12px 9px 7px 24px;
425 425 }
426 426
427 427 #header #header-inner #quick li ul li a.users,#header #header-inner #quick li ul li a.users:hover {
428 428 background:#FFF url("../images/icons/user_edit.png") no-repeat 4px 9px;
429 429 width:167px;
430 430 margin:0;
431 431 padding:12px 9px 7px 24px;
432 432 }
433 433
434 434 #header #header-inner #quick li ul li a.settings,#header #header-inner #quick li ul li a.settings:hover {
435 435 background:#FFF url("../images/icons/cog.png") no-repeat 4px 9px;
436 436 width:167px;
437 437 margin:0;
438 438 padding:12px 9px 7px 24px;
439 439 }
440 440
441 441 #header #header-inner #quick li ul li a.permissions,#header #header-inner #quick li ul li a.permissions:hover {
442 442 background:#FFF url("../images/icons/key.png") no-repeat 4px 9px;
443 443 width:167px;
444 444 margin:0;
445 445 padding:12px 9px 7px 24px;
446 446 }
447 447
448 448 #header #header-inner #quick li ul li a.fork,#header #header-inner #quick li ul li a.fork:hover {
449 449 background:#FFF url("../images/icons/arrow_divide.png") no-repeat 4px 9px;
450 450 width:167px;
451 451 margin:0;
452 452 padding:12px 9px 7px 24px;
453 453 }
454 454
455 455 #header #header-inner #quick li ul li a.search,#header #header-inner #quick li ul li a.search:hover {
456 456 background:#FFF url("../images/icons/search_16.png") no-repeat 4px 9px;
457 457 width:167px;
458 458 margin:0;
459 459 padding:12px 9px 7px 24px;
460 460 }
461 461
462 462 #header #header-inner #quick li ul li a.delete,#header #header-inner #quick li ul li a.delete:hover {
463 463 background:#FFF url("../images/icons/delete.png") no-repeat 4px 9px;
464 464 width:167px;
465 465 margin:0;
466 466 padding:12px 9px 7px 24px;
467 467 }
468 468
469 469 #header #header-inner #quick li ul li a.branches,#header #header-inner #quick li ul li a.branches:hover {
470 470 background:#FFF url("../images/icons/arrow_branch.png") no-repeat 4px 9px;
471 471 width:167px;
472 472 margin:0;
473 473 padding:12px 9px 7px 24px;
474 474 }
475 475
476 476 #header #header-inner #quick li ul li a.tags,#header #header-inner #quick li ul li a.tags:hover {
477 477 background:#FFF url("../images/icons/tag_blue.png") no-repeat 4px 9px;
478 478 width:167px;
479 479 margin:0;
480 480 padding:12px 9px 7px 24px;
481 481 }
482 482
483 483 #header #header-inner #quick li ul li a.admin,#header #header-inner #quick li ul li a.admin:hover {
484 484 background:#FFF url("../images/icons/cog_edit.png") no-repeat 4px 9px;
485 485 width:167px;
486 486 margin:0;
487 487 padding:12px 9px 7px 24px;
488 488 }
489 489
490 490 #content #left {
491 491 left:0;
492 492 width:280px;
493 493 position:absolute;
494 494 }
495 495
496 496 #content #right {
497 497 margin:0 60px 10px 290px;
498 498 }
499 499
500 500 #content div.box {
501 501 clear:both;
502 502 overflow:hidden;
503 503 background:#fff;
504 504 margin:0 0 10px;
505 505 padding:0 0 10px;
506 506 }
507 507
508 508 #content div.box-left {
509 509 width:49%;
510 510 clear:none;
511 511 float:left;
512 512 margin:0 0 10px;
513 513 }
514 514
515 515 #content div.box-right {
516 516 width:49%;
517 517 clear:none;
518 518 float:right;
519 519 margin:0 0 10px;
520 520 }
521 521
522 522 #content div.box div.title {
523 523 clear:both;
524 524 overflow:hidden;
525 525 background:#369 url("../images/header_inner.png") repeat-x;
526 526 margin:0 0 20px;
527 527 padding:0;
528 528 }
529 529
530 530 #content div.box div.title h5 {
531 531 float:left;
532 532 border:none;
533 533 color:#fff;
534 534 text-transform:uppercase;
535 535 margin:0;
536 536 padding:11px 0 11px 10px;
537 537 }
538 538
539 539 #content div.box div.title ul.links li {
540 540 list-style:none;
541 541 float:left;
542 542 margin:0;
543 543 padding:0;
544 544 }
545 545
546 546 #content div.box div.title ul.links li a {
547 547 height:1%;
548 548 display:block;
549 549 float:left;
550 550 border-left:1px solid #316293;
551 551 color:#fff;
552 552 font-size:11px;
553 553 font-weight:700;
554 554 text-decoration:none;
555 555 margin:0;
556 556 padding:13px 16px 12px;
557 557 }
558 558
559 559 #content div.box h1,#content div.box h2,#content div.box h3,#content div.box h4,#content div.box h5,#content div.box h6 {
560 560 clear:both;
561 561 overflow:hidden;
562 562 border-bottom:1px solid #DDD;
563 563 margin:10px 20px;
564 564 padding:0 0 15px;
565 565 }
566 566
567 567 #content div.box p {
568 568 color:#5f5f5f;
569 569 font-size:12px;
570 570 line-height:150%;
571 571 margin:0 24px 10px;
572 572 padding:0;
573 573 }
574 574
575 575 #content div.box blockquote {
576 576 border-left:4px solid #DDD;
577 577 color:#5f5f5f;
578 578 font-size:11px;
579 579 line-height:150%;
580 580 margin:0 34px;
581 581 padding:0 0 0 14px;
582 582 }
583 583
584 584 #content div.box blockquote p {
585 585 margin:10px 0;
586 586 padding:0;
587 587 }
588 588
589 589 #content div.box dl {
590 590 margin:10px 24px;
591 591 }
592 592
593 593 #content div.box dt {
594 594 font-size:12px;
595 595 margin:0;
596 596 }
597 597
598 598 #content div.box dd {
599 599 font-size:12px;
600 600 margin:0;
601 601 padding:8px 0 8px 15px;
602 602 }
603 603
604 604 #content div.box li {
605 605 font-size:12px;
606 606 padding:4px 0;
607 607 }
608 608
609 609 #content div.box ul.disc,#content div.box ul.circle {
610 610 margin:10px 24px 10px 38px;
611 611 }
612 612
613 613 #content div.box ul.square {
614 614 margin:10px 24px 10px 40px;
615 615 }
616 616
617 617 #content div.box img.left {
618 618 border:none;
619 619 float:left;
620 620 margin:10px 10px 10px 0;
621 621 }
622 622
623 623 #content div.box img.right {
624 624 border:none;
625 625 float:right;
626 626 margin:10px 0 10px 10px;
627 627 }
628 628
629 629 #content div.box div.messages {
630 630 clear:both;
631 631 overflow:hidden;
632 632 margin:0 20px;
633 633 padding:0;
634 634 }
635 635
636 636 #content div.box div.message {
637 637 clear:both;
638 638 overflow:hidden;
639 639 margin:0;
640 640 padding:10px 0;
641 641 }
642 642
643 643 #content div.box div.message a {
644 644 font-weight:400 !important;
645 645 }
646 646
647 647 #content div.box div.message div.image {
648 648 float:left;
649 649 margin:9px 0 0 5px;
650 650 padding:6px;
651 651 }
652 652
653 653 #content div.box div.message div.image img {
654 654 vertical-align:middle;
655 655 margin:0;
656 656 }
657 657
658 658 #content div.box div.message div.text {
659 659 float:left;
660 660 margin:0;
661 661 padding:9px 6px;
662 662 }
663 663
664 664 #content div.box div.message div.dismiss a {
665 665 height:16px;
666 666 width:16px;
667 667 display:block;
668 668 background:url("../images/icons/cross.png") no-repeat;
669 669 margin:15px 14px 0 0;
670 670 padding:0;
671 671 }
672 672
673 673 #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 {
674 674 border:none;
675 675 margin:0;
676 676 padding:0;
677 677 }
678 678
679 679 #content div.box div.message div.text span {
680 680 height:1%;
681 681 display:block;
682 682 margin:0;
683 683 padding:5px 0 0;
684 684 }
685 685
686 686 #content div.box div.message-error {
687 687 height:1%;
688 688 clear:both;
689 689 overflow:hidden;
690 690 background:#FBE3E4;
691 691 border:1px solid #FBC2C4;
692 692 color:#860006;
693 693 }
694 694
695 695 #content div.box div.message-error h6 {
696 696 color:#860006;
697 697 }
698 698
699 699 #content div.box div.message-warning {
700 700 height:1%;
701 701 clear:both;
702 702 overflow:hidden;
703 703 background:#FFF6BF;
704 704 border:1px solid #FFD324;
705 705 color:#5f5200;
706 706 }
707 707
708 708 #content div.box div.message-warning h6 {
709 709 color:#5f5200;
710 710 }
711 711
712 712 #content div.box div.message-notice {
713 713 height:1%;
714 714 clear:both;
715 715 overflow:hidden;
716 716 background:#8FBDE0;
717 717 border:1px solid #6BACDE;
718 718 color:#003863;
719 719 }
720 720
721 721 #content div.box div.message-notice h6 {
722 722 color:#003863;
723 723 }
724 724
725 725 #content div.box div.message-success {
726 726 height:1%;
727 727 clear:both;
728 728 overflow:hidden;
729 729 background:#E6EFC2;
730 730 border:1px solid #C6D880;
731 731 color:#4e6100;
732 732 }
733 733
734 734 #content div.box div.message-success h6 {
735 735 color:#4e6100;
736 736 }
737 737
738 738 #content div.box div.form div.fields div.field {
739 739 height:1%;
740 740 border-bottom:1px solid #DDD;
741 741 clear:both;
742 742 margin:0;
743 743 padding:10px 0;
744 744 }
745 745
746 746 #content div.box div.form div.fields div.field-first {
747 747 padding:0 0 10px;
748 748 }
749 749
750 750 #content div.box div.form div.fields div.field-noborder {
751 751 border-bottom:0 !important;
752 752 }
753 753
754 754 #content div.box div.form div.fields div.field span.error-message {
755 755 height:1%;
756 756 display:inline-block;
757 757 color:red;
758 758 margin:8px 0 0 4px;
759 759 padding:0;
760 760 }
761 761
762 762 #content div.box div.form div.fields div.field span.success {
763 763 height:1%;
764 764 display:block;
765 765 color:#316309;
766 766 margin:8px 0 0;
767 767 padding:0;
768 768 }
769 769
770 770 #content div.box div.form div.fields div.field div.label {
771 771 left:80px;
772 772 width:auto;
773 773 position:absolute;
774 774 margin:0;
775 775 padding:8px 0 0 5px;
776 776 }
777 777
778 778 #content div.box-left div.form div.fields div.field div.label,#content div.box-right div.form div.fields div.field div.label {
779 779 clear:both;
780 780 overflow:hidden;
781 781 left:0;
782 782 width:auto;
783 783 position:relative;
784 784 margin:0;
785 785 padding:0 0 8px;
786 786 }
787 787
788 788 #content div.box div.form div.fields div.field div.label-select {
789 789 padding:2px 0 0 5px;
790 790 }
791 791
792 792 #content div.box-left div.form div.fields div.field div.label-select,#content div.box-right div.form div.fields div.field div.label-select {
793 793 padding:0 0 8px;
794 794 }
795 795
796 796 #content div.box-left div.form div.fields div.field div.label-textarea,#content div.box-right div.form div.fields div.field div.label-textarea {
797 797 padding:0 0 8px !important;
798 798 }
799 799
800 800 #content div.box div.form div.fields div.field div.label label {
801 801 color:#393939;
802 802 font-weight:700;
803 803 }
804 804
805 805 #content div.box div.form div.fields div.field div.input {
806 806 margin:0 0 0 200px;
807 807 }
808 808
809 809 #content div.box-left div.form div.fields div.field div.input,#content div.box-right div.form div.fields div.field div.input {
810 810 clear:both;
811 811 overflow:hidden;
812 812 border-top:1px solid #b3b3b3;
813 813 border-left:1px solid #b3b3b3;
814 814 border-right:1px solid #eaeaea;
815 815 border-bottom:1px solid #eaeaea;
816 816 margin:0;
817 817 padding:7px 7px 6px;
818 818 }
819 819
820 820 #content div.box div.form div.fields div.field div.input input {
821 821 background:#FFF;
822 822 border-top:1px solid #b3b3b3;
823 823 border-left:1px solid #b3b3b3;
824 824 border-right:1px solid #eaeaea;
825 825 border-bottom:1px solid #eaeaea;
826 826 color:#000;
827 827 font-family:Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
828 828 font-size:11px;
829 829 margin:0;
830 830 padding:7px 7px 6px;
831 831 }
832 832
833 833 #content div.box-left div.form div.fields div.field div.input input,#content div.box-right div.form div.fields div.field div.input input {
834 834 width:100%;
835 835 border:none;
836 836 padding:0;
837 837 }
838 838
839 839 #content div.box div.form div.fields div.field div.input input.small {
840 840 width:30%;
841 841 }
842 842
843 843 #content div.box div.form div.fields div.field div.input input.medium {
844 844 width:55%;
845 845 }
846 846
847 847 #content div.box div.form div.fields div.field div.input input.large {
848 848 width:85%;
849 849 }
850 850
851 851 #content div.box div.form div.fields div.field div.input input.date {
852 852 width:177px;
853 853 }
854 854
855 855 #content div.box div.form div.fields div.field div.input input.button {
856 856 background:#D4D0C8;
857 857 border-top:1px solid #FFF;
858 858 border-left:1px solid #FFF;
859 859 border-right:1px solid #404040;
860 860 border-bottom:1px solid #404040;
861 861 color:#000;
862 862 margin:0;
863 863 padding:4px 8px;
864 864 }
865 865
866 866 #content div.box div.form div.fields div.field div.input a.ui-input-file {
867 867 width:28px;
868 868 height:28px;
869 869 display:inline;
870 870 position:absolute;
871 871 overflow:hidden;
872 872 cursor:pointer;
873 873 background:#e5e3e3 url("../images/button_browse.png") no-repeat;
874 874 border:none;
875 875 text-decoration:none;
876 876 margin:0 0 0 6px;
877 877 padding:0;
878 878 }
879 879
880 880 #content div.box div.form div.fields div.field div.textarea {
881 881 border-top:1px solid #b3b3b3;
882 882 border-left:1px solid #b3b3b3;
883 883 border-right:1px solid #eaeaea;
884 884 border-bottom:1px solid #eaeaea;
885 885 margin:0 0 0 200px;
886 886 padding:10px;
887 887 }
888 888
889 889 #content div.box div.form div.fields div.field div.textarea-editor {
890 890 border:1px solid #ddd;
891 891 padding:0;
892 892 }
893 893
894 894 #content div.box div.form div.fields div.field div.textarea textarea {
895 895 width:100%;
896 896 height:220px;
897 897 overflow:hidden;
898 898 background:#FFF;
899 899 color:#000;
900 900 font-family:Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
901 901 font-size:11px;
902 902 outline:none;
903 903 border-width:0;
904 904 margin:0;
905 905 padding:0;
906 906 }
907 907
908 908 #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 {
909 909 width:100%;
910 910 height:100px;
911 911 }
912 912
913 913 #content div.box div.form div.fields div.field div.textarea table {
914 914 width:100%;
915 915 border:none;
916 916 margin:0;
917 917 padding:0;
918 918 }
919 919
920 920 #content div.box div.form div.fields div.field div.textarea table td {
921 921 background:#DDD;
922 922 border:none;
923 923 padding:0;
924 924 }
925 925
926 926 #content div.box div.form div.fields div.field div.textarea table td table {
927 927 width:auto;
928 928 border:none;
929 929 margin:0;
930 930 padding:0;
931 931 }
932 932
933 933 #content div.box div.form div.fields div.field div.textarea table td table td {
934 934 font-family:Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
935 935 font-size:11px;
936 936 padding:5px 5px 5px 0;
937 937 }
938 938
939 939 #content div.box div.form div.fields div.field div.textarea table td table td a.mceButtonActive {
940 940 background:#b1b1b1;
941 941 }
942 942
943 943 #content div.box div.form div.fields div.field div.select a.ui-selectmenu {
944 944 color:#565656;
945 945 text-decoration:none;
946 946 }
947 947
948 948 #content div.box div.form div.fields div.field input[type=text]:focus,#content div.box div.form div.fields div.field input[type=password]:focus,#content div.box div.form div.fields div.field input[type=file]:focus,#content div.box div.form div.fields div.field textarea:focus,#content div.box div.form div.fields div.field select:focus {
949 949 background:#f6f6f6;
950 950 border-color:#666;
951 951 }
952 952
953 953 div.form div.fields div.field div.button {
954 954 margin:0;
955 955 padding:0 0 0 8px;
956 956 }
957 957
958 958 div.form div.fields div.field div.highlight .ui-state-default {
959 959 background:#4e85bb url("../images/button_highlight.png") repeat-x;
960 960 border-top:1px solid #5c91a4;
961 961 border-left:1px solid #2a6f89;
962 962 border-right:1px solid #2b7089;
963 963 border-bottom:1px solid #1a6480;
964 964 color:#FFF;
965 965 margin:0;
966 966 padding:6px 12px;
967 967 }
968 968
969 969 div.form div.fields div.field div.highlight .ui-state-hover {
970 970 background:#46a0c1 url("../images/button_highlight_selected.png") repeat-x;
971 971 border-top:1px solid #78acbf;
972 972 border-left:1px solid #34819e;
973 973 border-right:1px solid #35829f;
974 974 border-bottom:1px solid #257897;
975 975 color:#FFF;
976 976 margin:0;
977 977 padding:6px 12px;
978 978 }
979 979
980 980 #content div.box div.form div.fields div.buttons div.highlight input.ui-state-default {
981 981 background:#4e85bb url("../../images/button_highlight.png") repeat-x;
982 982 border-top:1px solid #5c91a4;
983 983 border-left:1px solid #2a6f89;
984 984 border-right:1px solid #2b7089;
985 985 border-bottom:1px solid #1a6480;
986 986 color:#fff;
987 987 margin:0;
988 988 padding:6px 12px;
989 989 }
990 990
991 991 #content div.box div.form div.fields div.buttons div.highlight input.ui-state-hover {
992 992 background:#46a0c1 url("../../images/button_highlight_selected.png") repeat-x;
993 993 border-top:1px solid #78acbf;
994 994 border-left:1px solid #34819e;
995 995 border-right:1px solid #35829f;
996 996 border-bottom:1px solid #257897;
997 997 color:#fff;
998 998 margin:0;
999 999 padding:6px 12px;
1000 1000 }
1001 1001
1002 1002 #content div.box table {
1003 1003 width:100%;
1004 1004 border-collapse:collapse;
1005 1005 margin:0;
1006 1006 padding:0;
1007 1007 }
1008 1008
1009 1009 #content div.box table th {
1010 1010 background:#eee;
1011 1011 border-bottom:1px solid #ddd;
1012 1012 padding:5px 0px 5px 5px;
1013 1013 }
1014 1014
1015 1015 #content div.box table th.left {
1016 1016 text-align:left;
1017 1017 }
1018 1018
1019 1019 #content div.box table th.right {
1020 1020 text-align:right;
1021 1021 }
1022 1022
1023 1023 #content div.box table th.center {
1024 1024 text-align:center;
1025 1025 }
1026 1026
1027 1027 #content div.box table th.selected {
1028 1028 vertical-align:middle;
1029 1029 padding:0;
1030 1030 }
1031 1031
1032 1032 #content div.box table td {
1033 1033 background:#fff;
1034 1034 border-bottom:1px solid #cdcdcd;
1035 1035 vertical-align:middle;
1036 1036 padding:5px;
1037 1037 }
1038 1038
1039 1039 #content div.box table tr.selected td {
1040 1040 background:#FFC;
1041 1041 }
1042 1042
1043 1043 #content div.box table td.selected {
1044 1044 width:3%;
1045 1045 text-align:center;
1046 1046 vertical-align:middle;
1047 1047 padding:0;
1048 1048 }
1049 1049
1050 1050 #content div.box table td.action {
1051 1051 width:45%;
1052 1052 text-align:left;
1053 1053 }
1054 1054
1055 1055 #content div.box table td.date {
1056 1056 width:33%;
1057 1057 text-align:center;
1058 1058 }
1059 1059
1060 1060 #content div.box div.action {
1061 1061 float:right;
1062 1062 background:#FFF;
1063 1063 text-align:right;
1064 1064 margin:10px 0 0;
1065 1065 padding:0;
1066 1066 }
1067 1067
1068 1068 #content div.box div.action select {
1069 1069 font-family:Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
1070 1070 font-size:11px;
1071 1071 margin:0;
1072 1072 }
1073 1073
1074 1074 #content div.box div.action .ui-selectmenu {
1075 1075 margin:0;
1076 1076 padding:0;
1077 1077 }
1078 1078
1079 1079 #content div.box div.pagination {
1080 1080 height:1%;
1081 1081 clear:both;
1082 1082 overflow:hidden;
1083 1083 margin:10px 0 0;
1084 1084 padding:0;
1085 1085 }
1086 1086
1087 1087 #content div.box div.pagination ul.pager {
1088 1088 float:right;
1089 1089 text-align:right;
1090 1090 margin:0;
1091 1091 padding:0;
1092 1092 }
1093 1093
1094 1094 #content div.box div.pagination ul.pager li {
1095 1095 height:1%;
1096 1096 float:left;
1097 1097 list-style:none;
1098 1098 background:#ebebeb url("../images/pager.png") repeat-x;
1099 1099 border-top:1px solid #dedede;
1100 1100 border-left:1px solid #cfcfcf;
1101 1101 border-right:1px solid #c4c4c4;
1102 1102 border-bottom:1px solid #c4c4c4;
1103 1103 color:#4A4A4A;
1104 1104 font-weight:700;
1105 1105 margin:0 0 0 4px;
1106 1106 padding:0;
1107 1107 }
1108 1108
1109 1109 #content div.box div.pagination ul.pager li.separator {
1110 1110 padding:6px;
1111 1111 }
1112 1112
1113 1113 #content div.box div.pagination ul.pager li.current {
1114 1114 background:#b4b4b4 url("../images/pager_selected.png") repeat-x;
1115 1115 border-top:1px solid #ccc;
1116 1116 border-left:1px solid #bebebe;
1117 1117 border-right:1px solid #b1b1b1;
1118 1118 border-bottom:1px solid #afafaf;
1119 1119 color:#515151;
1120 1120 padding:6px;
1121 1121 }
1122 1122
1123 1123 #content div.box div.pagination ul.pager li a {
1124 1124 height:1%;
1125 1125 display:block;
1126 1126 float:left;
1127 1127 color:#515151;
1128 1128 text-decoration:none;
1129 1129 margin:0;
1130 1130 padding:6px;
1131 1131 }
1132 1132
1133 1133 #content div.box div.pagination ul.pager li a:hover,#content div.box div.pagination ul.pager li a:active {
1134 1134 background:#b4b4b4 url("../images/pager_selected.png") repeat-x;
1135 1135 border-top:1px solid #ccc;
1136 1136 border-left:1px solid #bebebe;
1137 1137 border-right:1px solid #b1b1b1;
1138 1138 border-bottom:1px solid #afafaf;
1139 1139 margin:-1px;
1140 1140 }
1141 1141
1142 1142 #content div.box div.pagination-wh {
1143 1143 height:1%;
1144 1144 clear:both;
1145 1145 overflow:hidden;
1146 1146 text-align:right;
1147 1147 margin:10px 0 0;
1148 1148 padding:0;
1149 1149 }
1150 1150
1151 1151 #content div.box div.pagination-right {
1152 1152 float:right;
1153 1153 }
1154 1154
1155 1155 #content div.box div.pagination-wh a,#content div.box div.pagination-wh span.pager_dotdot {
1156 1156 height:1%;
1157 1157 float:left;
1158 1158 background:#ebebeb url("../images/pager.png") repeat-x;
1159 1159 border-top:1px solid #dedede;
1160 1160 border-left:1px solid #cfcfcf;
1161 1161 border-right:1px solid #c4c4c4;
1162 1162 border-bottom:1px solid #c4c4c4;
1163 1163 color:#4A4A4A;
1164 1164 font-weight:700;
1165 1165 margin:0 0 0 4px;
1166 1166 padding:6px;
1167 1167 }
1168 1168
1169 1169 #content div.box div.pagination-wh span.pager_curpage {
1170 1170 height:1%;
1171 1171 float:left;
1172 1172 background:#b4b4b4 url("../images/pager_selected.png") repeat-x;
1173 1173 border-top:1px solid #ccc;
1174 1174 border-left:1px solid #bebebe;
1175 1175 border-right:1px solid #b1b1b1;
1176 1176 border-bottom:1px solid #afafaf;
1177 1177 color:#515151;
1178 1178 font-weight:700;
1179 1179 margin:0 0 0 4px;
1180 1180 padding:6px;
1181 1181 }
1182 1182
1183 1183 #content div.box div.pagination-wh a:hover,#content div.box div.pagination-wh a:active {
1184 1184 background:#b4b4b4 url("../images/pager_selected.png") repeat-x;
1185 1185 border-top:1px solid #ccc;
1186 1186 border-left:1px solid #bebebe;
1187 1187 border-right:1px solid #b1b1b1;
1188 1188 border-bottom:1px solid #afafaf;
1189 1189 text-decoration:none;
1190 1190 }
1191 1191
1192 1192 #content div.box div.traffic div.legend {
1193 1193 clear:both;
1194 1194 overflow:hidden;
1195 1195 border-bottom:1px solid #ddd;
1196 1196 margin:0 0 10px;
1197 1197 padding:0 0 10px;
1198 1198 }
1199 1199
1200 1200 #content div.box div.traffic div.legend h6 {
1201 1201 float:left;
1202 1202 border:none;
1203 1203 margin:0;
1204 1204 padding:0;
1205 1205 }
1206 1206
1207 1207 #content div.box div.traffic div.legend li {
1208 1208 list-style:none;
1209 1209 float:left;
1210 1210 font-size:11px;
1211 1211 margin:0;
1212 1212 padding:0 8px 0 4px;
1213 1213 }
1214 1214
1215 1215 #content div.box div.traffic div.legend li.visits {
1216 1216 border-left:12px solid #edc240;
1217 1217 }
1218 1218
1219 1219 #content div.box div.traffic div.legend li.pageviews {
1220 1220 border-left:12px solid #afd8f8;
1221 1221 }
1222 1222
1223 1223 #content div.box div.traffic table {
1224 1224 width:auto;
1225 1225 }
1226 1226
1227 1227 #content div.box div.traffic table td {
1228 1228 background:transparent;
1229 1229 border:none;
1230 1230 padding:2px 3px 3px;
1231 1231 }
1232 1232
1233 1233 #content div.box div.traffic table td.legendLabel {
1234 1234 padding:0 3px 2px;
1235 1235 }
1236 1236
1237 1237 #footer {
1238 1238 clear:both;
1239 1239 overflow:hidden;
1240 1240 text-align:right;
1241 1241 margin:0;
1242 1242 padding:0 30px 4px;
1243 1243 margin:-10px 0 0;
1244 1244 }
1245 1245
1246 1246 #footer div#footer-inner {
1247 1247 background:url("../images/header_inner.png") repeat-x scroll 0 0 #003367;
1248 1248 border-top:2px solid #FFFFFF;
1249 1249 }
1250 1250
1251 1251 #footer div#footer-inner p {
1252 1252 padding:15px 25px 15px 0;
1253 1253 color:#FFF;
1254 1254 font-weight:700;
1255 1255 }
1256 1256 #footer div#footer-inner .footer-link {
1257 1257 float:left;
1258 1258 padding-left:10px;
1259 1259 }
1260 1260 #footer div#footer-inner .footer-link a {
1261 1261 color:#FFF;
1262 1262 }
1263 1263
1264 1264 #login div.title {
1265 1265 width:420px;
1266 1266 clear:both;
1267 1267 overflow:hidden;
1268 1268 position:relative;
1269 1269 background:#003367 url("../../images/header_inner.png") repeat-x;
1270 1270 margin:0 auto;
1271 1271 padding:0;
1272 1272 }
1273 1273
1274 1274 #login div.inner {
1275 1275 width:380px;
1276 1276 background:#FFF url("../images/login.png") no-repeat top left;
1277 1277 border-top:none;
1278 1278 border-bottom:none;
1279 1279 margin:0 auto;
1280 1280 padding:20px;
1281 1281 }
1282 1282
1283 1283 #login div.form div.fields div.field div.label {
1284 1284 width:173px;
1285 1285 float:left;
1286 1286 text-align:right;
1287 1287 margin:2px 10px 0 0;
1288 1288 padding:5px 0 0 5px;
1289 1289 }
1290 1290
1291 1291 #login div.form div.fields div.field div.input input {
1292 1292 width:176px;
1293 1293 background:#FFF;
1294 1294 border-top:1px solid #b3b3b3;
1295 1295 border-left:1px solid #b3b3b3;
1296 1296 border-right:1px solid #eaeaea;
1297 1297 border-bottom:1px solid #eaeaea;
1298 1298 color:#000;
1299 1299 font-family:Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
1300 1300 font-size:11px;
1301 1301 margin:0;
1302 1302 padding:7px 7px 6px;
1303 1303 }
1304 1304
1305 1305 #login div.form div.fields div.buttons {
1306 1306 clear:both;
1307 1307 overflow:hidden;
1308 1308 border-top:1px solid #DDD;
1309 1309 text-align:right;
1310 1310 margin:0;
1311 1311 padding:10px 0 0;
1312 1312 }
1313 1313
1314 1314 #login div.form div.links {
1315 1315 clear:both;
1316 1316 overflow:hidden;
1317 1317 margin:10px 0 0;
1318 1318 padding:0 0 2px;
1319 1319 }
1320 1320
1321 1321 #register div.title {
1322 1322 width:420px;
1323 1323 clear:both;
1324 1324 overflow:hidden;
1325 1325 position:relative;
1326 1326 background:#003367 url("../images/header_inner.png") repeat-x;
1327 1327 margin:0 auto;
1328 1328 padding:0;
1329 1329 }
1330 1330
1331 1331 #register div.inner {
1332 1332 width:380px;
1333 1333 background:#FFF;
1334 1334 border-top:none;
1335 1335 border-bottom:none;
1336 1336 margin:0 auto;
1337 1337 padding:20px;
1338 1338 }
1339 1339
1340 1340 #register div.form div.fields div.field div.label {
1341 1341 width:100px;
1342 1342 float:left;
1343 1343 text-align:right;
1344 1344 margin:2px 10px 0 0;
1345 1345 padding:5px 0 0 5px;
1346 1346 }
1347 1347
1348 1348 #register div.form div.fields div.field div.input input {
1349 1349 width:245px;
1350 1350 background:#FFF;
1351 1351 border-top:1px solid #b3b3b3;
1352 1352 border-left:1px solid #b3b3b3;
1353 1353 border-right:1px solid #eaeaea;
1354 1354 border-bottom:1px solid #eaeaea;
1355 1355 color:#000;
1356 1356 font-family:Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
1357 1357 font-size:11px;
1358 1358 margin:0;
1359 1359 padding:7px 7px 6px;
1360 1360 }
1361 1361
1362 1362 #register div.form div.fields div.buttons {
1363 1363 clear:both;
1364 1364 overflow:hidden;
1365 1365 border-top:1px solid #DDD;
1366 1366 text-align:left;
1367 1367 margin:0;
1368 1368 padding:10px 0 0 114px;
1369 1369 }
1370 1370
1371 1371 #register div.form div.fields div.buttons div.highlight input.ui-state-default {
1372 1372 background:url("../images/button_highlight.png") repeat-x scroll 0 0 #4E85BB;
1373 1373 color:#FFF;
1374 1374 border-color:#5C91A4 #2B7089 #1A6480 #2A6F89;
1375 1375 border-style:solid;
1376 1376 border-width:1px;
1377 1377 }
1378 1378
1379 1379 #register div.form div.activation_msg {
1380 1380 padding-top:4px;
1381 1381 padding-bottom:4px;
1382 1382 }
1383 1383
1384 1384 .trending_language_tbl,.trending_language_tbl td {
1385 1385 border:0 !important;
1386 1386 margin:0 !important;
1387 1387 padding:0 !important;
1388 1388 }
1389 1389
1390 1390 .trending_language {
1391 1391 background-color:#003367;
1392 1392 color:#FFF;
1393 1393 display:block;
1394 1394 min-width:20px;
1395 1395 text-decoration:none;
1396 1396 height:12px;
1397 1397 margin-bottom:4px;
1398 1398 margin-left:5px;
1399 1399 white-space:pre;
1400 1400 padding:3px;
1401 1401 }
1402 1402
1403 1403 h3.files_location {
1404 1404 font-size:1.8em;
1405 1405 font-weight:700;
1406 1406 border-bottom:none !important;
1407 1407 margin:10px 0 !important;
1408 1408 }
1409 1409
1410 1410 #files_data dl dt {
1411 1411 float:left;
1412 1412 width:115px;
1413 1413 margin:0 !important;
1414 1414 padding:5px;
1415 1415 }
1416 1416
1417 1417 #files_data dl dd {
1418 1418 margin:0 !important;
1419 1419 padding:5px !important;
1420 1420 }
1421 1421
1422 1422 #changeset_content {
1423 1423 border:1px solid #CCC;
1424 1424 padding:5px;
1425 1425 }
1426 1426
1427 1427 #changeset_content .container {
1428 1428 min-height:120px;
1429 1429 font-size:1.2em;
1430 1430 overflow:hidden;
1431 1431 }
1432 1432
1433 1433 #changeset_content .container .right {
1434 1434 float:right;
1435 1435 width:25%;
1436 1436 text-align:right;
1437 1437 }
1438 1438
1439 1439 #changeset_content .container .left .message {
1440 1440 font-style:italic;
1441 1441 color:#556CB5;
1442 1442 white-space:pre-wrap;
1443 1443 }
1444 1444
1445 1445 .cs_files .cs_added {
1446 1446 background:url("../images/icons/page_white_add.png") no-repeat scroll 3px;
1447 1447 height:16px;
1448 1448 padding-left:20px;
1449 1449 margin-top:7px;
1450 1450 text-align:left;
1451 1451 }
1452 1452
1453 1453 .cs_files .cs_changed {
1454 1454 background:url("../images/icons/page_white_edit.png") no-repeat scroll 3px;
1455 1455 height:16px;
1456 1456 padding-left:20px;
1457 1457 margin-top:7px;
1458 1458 text-align:left;
1459 1459 }
1460 1460
1461 1461 .cs_files .cs_removed {
1462 1462 background:url("../images/icons/page_white_delete.png") no-repeat scroll 3px;
1463 1463 height:16px;
1464 1464 padding-left:20px;
1465 1465 margin-top:7px;
1466 1466 text-align:left;
1467 1467 }
1468 1468
1469 1469 #graph {
1470 1470 overflow:hidden;
1471 1471 }
1472 1472
1473 1473 #graph_nodes {
1474 1474 width:160px;
1475 1475 float:left;
1476 1476 margin-left:-50px;
1477 1477 margin-top:5px;
1478 1478 }
1479 1479
1480 1480 #graph_content {
1481 1481 width:800px;
1482 1482 float:left;
1483 1483 }
1484 1484
1485 1485 #graph_content .container_header {
1486 1486 border:1px solid #CCC;
1487 1487 padding:10px;
1488 1488 }
1489 1489
1490 1490 #graph_content .container {
1491 1491 border-bottom:1px solid #CCC;
1492 1492 border-left:1px solid #CCC;
1493 1493 border-right:1px solid #CCC;
1494 1494 min-height:80px;
1495 1495 overflow:hidden;
1496 1496 font-size:1.2em;
1497 1497 }
1498 1498
1499 1499 #graph_content .container .right {
1500 1500 float:right;
1501 1501 width:28%;
1502 1502 text-align:right;
1503 1503 padding-bottom:5px;
1504 1504 }
1505 1505
1506 1506 #graph_content .container .left .date {
1507 1507 font-weight:700;
1508 1508 padding-bottom:5px;
1509 1509 }
1510 1510
1511 1511 #graph_content .container .left .message {
1512 1512 font-size:100%;
1513 1513 padding-top:3px;
1514 1514 white-space:pre-wrap;
1515 1515 }
1516 1516
1517 1517 .right div {
1518 1518 clear:both;
1519 1519 }
1520 1520
1521 1521 .right .changes .added,.changed,.removed {
1522 1522 border:1px solid #DDD;
1523 1523 display:block;
1524 1524 float:right;
1525 1525 text-align:center;
1526 1526 min-width:15px;
1527 1527 }
1528 1528
1529 1529 .right .changes .added {
1530 1530 background:#BFB;
1531 1531 }
1532 1532
1533 1533 .right .changes .changed {
1534 1534 background:#FD8;
1535 1535 }
1536 1536
1537 1537 .right .changes .removed {
1538 1538 background:#F88;
1539 1539 }
1540 1540
1541 1541 .right .merge {
1542 1542 vertical-align:top;
1543 1543 font-size:0.75em;
1544 1544 font-weight:700;
1545 1545 }
1546 1546
1547 1547 .right .parent {
1548 1548 font-size:90%;
1549 1549 font-family:monospace;
1550 1550 }
1551 1551
1552 1552 .right .logtags .branchtag {
1553 1553 background:#FFF url("../images/icons/arrow_branch.png") no-repeat right 6px;
1554 1554 display:block;
1555 1555 font-size:0.8em;
1556 1556 padding:11px 16px 0 0;
1557 1557 }
1558 1558
1559 1559 .right .logtags .tagtag {
1560 1560 background:#FFF url("../images/icons/tag_blue.png") no-repeat right 6px;
1561 1561 display:block;
1562 1562 font-size:0.8em;
1563 1563 padding:11px 16px 0 0;
1564 1564 }
1565 1565
1566 1566 div.browserblock {
1567 1567 overflow:hidden;
1568 1568 border:1px solid #ccc;
1569 1569 background:#f8f8f8;
1570 1570 font-size:100%;
1571 1571 line-height:125%;
1572 1572 padding:0;
1573 1573 }
1574 1574
1575 1575 div.browserblock .browser-header {
1576 1576 border-bottom:1px solid #CCC;
1577 1577 background:#FFF;
1578 1578 color:blue;
1579 1579 padding:10px 0;
1580 1580 }
1581 1581
1582 1582 div.browserblock .browser-header span {
1583 1583 margin-left:25px;
1584 1584 font-weight:700;
1585 1585 }
1586 1586
1587 1587 div.browserblock .browser-body {
1588 1588 background:#EEE;
1589 1589 }
1590 1590
1591 1591 table.code-browser {
1592 1592 border-collapse:collapse;
1593 1593 width:100%;
1594 1594 }
1595 1595
1596 1596 table.code-browser tr {
1597 1597 margin:3px;
1598 1598 }
1599 1599
1600 1600 table.code-browser thead th {
1601 1601 background-color:#EEE;
1602 1602 height:20px;
1603 1603 font-size:1.1em;
1604 1604 font-weight:700;
1605 1605 text-align:left;
1606 1606 padding-left:10px;
1607 1607 }
1608 1608
1609 1609 table.code-browser tbody td {
1610 1610 padding-left:10px;
1611 1611 height:20px;
1612 1612 }
1613 1613
1614 1614 table.code-browser .browser-file {
1615 1615 background:url("../images/icons/document_16.png") no-repeat scroll 3px;
1616 1616 height:16px;
1617 1617 padding-left:20px;
1618 1618 text-align:left;
1619 1619 }
1620 1620
1621 1621 table.code-browser .browser-dir {
1622 1622 background:url("../images/icons/folder_16.png") no-repeat scroll 3px;
1623 1623 height:16px;
1624 1624 padding-left:20px;
1625 1625 text-align:left;
1626 1626 }
1627 1627
1628 1628 .box .search {
1629 1629 clear:both;
1630 1630 overflow:hidden;
1631 1631 margin:0;
1632 1632 padding:0 20px 10px;
1633 1633 }
1634 1634
1635 1635 .box .search div.search_path {
1636 1636 background:none repeat scroll 0 0 #EEE;
1637 1637 border:1px solid #CCC;
1638 1638 color:blue;
1639 1639 margin-bottom:10px;
1640 1640 padding:10px 0;
1641 1641 }
1642 1642
1643 1643 .box .search div.search_path div.link {
1644 1644 font-weight:700;
1645 1645 margin-left:25px;
1646 1646 }
1647 1647
1648 1648 .box .search div.search_path div.link a {
1649 1649 color:#003367;
1650 1650 cursor:pointer;
1651 1651 text-decoration:none;
1652 1652 }
1653 1653
1654 1654 #path_unlock {
1655 1655 color:red;
1656 1656 font-size:1.2em;
1657 1657 padding-left:4px;
1658 1658 }
1659 1659
1660 1660 .info_box * {
1661 1661 background:url("../../images/pager.png") repeat-x scroll 0 0 #EBEBEB;
1662 1662 color:#4A4A4A;
1663 1663 font-weight:700;
1664 1664 height:1%;
1665 1665 display:inline;
1666 1666 border-color:#DEDEDE #C4C4C4 #C4C4C4 #CFCFCF;
1667 1667 border-style:solid;
1668 1668 border-width:1px;
1669 1669 padding:4px 6px;
1670 1670 }
1671 1671
1672 1672 .info_box span {
1673 1673 margin-left:3px;
1674 1674 margin-right:3px;
1675 1675 }
1676 1676
1677 1677 .info_box input#at_rev {
1678 1678 text-align:center;
1679 1679 padding:5px 3px 3px 2px;
1680 1680 }
1681 1681
1682 1682 .info_box input#view {
1683 1683 text-align:center;
1684 1684 padding:4px 3px 2px 2px;
1685 1685 }
1686 1686
1687 1687 .yui-overlay,.yui-panel-container {
1688 1688 visibility:hidden;
1689 1689 position:absolute;
1690 1690 z-index:2;
1691 1691 }
1692 1692
1693 1693 .yui-tt {
1694 1694 visibility:hidden;
1695 1695 position:absolute;
1696 1696 color:#666;
1697 1697 background-color:#FFF;
1698 1698 font-family:arial, helvetica, verdana, sans-serif;
1699 1699 border:2px solid #003367;
1700 1700 font:100% sans-serif;
1701 1701 width:auto;
1702 1702 opacity:1px;
1703 1703 padding:8px;
1704 1704 white-space: pre;
1705 1705 }
1706 1706
1707 1707 .ac {
1708 1708 vertical-align:top;
1709 1709 }
1710 1710
1711 1711 .ac .yui-ac {
1712 1712 position:relative;
1713 1713 font-family:arial;
1714 1714 font-size:100%;
1715 1715 }
1716 1716
1717 1717 .ac .perm_ac {
1718 1718 width:15em;
1719 1719 }
1720 1720
1721 1721 .ac .yui-ac-input {
1722 1722 width:100%;
1723 1723 }
1724 1724
1725 1725 .ac .yui-ac-container {
1726 1726 position:absolute;
1727 1727 top:1.6em;
1728 1728 width:100%;
1729 1729 }
1730 1730
1731 1731 .ac .yui-ac-content {
1732 1732 position:absolute;
1733 1733 width:100%;
1734 1734 border:1px solid gray;
1735 1735 background:#fff;
1736 1736 overflow:hidden;
1737 1737 z-index:9050;
1738 1738 }
1739 1739
1740 1740 .ac .yui-ac-shadow {
1741 1741 position:absolute;
1742 1742 width:100%;
1743 1743 background:#000;
1744 1744 -moz-opacity:0.1px;
1745 1745 opacity:.10;
1746 1746 filter:alpha(opacity = 10);
1747 1747 z-index:9049;
1748 1748 margin:.3em;
1749 1749 }
1750 1750
1751 1751 .ac .yui-ac-content ul {
1752 1752 width:100%;
1753 1753 margin:0;
1754 1754 padding:0;
1755 1755 }
1756 1756
1757 1757 .ac .yui-ac-content li {
1758 1758 cursor:default;
1759 1759 white-space:nowrap;
1760 1760 margin:0;
1761 1761 padding:2px 5px;
1762 1762 }
1763 1763
1764 1764 .ac .yui-ac-content li.yui-ac-prehighlight {
1765 1765 background:#B3D4FF;
1766 1766 }
1767 1767
1768 1768 .ac .yui-ac-content li.yui-ac-highlight {
1769 1769 background:#556CB5;
1770 1770 color:#FFF;
1771 1771 }
1772 1772
1773 1773 .add_icon {
1774 1774 background:url("../images/icons/add.png") no-repeat scroll 3px;
1775 1775 height:16px;
1776 1776 padding-left:20px;
1777 1777 padding-top:1px;
1778 1778 text-align:left;
1779 1779 }
1780 1780
1781 1781 .edit_icon {
1782 1782 background:url("../images/icons/folder_edit.png") no-repeat scroll 3px;
1783 1783 height:16px;
1784 1784 padding-left:20px;
1785 1785 padding-top:1px;
1786 1786 text-align:left;
1787 1787 }
1788 1788
1789 1789 .delete_icon {
1790 1790 background:url("../images/icons/delete.png") no-repeat scroll 3px;
1791 1791 height:16px;
1792 1792 padding-left:20px;
1793 1793 padding-top:1px;
1794 1794 text-align:left;
1795 1795 }
1796 1796
1797 1797 .rss_icon {
1798 1798 background:url("../images/icons/rss_16.png") no-repeat scroll 3px;
1799 1799 height:16px;
1800 1800 padding-left:20px;
1801 1801 padding-top:1px;
1802 1802 text-align:left;
1803 1803 }
1804 1804
1805 1805 .atom_icon {
1806 1806 background:url("../images/icons/atom.png") no-repeat scroll 3px;
1807 1807 height:16px;
1808 1808 padding-left:20px;
1809 1809 padding-top:1px;
1810 1810 text-align:left;
1811 1811 }
1812 1812
1813 1813 .archive_icon {
1814 1814 background:url("../images/icons/compress.png") no-repeat scroll 3px;
1815 1815 height:16px;
1816 1816 padding-left:20px;
1817 1817 text-align:left;
1818 1818 padding-top:1px;
1819 1819 }
1820 1820
1821 1821 .action_button {
1822 1822 border:0;
1823 1823 display:block;
1824 1824 }
1825 1825
1826 1826 .action_button:hover {
1827 1827 border:0;
1828 1828 text-decoration:underline;
1829 1829 cursor:pointer;
1830 1830 }
1831 1831
1832 1832 #switch_repos {
1833 1833 position:absolute;
1834 1834 height:25px;
1835 1835 z-index:1;
1836 1836 }
1837 1837
1838 1838 #switch_repos select {
1839 1839 min-width:150px;
1840 1840 max-height:250px;
1841 1841 z-index:1;
1842 1842 }
1843 1843
1844 1844 .breadcrumbs {
1845 1845 border:medium none;
1846 1846 color:#FFF;
1847 1847 float:left;
1848 1848 text-transform:uppercase;
1849 1849 font-weight:700;
1850 1850 font-size:14px;
1851 1851 margin:0;
1852 1852 padding:11px 0 11px 10px;
1853 1853 }
1854 1854
1855 1855 .breadcrumbs a {
1856 1856 color:#FFF;
1857 1857 }
1858 1858
1859 1859 .flash_msg ul {
1860 1860 margin:0;
1861 1861 padding:0 0 10px;
1862 1862 }
1863 1863
1864 1864 .error_msg {
1865 1865 background-color:#FFCFCF;
1866 1866 background-image:url("../../images/icons/error_msg.png");
1867 1867 border:1px solid #FF9595;
1868 1868 color:#C30;
1869 1869 }
1870 1870
1871 1871 .warning_msg {
1872 1872 background-color:#FFFBCC;
1873 1873 background-image:url("../../images/icons/warning_msg.png");
1874 1874 border:1px solid #FFF35E;
1875 1875 color:#C69E00;
1876 1876 }
1877 1877
1878 1878 .success_msg {
1879 1879 background-color:#D5FFCF;
1880 1880 background-image:url("../../images/icons/success_msg.png");
1881 1881 border:1px solid #97FF88;
1882 1882 color:#090;
1883 1883 }
1884 1884
1885 1885 .notice_msg {
1886 1886 background-color:#DCE3FF;
1887 1887 background-image:url("../../images/icons/notice_msg.png");
1888 1888 border:1px solid #93A8FF;
1889 1889 color:#556CB5;
1890 1890 }
1891 1891
1892 1892 .success_msg,.error_msg,.notice_msg,.warning_msg {
1893 1893 background-position:10px center;
1894 1894 background-repeat:no-repeat;
1895 1895 font-size:12px;
1896 1896 font-weight:700;
1897 1897 min-height:14px;
1898 1898 line-height:14px;
1899 1899 margin-bottom:0;
1900 1900 margin-top:0;
1901 1901 display:block;
1902 1902 overflow:auto;
1903 1903 padding:6px 10px 6px 40px;
1904 1904 }
1905 1905
1906 1906 #msg_close {
1907 1907 background:transparent url("../../icons/cross_grey_small.png") no-repeat scroll 0 0;
1908 1908 cursor:pointer;
1909 1909 height:16px;
1910 1910 position:absolute;
1911 1911 right:5px;
1912 1912 top:5px;
1913 1913 width:16px;
1914 1914 }
1915 1915
1916 1916 div#legend_container table,div#legend_choices table {
1917 1917 width:auto !important;
1918 1918 }
1919 1919
1920 1920 table#permissions_manage {
1921 1921 width:0 !important;
1922 1922 }
1923 1923
1924 1924 table#permissions_manage span.private_repo_msg {
1925 1925 font-size:0.8em;
1926 1926 opacity:0.6px;
1927 1927 }
1928 1928
1929 1929 table#permissions_manage td.private_repo_msg {
1930 1930 font-size:0.8em;
1931 1931 }
1932 1932
1933 1933 table#permissions_manage tr#add_perm_input td {
1934 1934 vertical-align:middle;
1935 1935 }
1936 1936
1937 1937 div.gravatar {
1938 1938 background-color:#FFF;
1939 1939 border:1px solid #D0D0D0;
1940 1940 float:left;
1941 1941 margin-right:0.7em;
1942 1942 padding:2px 2px 0;
1943 1943 }
1944 1944
1945 1945 #header,#content,#footer {
1946 1946 min-width:1024px;
1947 1947 }
1948 1948
1949 1949 #content {
1950 1950 min-height:100%;
1951 1951 clear:both;
1952 1952 overflow:hidden;
1953 1953 padding:14px 30px;
1954 1954 }
1955 1955
1956 1956 #content div.box div.title div.search {
1957 1957 background:url("../../images/title_link.png") no-repeat top left;
1958 1958 border-left:1px solid #316293;
1959 1959 }
1960 1960
1961 1961 #content div.box div.title div.search div.input input {
1962 1962 border:1px solid #316293;
1963 1963 }
1964 1964
1965 1965 #content div.box div.title div.search div.button input.ui-state-default {
1966 1966 background:#4e85bb url("../../images/button_highlight.png") repeat-x;
1967 1967 border:1px solid #316293;
1968 1968 border-left:none;
1969 1969 color:#FFF;
1970 1970 }
1971 1971
1972 1972 #content div.box div.title div.search div.button input.ui-state-hover {
1973 1973 background:#46a0c1 url("../../images/button_highlight_selected.png") repeat-x;
1974 1974 border:1px solid #316293;
1975 1975 border-left:none;
1976 1976 color:#FFF;
1977 1977 }
1978 1978
1979 1979 #content div.box div.form div.fields div.field div.highlight .ui-state-default {
1980 1980 background:#4e85bb url("../../images/button_highlight.png") repeat-x;
1981 1981 border-top:1px solid #5c91a4;
1982 1982 border-left:1px solid #2a6f89;
1983 1983 border-right:1px solid #2b7089;
1984 1984 border-bottom:1px solid #1a6480;
1985 1985 color:#fff;
1986 1986 }
1987 1987
1988 1988 #content div.box div.form div.fields div.field div.highlight .ui-state-hover {
1989 1989 background:#46a0c1 url("../../images/button_highlight_selected.png") repeat-x;
1990 1990 border-top:1px solid #78acbf;
1991 1991 border-left:1px solid #34819e;
1992 1992 border-right:1px solid #35829f;
1993 1993 border-bottom:1px solid #257897;
1994 1994 color:#fff;
1995 1995 }
1996 1996
1997 1997 ins,div.options a:hover {
1998 1998 text-decoration:none;
1999 1999 }
2000 2000
2001 2001 img,#header #header-inner #quick li a:hover span.normal,#header #header-inner #quick li ul li.last,#content div.box div.form div.fields div.field div.textarea table td table td a,#clone_url {
2002 2002 border:none;
2003 2003 }
2004 2004
2005 2005 img.icon,.right .merge img {
2006 2006 vertical-align:bottom;
2007 2007 }
2008 2008
2009 2009 #header ul#logged-user,#content div.box div.title ul.links,#content div.box div.message div.dismiss,#content div.box div.traffic div.legend ul {
2010 2010 float:right;
2011 2011 margin:0;
2012 2012 padding:0;
2013 2013 }
2014 2014
2015 2015 #header #header-inner #home,#header #header-inner #logo,#content div.box ul.left,#content div.box ol.left,#content div.box div.pagination-left,div#commit_history,div#legend_data,div#legend_container,div#legend_choices {
2016 2016 float:left;
2017 2017 }
2018 2018
2019 2019 #header #header-inner #quick li:hover ul ul,#header #header-inner #quick li:hover ul ul ul,#header #header-inner #quick li:hover ul ul ul ul,#content #left #menu ul.closed,#content #left #menu li ul.collapsed,.yui-tt-shadow {
2020 2020 display:none;
2021 2021 }
2022 2022
2023 2023 #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 {
2024 2024 display:block;
2025 2025 }
2026 2026
2027 2027 #content div.box div.title ul.links li a:hover,#content div.box div.title ul.links li.ui-tabs-selected a {
2028 2028 color:#bfe3ff;
2029 2029 }
2030 2030
2031 2031 #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 {
2032 2032 margin:10px 24px 10px 44px;
2033 2033 }
2034 2034
2035 2035 #content div.box div.form,#content div.box div.table,#content div.box div.traffic {
2036 2036 clear:both;
2037 2037 overflow:hidden;
2038 2038 margin:0;
2039 2039 padding:0 20px 10px;
2040 2040 }
2041 2041
2042 2042 #content div.box div.form div.fields,#login div.form,#login div.form div.fields,#register div.form,#register div.form div.fields {
2043 2043 clear:both;
2044 2044 overflow:hidden;
2045 2045 margin:0;
2046 2046 padding:0;
2047 2047 }
2048 2048
2049 2049 #content div.box div.form div.fields div.field div.label-checkbox,#content div.box div.form div.fields div.field div.label-radio,#content div.box div.form div.fields div.field div.label-textarea {
2050 2050 padding:0 0 0 5px !important;
2051 2051 }
2052 2052
2053 2053 #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 {
2054 2054 height:1%;
2055 2055 display:block;
2056 2056 color:#363636;
2057 2057 margin:0;
2058 2058 padding:2px 0 0;
2059 2059 }
2060 2060
2061 2061 #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 {
2062 2062 background:#FBE3E4;
2063 2063 border-top:1px solid #e1b2b3;
2064 2064 border-left:1px solid #e1b2b3;
2065 2065 border-right:1px solid #FBC2C4;
2066 2066 border-bottom:1px solid #FBC2C4;
2067 2067 }
2068 2068
2069 2069 #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 {
2070 2070 background:#E6EFC2;
2071 2071 border-top:1px solid #cebb98;
2072 2072 border-left:1px solid #cebb98;
2073 2073 border-right:1px solid #c6d880;
2074 2074 border-bottom:1px solid #c6d880;
2075 2075 }
2076 2076
2077 2077 #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 {
2078 2078 margin:0;
2079 2079 }
2080 2080
2081 2081 #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 {
2082 2082 margin:0 0 0 200px;
2083 2083 padding:0;
2084 2084 }
2085 2085
2086 2086 #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 {
2087 2087 color:#000;
2088 2088 text-decoration:none;
2089 2089 }
2090 2090
2091 2091 #content div.box div.form div.fields div.field div.select a.ui-selectmenu-focus,#content div.box div.action a.ui-selectmenu-focus {
2092 2092 border:1px solid #666;
2093 2093 }
2094 2094
2095 2095 #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 {
2096 2096 clear:both;
2097 2097 overflow:hidden;
2098 2098 margin:0;
2099 padding:2px 0;
2099 padding:2px 2px;
2100 2100 }
2101 2101
2102 2102 #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 {
2103 2103 float:left;
2104 2104 margin:0;
2105 2105 }
2106 2106
2107 2107 #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 {
2108 2108 height:1%;
2109 2109 display:block;
2110 2110 float:left;
2111 2111 margin:3px 0 0 4px;
2112 2112 }
2113 2113
2114 2114 div.form div.fields div.field div.button input,#content div.box div.form div.fields div.buttons input,div.form div.fields div.buttons input,#content div.box div.action div.button input {
2115 2115 color:#000;
2116 2116 font-family:Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
2117 2117 font-size:11px;
2118 2118 font-weight:700;
2119 2119 margin:0;
2120 2120 }
2121 2121
2122 2122 div.form div.fields div.field div.button .ui-state-default,#content div.box div.form div.fields div.buttons input.ui-state-default {
2123 2123 background:#e5e3e3 url("../images/button.png") repeat-x;
2124 2124 border-top:1px solid #DDD;
2125 2125 border-left:1px solid #c6c6c6;
2126 2126 border-right:1px solid #DDD;
2127 2127 border-bottom:1px solid #c6c6c6;
2128 2128 color:#515151;
2129 2129 outline:none;
2130 2130 margin:0;
2131 2131 padding:6px 12px;
2132 2132 }
2133 2133
2134 2134 div.form div.fields div.field div.button .ui-state-hover,#content div.box div.form div.fields div.buttons input.ui-state-hover {
2135 2135 background:#b4b4b4 url("../images/button_selected.png") repeat-x;
2136 2136 border-top:1px solid #ccc;
2137 2137 border-left:1px solid #bebebe;
2138 2138 border-right:1px solid #b1b1b1;
2139 2139 border-bottom:1px solid #afafaf;
2140 2140 color:#515151;
2141 2141 outline:none;
2142 2142 margin:0;
2143 2143 padding:6px 12px;
2144 2144 }
2145 2145
2146 2146 div.form div.fields div.field div.highlight,#content div.box div.form div.fields div.buttons div.highlight {
2147 2147 display:inline;
2148 2148 }
2149 2149
2150 2150 #content div.box div.form div.fields div.buttons,div.form div.fields div.buttons {
2151 2151 margin:10px 0 0 200px;
2152 2152 padding:0;
2153 2153 }
2154 2154
2155 2155 #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 {
2156 2156 margin:10px 0 0;
2157 2157 }
2158 2158
2159 2159 #content div.box table td.user,#content div.box table td.address {
2160 2160 width:10%;
2161 2161 text-align:center;
2162 2162 }
2163 2163
2164 2164 #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 {
2165 2165 text-align:right;
2166 2166 margin:6px 0 0;
2167 2167 padding:0;
2168 2168 }
2169 2169
2170 2170 #content div.box div.action div.button input.ui-state-default,#login div.form div.fields div.buttons input.ui-state-default,#register div.form div.fields div.buttons input.ui-state-default {
2171 2171 background:#e5e3e3 url("../images/button.png") repeat-x;
2172 2172 border-top:1px solid #DDD;
2173 2173 border-left:1px solid #c6c6c6;
2174 2174 border-right:1px solid #DDD;
2175 2175 border-bottom:1px solid #c6c6c6;
2176 2176 color:#515151;
2177 2177 margin:0;
2178 2178 padding:6px 12px;
2179 2179 }
2180 2180
2181 2181 #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 {
2182 2182 background:#b4b4b4 url("../images/button_selected.png") repeat-x;
2183 2183 border-top:1px solid #ccc;
2184 2184 border-left:1px solid #bebebe;
2185 2185 border-right:1px solid #b1b1b1;
2186 2186 border-bottom:1px solid #afafaf;
2187 2187 color:#515151;
2188 2188 margin:0;
2189 2189 padding:6px 12px;
2190 2190 }
2191 2191
2192 2192 #content div.box div.pagination div.results,#content div.box div.pagination-wh div.results {
2193 2193 text-align:left;
2194 2194 float:left;
2195 2195 margin:0;
2196 2196 padding:0;
2197 2197 }
2198 2198
2199 2199 #content div.box div.pagination div.results span,#content div.box div.pagination-wh div.results span {
2200 2200 height:1%;
2201 2201 display:block;
2202 2202 float:left;
2203 2203 background:#ebebeb url("../images/pager.png") repeat-x;
2204 2204 border-top:1px solid #dedede;
2205 2205 border-left:1px solid #cfcfcf;
2206 2206 border-right:1px solid #c4c4c4;
2207 2207 border-bottom:1px solid #c4c4c4;
2208 2208 color:#4A4A4A;
2209 2209 font-weight:700;
2210 2210 margin:0;
2211 2211 padding:6px 8px;
2212 2212 }
2213 2213
2214 2214 #content div.box div.pagination ul.pager li.disabled,#content div.box div.pagination-wh a.disabled {
2215 2215 color:#B4B4B4;
2216 2216 padding:6px;
2217 2217 }
2218 2218
2219 2219 #login,#register {
2220 2220 width:420px;
2221 2221 margin:10% auto 0;
2222 2222 padding:0;
2223 2223 }
2224 2224
2225 2225 #login div.color,#register div.color {
2226 2226 clear:both;
2227 2227 overflow:hidden;
2228 2228 background:#FFF;
2229 2229 margin:10px auto 0;
2230 2230 padding:3px 3px 3px 0;
2231 2231 }
2232 2232
2233 2233 #login div.color a,#register div.color a {
2234 2234 width:20px;
2235 2235 height:20px;
2236 2236 display:block;
2237 2237 float:left;
2238 2238 margin:0 0 0 3px;
2239 2239 padding:0;
2240 2240 }
2241 2241
2242 2242 #login div.title h5,#register div.title h5 {
2243 2243 color:#fff;
2244 2244 margin:10px;
2245 2245 padding:0;
2246 2246 }
2247 2247
2248 2248 #login div.form div.fields div.field,#register div.form div.fields div.field {
2249 2249 clear:both;
2250 2250 overflow:hidden;
2251 2251 margin:0;
2252 2252 padding:0 0 10px;
2253 2253 }
2254 2254
2255 2255 #login div.form div.fields div.field span.error-message,#register div.form div.fields div.field span.error-message {
2256 2256 height:1%;
2257 2257 display:block;
2258 2258 color:red;
2259 2259 margin:8px 0 0;
2260 2260 padding:0;
2261 2261 }
2262 2262
2263 2263 #login div.form div.fields div.field div.label label,#register div.form div.fields div.field div.label label {
2264 2264 color:#000;
2265 2265 font-weight:700;
2266 2266 }
2267 2267
2268 2268 #login div.form div.fields div.field div.input,#register div.form div.fields div.field div.input {
2269 2269 float:left;
2270 2270 margin:0;
2271 2271 padding:0;
2272 2272 }
2273 2273
2274 2274 #login div.form div.fields div.field div.checkbox,#register div.form div.fields div.field div.checkbox {
2275 2275 margin:0 0 0 184px;
2276 2276 padding:0;
2277 2277 }
2278 2278
2279 2279 #login div.form div.fields div.field div.checkbox label,#register div.form div.fields div.field div.checkbox label {
2280 2280 color:#565656;
2281 2281 font-weight:700;
2282 2282 }
2283 2283
2284 2284 #login div.form div.fields div.buttons input,#register div.form div.fields div.buttons input {
2285 2285 color:#000;
2286 2286 font-size:1em;
2287 2287 font-weight:700;
2288 2288 font-family:Verdana, Helvetica, Sans-Serif;
2289 2289 margin:0;
2290 2290 }
2291 2291
2292 2292 #changeset_content .container .wrapper,#graph_content .container .wrapper {
2293 2293 width:600px;
2294 2294 }
2295 2295
2296 2296 #changeset_content .container .left,#graph_content .container .left {
2297 2297 float:left;
2298 2298 width:70%;
2299 2299 padding-left:5px;
2300 2300 }
2301 2301
2302 2302 #changeset_content .container .left .date,.ac .match {
2303 2303 font-weight:700;
2304 2304 padding-top: 5px;
2305 2305 padding-bottom:5px;
2306 2306 }
2307 2307
2308 2308 div#legend_container table td,div#legend_choices table td {
2309 2309 border:none !important;
2310 2310 height:20px !important;
2311 2311 padding:0 !important;
2312 2312 }
2313 2313
2314 2314 #q_filter{
2315 2315 border:0 none;
2316 2316 color:#AAAAAA;
2317 2317 margin-bottom:-4px;
2318 2318 margin-top:-4px;
2319 2319 padding-left:3px;
2320 2320 }
2321 2321
@@ -1,68 +1,77 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.html"/>
3 3
4 4 <%def name="title()">
5 5 ${_('Permissions administration')} - ${c.rhodecode_name}
6 6 </%def>
7 7
8 8 <%def name="breadcrumbs_links()">
9 9 ${h.link_to(_('Admin'),h.url('admin_home'))}
10 10 &raquo;
11 11 ${_('Permissions')}
12 12 </%def>
13 13
14 14 <%def name="page_nav()">
15 15 ${self.menu('admin')}
16 16 </%def>
17 17
18 18 <%def name="main()">
19 19 <div class="box">
20 20 <!-- box / title -->
21 21 <div class="title">
22 22 ${self.breadcrumbs()}
23 23 </div>
24 24 <h3>${_('Default permissions')}</h3>
25 25 ${h.form(url('permission', id='default'),method='put')}
26 26 <div class="form">
27 27 <!-- fields -->
28 28 <div class="fields">
29
29 <div class="field">
30 <div class="label label-checkbox">
31 <label for="anonymous">${_('Anonymous access')}:</label>
32 </div>
33 <div class="checkboxes">
34 <div class="checkbox">
35 ${h.checkbox('anonymous',True)}
36 </div>
37 </div>
38 </div>
30 39 <div class="field">
31 40 <div class="label">
32 41 <label for="default_perm">${_('Repository permission')}:</label>
33 42 </div>
34 43 <div class="select">
35 44 ${h.select('default_perm','',c.perms_choices)}
36 45
37 46 ${h.checkbox('overwrite_default','true')}
38 47 <label for="overwrite_default">
39 48 <span class="tooltip"
40 49 tooltip_title="${h.tooltip(_('All default permissions on each repository will be reset to choosen permission, note that all custom default permission on repositories will be lost'))}">
41 50 ${_('overwrite existing settings')}</span> </label>
42 51 </div>
43 52 </div>
44 53 <div class="field">
45 54 <div class="label">
46 55 <label for="default_register">${_('Registration')}:</label>
47 56 </div>
48 57 <div class="select">
49 58 ${h.select('default_register','',c.register_choices)}
50 59 </div>
51 60 </div>
52 61 <div class="field">
53 62 <div class="label">
54 63 <label for="default_create">${_('Repository creation')}:</label>
55 64 </div>
56 65 <div class="select">
57 66 ${h.select('default_create','',c.create_choices)}
58 67 </div>
59 68 </div>
60 69
61 70 <div class="buttons">
62 71 ${h.submit('set','set',class_="ui-button ui-widget ui-state-default ui-corner-all")}
63 72 </div>
64 73 </div>
65 74 </div>
66 75 ${h.end_form()}
67 76 </div>
68 77 </%def>
@@ -1,286 +1,296 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3 3 <html xmlns="http://www.w3.org/1999/xhtml" id="mainhtml">
4 4 <head>
5 5 <title>${next.title()}</title>
6 6 <link rel="icon" href="/images/hgicon.png" type="image/png" />
7 7 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
8 8 <meta name="robots" content="index, nofollow"/>
9 9 <!-- stylesheets -->
10 10 ${self.css()}
11 11 <!-- scripts -->
12 12 ${self.js()}
13 13 </head>
14 14 <body>
15 15 <!-- header -->
16 16 <div id="header">
17 17 <!-- user -->
18 18 <ul id="logged-user">
19 19 <li class="first">
20 20 <div class="gravatar">
21 21 <img alt="gravatar" src="${h.gravatar_url(c.rhodecode_user.email,24)}" />
22 22 </div>
23 %if c.rhodecode_user.username == 'default':
24 <div class="account">
25 ${h.link_to('%s %s'%(c.rhodecode_user.name,c.rhodecode_user.lastname),h.url('#'))}<br/>
26 ${h.link_to(c.rhodecode_user.username,h.url('#'))}
27 </div>
28 </li>
29 <li class="last highlight">${h.link_to(u'Login',h.url('login_home'))}</li>
30 %else:
31
23 32 <div class="account">
24 33 ${h.link_to('%s %s'%(c.rhodecode_user.name,c.rhodecode_user.lastname),h.url('admin_settings_my_account'))}<br/>
25 34 ${h.link_to(c.rhodecode_user.username,h.url('admin_settings_my_account'))}
26 35 </div>
27 36 </li>
28 37 <li class="last highlight">${h.link_to(u'Logout',h.url('logout_home'))}</li>
38 %endif
29 39 </ul>
30 40 <!-- end user -->
31 41 <div id="header-inner" class="title top-left-rounded-corner top-right-rounded-corner">
32 42 <!-- logo -->
33 43 <div id="logo">
34 44 <h1><a href="${h.url('home')}">${c.rhodecode_name}</a></h1>
35 45 </div>
36 46 <!-- end logo -->
37 47 <!-- menu -->
38 48 ${self.page_nav()}
39 49 <!-- quick -->
40 50 </div>
41 51 </div>
42 52 <!-- end header -->
43 53
44 54 <!-- CONTENT -->
45 55 <div id="content">
46 56 <div class="flash_msg">
47 57 <% messages = h.flash.pop_messages() %>
48 58 % if messages:
49 59 <ul id="flash-messages">
50 60 % for message in messages:
51 61 <li class="${message.category}_msg">${message}</li>
52 62 % endfor
53 63 </ul>
54 64 % endif
55 65 </div>
56 66 <div id="main">
57 67 ${next.main()}
58 68 </div>
59 69 </div>
60 70 <!-- END CONTENT -->
61 71
62 72 <!-- footer -->
63 73 <div id="footer">
64 74 <div id="footer-inner" class="title bottom-left-rounded-corner bottom-right-rounded-corner">
65 75 <div>
66 76 <p class="footer-link">${h.link_to(_('Submit a bug'),h.url('bugtracker'))}</p>
67 77 <p class="footer-link">${h.link_to(_('GPL license'),h.url('gpl_license'))}</p>
68 78 <p>RhodeCode ${c.rhodecode_version} &copy; 2010 by Marcin Kuzminski</p>
69 79 </div>
70 80 </div>
71 81 <script type="text/javascript">${h.tooltip.activate()}</script>
72 82 </div>
73 83 <!-- end footer -->
74 84 </body>
75 85
76 86 </html>
77 87
78 88 ### MAKO DEFS ###
79 89 <%def name="page_nav()">
80 90 ${self.menu()}
81 91 </%def>
82 92
83 93 <%def name="menu(current=None)">
84 94 <%
85 95 def is_current(selected):
86 96 if selected == current:
87 97 return h.literal('class="current"')
88 98 %>
89 99 %if current not in ['home','admin']:
90 100 ##REGULAR MENU
91 101 <ul id="quick">
92 102 <!-- repo switcher -->
93 103 <li>
94 104 <a id="repo_switcher" title="${_('Switch repository')}" href="#">
95 105 <span class="icon">
96 106 <img src="/images/icons/database.png" alt="${_('Products')}" />
97 107 </span>
98 108 <span>&darr;</span>
99 109 </a>
100 110 <ul class="repo_switcher">
101 111 %for repo in c.cached_repo_list:
102 112
103 113 %if repo['repo'].dbrepo.private:
104 114 <li>${h.link_to(repo['repo'].name,h.url('summary_home',repo_name=repo['repo'].name),class_="private_repo %s" % repo['repo'].dbrepo.repo_type)}</li>
105 115 %else:
106 116 <li>${h.link_to(repo['repo'].name,h.url('summary_home',repo_name=repo['repo'].name),class_="public_repo %s" % repo['repo'].dbrepo.repo_type)}</li>
107 117 %endif
108 118 %endfor
109 119 </ul>
110 120 </li>
111 121
112 122 <li ${is_current('summary')}>
113 123 <a title="${_('Summary')}" href="${h.url('summary_home',repo_name=c.repo_name)}">
114 124 <span class="icon">
115 125 <img src="/images/icons/clipboard_16.png" alt="${_('Summary')}" />
116 126 </span>
117 127 <span>${_('Summary')}</span>
118 128 </a>
119 129 </li>
120 130 ##<li ${is_current('shortlog')}>
121 131 ## <a title="${_('Shortlog')}" href="${h.url('shortlog_home',repo_name=c.repo_name)}">
122 132 ## <span class="icon">
123 133 ## <img src="/images/icons/application_view_list.png" alt="${_('Shortlog')}" />
124 134 ## </span>
125 135 ## <span>${_('Shortlog')}</span>
126 136 ## </a>
127 137 ##</li>
128 138 <li ${is_current('changelog')}>
129 139 <a title="${_('Changelog')}" href="${h.url('changelog_home',repo_name=c.repo_name)}">
130 140 <span class="icon">
131 141 <img src="/images/icons/time.png" alt="${_('Changelog')}" />
132 142 </span>
133 143 <span>${_('Changelog')}</span>
134 144 </a>
135 145 </li>
136 146
137 147 <li ${is_current('switch_to')}>
138 148 <a title="${_('Switch to')}" href="#">
139 149 <span class="icon">
140 150 <img src="/images/icons/arrow_switch.png" alt="${_('Switch to')}" />
141 151 </span>
142 152 <span>${_('Switch to')}</span>
143 153 </a>
144 154 <ul>
145 155 <li>
146 156 ${h.link_to('%s (%s)' % (_('branches'),len(c.repository_branches.values()),),h.url('branches_home',repo_name=c.repo_name),class_='branches childs')}
147 157 <ul>
148 158 %if c.repository_branches.values():
149 159 %for cnt,branch in enumerate(c.repository_branches.items()):
150 160 <li>${h.link_to('%s - %s' % (branch[0],h.short_id(branch[1])),h.url('files_home',repo_name=c.repo_name,revision=branch[1]))}</li>
151 161 %endfor
152 162 %else:
153 163 <li>${h.link_to(_('There are no branches yet'),'#')}</li>
154 164 %endif
155 165 </ul>
156 166 </li>
157 167 <li>
158 168 ${h.link_to('%s (%s)' % (_('tags'),len(c.repository_tags.values()),),h.url('tags_home',repo_name=c.repo_name),class_='tags childs')}
159 169 <ul>
160 170 %if c.repository_tags.values():
161 171 %for cnt,tag in enumerate(c.repository_tags.items()):
162 172 <li>${h.link_to('%s - %s' % (tag[0],h.short_id(tag[1])),h.url('files_home',repo_name=c.repo_name,revision=tag[1]))}</li>
163 173 %endfor
164 174 %else:
165 175 <li>${h.link_to(_('There are no tags yet'),'#')}</li>
166 176 %endif
167 177 </ul>
168 178 </li>
169 179 </ul>
170 180 </li>
171 181 <li ${is_current('files')}>
172 182 <a title="${_('Files')}" href="${h.url('files_home',repo_name=c.repo_name)}">
173 183 <span class="icon">
174 184 <img src="/images/icons/file.png" alt="${_('Files')}" />
175 185 </span>
176 186 <span>${_('Files')}</span>
177 187 </a>
178 188 </li>
179 189
180 190 <li ${is_current('options')}>
181 191 <a title="${_('Options')}" href="#">
182 192 <span class="icon">
183 193 <img src="/images/icons/table_gear.png" alt="${_('Admin')}" />
184 194 </span>
185 195 <span>${_('Options')}</span>
186 196 </a>
187 197 <ul>
188 198 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
189 199 <li>${h.link_to(_('settings'),h.url('repo_settings_home',repo_name=c.repo_name),class_='settings')}</li>
190 200 %endif
191 201 <li>${h.link_to(_('fork'),h.url('repo_fork_home',repo_name=c.repo_name),class_='fork')}</li>
192 202 <li>${h.link_to(_('search'),h.url('search_repo',search_repo=c.repo_name),class_='search')}</li>
193 203
194 204 %if h.HasPermissionAll('hg.admin')('access admin main page'):
195 205 <li>
196 206 ${h.link_to(_('admin'),h.url('admin_home'),class_='admin')}
197 207 <ul>
198 208 <li>${h.link_to(_('journal'),h.url('admin_home'),class_='journal')}</li>
199 209 <li>${h.link_to(_('repositories'),h.url('repos'),class_='repos')}</li>
200 210 <li>${h.link_to(_('users'),h.url('users'),class_='users')}</li>
201 211 <li>${h.link_to(_('permissions'),h.url('edit_permission',id='default'),class_='permissions')}</li>
202 212 <li class="last">${h.link_to(_('settings'),h.url('admin_settings'),class_='settings')}</li>
203 213 </ul>
204 214 </li>
205 215 %endif
206 216
207 217
208 218 ## %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
209 219 ## <li class="last">
210 220 ## ${h.link_to(_('delete'),'#',class_='delete')}
211 221 ## ${h.form(url('repo_settings_delete', repo_name=c.repo_name),method='delete')}
212 222 ## ${h.submit('remove_%s' % c.repo_name,'delete',class_="delete_icon action_button",onclick="return confirm('Confirm to delete this repository');")}
213 223 ## ${h.end_form()}
214 224 ## </li>
215 225 ## %endif
216 226 </ul>
217 227 </li>
218 228 </ul>
219 229 %else:
220 230 ##ROOT MENU
221 231 <ul id="quick">
222 232 <li>
223 233 <a title="${_('Home')}" href="${h.url('home')}">
224 234 <span class="icon">
225 235 <img src="/images/icons/home_16.png" alt="${_('Home')}" />
226 236 </span>
227 237 <span>${_('Home')}</span>
228 238 </a>
229 239 </li>
230 240
231 241 <li>
232 242 <a title="${_('Search')}" href="${h.url('search')}">
233 243 <span class="icon">
234 244 <img src="/images/icons/search_16.png" alt="${_('Search')}" />
235 245 </span>
236 246 <span>${_('Search')}</span>
237 247 </a>
238 248 </li>
239 249
240 250 %if h.HasPermissionAll('hg.admin')('access admin main page'):
241 251 <li ${is_current('admin')}>
242 252 <a title="${_('Admin')}" href="${h.url('admin_home')}">
243 253 <span class="icon">
244 254 <img src="/images/icons/cog_edit.png" alt="${_('Admin')}" />
245 255 </span>
246 256 <span>${_('Admin')}</span>
247 257 </a>
248 258 <ul>
249 259 <li>${h.link_to(_('journal'),h.url('admin_home'),class_='journal')}</li>
250 260 <li>${h.link_to(_('repositories'),h.url('repos'),class_='repos')}</li>
251 261 <li>${h.link_to(_('users'),h.url('users'),class_='users')}</li>
252 262 <li>${h.link_to(_('permissions'),h.url('edit_permission',id='default'),class_='permissions')}</li>
253 263 <li class="last">${h.link_to(_('settings'),h.url('admin_settings'),class_='settings')}</li>
254 264 </ul>
255 265 </li>
256 266 %endif
257 267
258 268 </ul>
259 269 %endif
260 270 </%def>
261 271
262 272
263 273 <%def name="css()">
264 274 <link rel="stylesheet" type="text/css" href="/css/style.css" media="screen" />
265 275 <link rel="stylesheet" type="text/css" href="/css/pygments.css" />
266 276 <link rel="stylesheet" type="text/css" href="/css/diff.css" />
267 277 </%def>
268 278
269 279 <%def name="js()">
270 280 ##<script type="text/javascript" src="/js/yui/utilities/utilities.js"></script>
271 281 ##<script type="text/javascript" src="/js/yui/container/container.js"></script>
272 282 ##<script type="text/javascript" src="/js/yui/datasource/datasource.js"></script>
273 283 ##<script type="text/javascript" src="/js/yui/autocomplete/autocomplete.js"></script>
274 284 ##<script type="text/javascript" src="/js/yui/selector/selector-min.js"></script>
275 285
276 286 <script type="text/javascript" src="/js/yui2.js"></script>
277 287 <script type="text/javascript" src="/js/yui/selector/selector-min.js"></script>
278 288 <!--[if IE]><script language="javascript" type="text/javascript" src="/js/excanvas.min.js"></script><![endif]-->
279 289 <script type="text/javascript" src="/js/yui.flot.js"></script>
280 290 </%def>
281 291
282 292 <%def name="breadcrumbs()">
283 293 <div class="breadcrumbs">
284 294 ${self.breadcrumbs_links()}
285 295 </div>
286 296 </%def> No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now