##// END OF EJS Templates
index: always use lightweight - there shouldn't be any reason not to
Mads Kiilerich -
r3752:1e5bb8ed beta
parent child Browse files
Show More
@@ -1,404 +1,400 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.admin.repos_groups
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Repository groups controller for RhodeCode
7 7
8 8 :created_on: Mar 23, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import logging
27 27 import traceback
28 28 import formencode
29 29
30 30 from formencode import htmlfill
31 31
32 32 from pylons import request, tmpl_context as c, url
33 33 from pylons.controllers.util import abort, redirect
34 34 from pylons.i18n.translation import _
35 35
36 36 from sqlalchemy.exc import IntegrityError
37 37
38 38 import rhodecode
39 39 from rhodecode.lib import helpers as h
40 40 from rhodecode.lib.compat import json
41 41 from rhodecode.lib.auth import LoginRequired, HasPermissionAnyDecorator,\
42 42 HasReposGroupPermissionAnyDecorator, HasReposGroupPermissionAll,\
43 43 HasPermissionAll
44 44 from rhodecode.lib.base import BaseController, render
45 45 from rhodecode.model.db import RepoGroup, Repository
46 46 from rhodecode.model.scm import RepoGroupList
47 47 from rhodecode.model.repos_group import ReposGroupModel
48 48 from rhodecode.model.forms import ReposGroupForm, RepoGroupPermsForm
49 49 from rhodecode.model.meta import Session
50 50 from rhodecode.model.repo import RepoModel
51 51 from webob.exc import HTTPInternalServerError, HTTPNotFound
52 52 from rhodecode.lib.utils2 import str2bool, safe_int
53 53 from sqlalchemy.sql.expression import func
54 54
55 55
56 56 log = logging.getLogger(__name__)
57 57
58 58
59 59 class ReposGroupsController(BaseController):
60 60 """REST Controller styled on the Atom Publishing Protocol"""
61 61 # To properly map this controller, ensure your config/routing.py
62 62 # file has a resource setup:
63 63 # map.resource('repos_group', 'repos_groups')
64 64
65 65 @LoginRequired()
66 66 def __before__(self):
67 67 super(ReposGroupsController, self).__before__()
68 68
69 69 def __load_defaults(self, allow_empty_group=False, exclude_group_ids=[]):
70 70 if HasPermissionAll('hg.admin')('group edit'):
71 71 #we're global admin, we're ok and we can create TOP level groups
72 72 allow_empty_group = True
73 73
74 74 #override the choices for this form, we need to filter choices
75 75 #and display only those we have ADMIN right
76 76 groups_with_admin_rights = RepoGroupList(RepoGroup.query().all(),
77 77 perm_set=['group.admin'])
78 78 c.repo_groups = RepoGroup.groups_choices(groups=groups_with_admin_rights,
79 79 show_empty_group=allow_empty_group)
80 80 # exclude filtered ids
81 81 c.repo_groups = filter(lambda x: x[0] not in exclude_group_ids,
82 82 c.repo_groups)
83 83 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
84 84 repo_model = RepoModel()
85 85 c.users_array = repo_model.get_users_js()
86 86 c.users_groups_array = repo_model.get_users_groups_js()
87 87
88 88 def __load_data(self, group_id):
89 89 """
90 90 Load defaults settings for edit, and update
91 91
92 92 :param group_id:
93 93 """
94 94 repo_group = RepoGroup.get_or_404(group_id)
95 95 data = repo_group.get_dict()
96 96 data['group_name'] = repo_group.name
97 97
98 98 # fill repository group users
99 99 for p in repo_group.repo_group_to_perm:
100 100 data.update({'u_perm_%s' % p.user.username:
101 101 p.permission.permission_name})
102 102
103 103 # fill repository group groups
104 104 for p in repo_group.users_group_to_perm:
105 105 data.update({'g_perm_%s' % p.users_group.users_group_name:
106 106 p.permission.permission_name})
107 107
108 108 return data
109 109
110 110 def _revoke_perms_on_yourself(self, form_result):
111 111 _up = filter(lambda u: c.rhodecode_user.username == u[0],
112 112 form_result['perms_updates'])
113 113 _new = filter(lambda u: c.rhodecode_user.username == u[0],
114 114 form_result['perms_new'])
115 115 if _new and _new[0][1] != 'group.admin' or _up and _up[0][1] != 'group.admin':
116 116 return True
117 117 return False
118 118
119 119 def index(self, format='html'):
120 120 """GET /repos_groups: All items in the collection"""
121 121 # url('repos_groups')
122 122 group_iter = RepoGroupList(RepoGroup.query().all(),
123 123 perm_set=['group.admin'])
124 124 sk = lambda g: g.parents[0].group_name if g.parents else g.group_name
125 125 c.groups = sorted(group_iter, key=sk)
126 126 return render('admin/repos_groups/repos_groups_show.html')
127 127
128 128 def create(self):
129 129 """POST /repos_groups: Create a new item"""
130 130 # url('repos_groups')
131 131
132 132 self.__load_defaults()
133 133
134 134 # permissions for can create group based on parent_id are checked
135 135 # here in the Form
136 136 repos_group_form = ReposGroupForm(available_groups=
137 137 map(lambda k: unicode(k[0]), c.repo_groups))()
138 138 try:
139 139 form_result = repos_group_form.to_python(dict(request.POST))
140 140 ReposGroupModel().create(
141 141 group_name=form_result['group_name'],
142 142 group_description=form_result['group_description'],
143 143 parent=form_result['group_parent_id'],
144 144 owner=self.rhodecode_user.user_id
145 145 )
146 146 Session().commit()
147 147 h.flash(_('Created repository group %s') \
148 148 % form_result['group_name'], category='success')
149 149 #TODO: in futureaction_logger(, '', '', '', self.sa)
150 150 except formencode.Invalid, errors:
151 151 return htmlfill.render(
152 152 render('admin/repos_groups/repos_groups_add.html'),
153 153 defaults=errors.value,
154 154 errors=errors.error_dict or {},
155 155 prefix_error=False,
156 156 encoding="UTF-8")
157 157 except Exception:
158 158 log.error(traceback.format_exc())
159 159 h.flash(_('Error occurred during creation of repository group %s') \
160 160 % request.POST.get('group_name'), category='error')
161 161 parent_group_id = form_result['group_parent_id']
162 162 #TODO: maybe we should get back to the main view, not the admin one
163 163 return redirect(url('repos_groups', parent_group=parent_group_id))
164 164
165 165 def new(self, format='html'):
166 166 """GET /repos_groups/new: Form to create a new item"""
167 167 # url('new_repos_group')
168 168 if HasPermissionAll('hg.admin')('group create'):
169 169 #we're global admin, we're ok and we can create TOP level groups
170 170 pass
171 171 else:
172 172 # we pass in parent group into creation form, thus we know
173 173 # what would be the group, we can check perms here !
174 174 group_id = safe_int(request.GET.get('parent_group'))
175 175 group = RepoGroup.get(group_id) if group_id else None
176 176 group_name = group.group_name if group else None
177 177 if HasReposGroupPermissionAll('group.admin')(group_name, 'group create'):
178 178 pass
179 179 else:
180 180 return abort(403)
181 181
182 182 self.__load_defaults()
183 183 return render('admin/repos_groups/repos_groups_add.html')
184 184
185 185 @HasReposGroupPermissionAnyDecorator('group.admin')
186 186 def update(self, group_name):
187 187 """PUT /repos_groups/group_name: Update an existing item"""
188 188 # Forms posted to this method should contain a hidden field:
189 189 # <input type="hidden" name="_method" value="PUT" />
190 190 # Or using helpers:
191 191 # h.form(url('repos_group', group_name=GROUP_NAME),
192 192 # method='put')
193 193 # url('repos_group', group_name=GROUP_NAME)
194 194
195 195 c.repos_group = ReposGroupModel()._get_repo_group(group_name)
196 196 if HasPermissionAll('hg.admin')('group edit'):
197 197 #we're global admin, we're ok and we can create TOP level groups
198 198 allow_empty_group = True
199 199 elif not c.repos_group.parent_group:
200 200 allow_empty_group = True
201 201 else:
202 202 allow_empty_group = False
203 203 self.__load_defaults(allow_empty_group=allow_empty_group,
204 204 exclude_group_ids=[c.repos_group.group_id])
205 205
206 206 repos_group_form = ReposGroupForm(
207 207 edit=True,
208 208 old_data=c.repos_group.get_dict(),
209 209 available_groups=c.repo_groups_choices,
210 210 can_create_in_root=allow_empty_group,
211 211 )()
212 212 try:
213 213 form_result = repos_group_form.to_python(dict(request.POST))
214 214 if not c.rhodecode_user.is_admin:
215 215 if self._revoke_perms_on_yourself(form_result):
216 216 msg = _('Cannot revoke permission for yourself as admin')
217 217 h.flash(msg, category='warning')
218 218 raise Exception('revoke admin permission on self')
219 219
220 220 new_gr = ReposGroupModel().update(group_name, form_result)
221 221 Session().commit()
222 222 h.flash(_('Updated repository group %s') \
223 223 % form_result['group_name'], category='success')
224 224 # we now have new name !
225 225 group_name = new_gr.group_name
226 226 #TODO: in future action_logger(, '', '', '', self.sa)
227 227 except formencode.Invalid, errors:
228 228
229 229 return htmlfill.render(
230 230 render('admin/repos_groups/repos_groups_edit.html'),
231 231 defaults=errors.value,
232 232 errors=errors.error_dict or {},
233 233 prefix_error=False,
234 234 encoding="UTF-8")
235 235 except Exception:
236 236 log.error(traceback.format_exc())
237 237 h.flash(_('Error occurred during update of repository group %s') \
238 238 % request.POST.get('group_name'), category='error')
239 239
240 240 return redirect(url('edit_repos_group', group_name=group_name))
241 241
242 242 @HasReposGroupPermissionAnyDecorator('group.admin')
243 243 def delete(self, group_name):
244 244 """DELETE /repos_groups/group_name: Delete an existing item"""
245 245 # Forms posted to this method should contain a hidden field:
246 246 # <input type="hidden" name="_method" value="DELETE" />
247 247 # Or using helpers:
248 248 # h.form(url('repos_group', group_name=GROUP_NAME),
249 249 # method='delete')
250 250 # url('repos_group', group_name=GROUP_NAME)
251 251
252 252 gr = c.repos_group = ReposGroupModel()._get_repo_group(group_name)
253 253 repos = gr.repositories.all()
254 254 if repos:
255 255 h.flash(_('This group contains %s repositores and cannot be '
256 256 'deleted') % len(repos), category='warning')
257 257 return redirect(url('repos_groups'))
258 258
259 259 children = gr.children.all()
260 260 if children:
261 261 h.flash(_('This group contains %s subgroups and cannot be deleted'
262 262 % (len(children))), category='warning')
263 263 return redirect(url('repos_groups'))
264 264
265 265 try:
266 266 ReposGroupModel().delete(group_name)
267 267 Session().commit()
268 268 h.flash(_('Removed repository group %s') % group_name,
269 269 category='success')
270 270 #TODO: in future action_logger(, '', '', '', self.sa)
271 271 except Exception:
272 272 log.error(traceback.format_exc())
273 273 h.flash(_('Error occurred during deletion of repos '
274 274 'group %s') % group_name, category='error')
275 275
276 276 return redirect(url('repos_groups'))
277 277
278 278 @HasReposGroupPermissionAnyDecorator('group.admin')
279 279 def set_repo_group_perm_member(self, group_name):
280 280 c.repos_group = ReposGroupModel()._get_repo_group(group_name)
281 281 form = RepoGroupPermsForm()().to_python(request.POST)
282 282
283 283 recursive = form['recursive']
284 284 # iterate over all members(if in recursive mode) of this groups and
285 285 # set the permissions !
286 286 # this can be potentially heavy operation
287 287 ReposGroupModel()._update_permissions(c.repos_group, form['perms_new'],
288 288 form['perms_updates'], recursive)
289 289 #TODO: implement this
290 290 #action_logger(self.rhodecode_user, 'admin_changed_repo_permissions',
291 291 # repo_name, self.ip_addr, self.sa)
292 292 Session().commit()
293 293 h.flash(_('Repository Group permissions updated'), category='success')
294 294 return redirect(url('edit_repos_group', group_name=group_name))
295 295
296 296 @HasReposGroupPermissionAnyDecorator('group.admin')
297 297 def delete_repo_group_perm_member(self, group_name):
298 298 """
299 299 DELETE an existing repository group permission user
300 300
301 301 :param group_name:
302 302 """
303 303 try:
304 304 obj_type = request.POST.get('obj_type')
305 305 obj_id = None
306 306 if obj_type == 'user':
307 307 obj_id = safe_int(request.POST.get('user_id'))
308 308 elif obj_type == 'user_group':
309 309 obj_id = safe_int(request.POST.get('user_group_id'))
310 310
311 311 if not c.rhodecode_user.is_admin:
312 312 if obj_type == 'user' and c.rhodecode_user.user_id == obj_id:
313 313 msg = _('Cannot revoke permission for yourself as admin')
314 314 h.flash(msg, category='warning')
315 315 raise Exception('revoke admin permission on self')
316 316 recursive = str2bool(request.POST.get('recursive', False))
317 317 if obj_type == 'user':
318 318 ReposGroupModel().delete_permission(
319 319 repos_group=group_name, obj=obj_id,
320 320 obj_type='user', recursive=recursive
321 321 )
322 322 elif obj_type == 'user_group':
323 323 ReposGroupModel().delete_permission(
324 324 repos_group=group_name, obj=obj_id,
325 325 obj_type='users_group', recursive=recursive
326 326 )
327 327
328 328 Session().commit()
329 329 except Exception:
330 330 log.error(traceback.format_exc())
331 331 h.flash(_('An error occurred during revoking of permission'),
332 332 category='error')
333 333 raise HTTPInternalServerError()
334 334
335 335 def show_by_name(self, group_name):
336 336 """
337 337 This is a proxy that does a lookup group_name -> id, and shows
338 338 the group by id view instead
339 339 """
340 340 group_name = group_name.rstrip('/')
341 341 id_ = RepoGroup.get_by_group_name(group_name)
342 342 if id_:
343 343 return self.show(id_.group_id)
344 344 raise HTTPNotFound
345 345
346 346 @HasReposGroupPermissionAnyDecorator('group.read', 'group.write',
347 347 'group.admin')
348 348 def show(self, group_name, format='html'):
349 349 """GET /repos_groups/group_name: Show a specific item"""
350 350 # url('repos_group', group_name=GROUP_NAME)
351 351
352 352 c.group = c.repos_group = ReposGroupModel()._get_repo_group(group_name)
353 353 c.group_repos = c.group.repositories.all()
354 354
355 355 #overwrite our cached list with current filter
356 356 gr_filter = c.group_repos
357 357 c.repo_cnt = 0
358 358
359 359 groups = RepoGroup.query().order_by(RepoGroup.group_name)\
360 360 .filter(RepoGroup.group_parent_id == c.group.group_id).all()
361 361 c.groups = self.scm_model.get_repos_groups(groups)
362 362
363 if not c.visual.lightweight_dashboard:
364 c.repos_list = self.scm_model.get_repos(all_repos=gr_filter)
365 ## lightweight version of dashboard
366 else:
367 c.repos_list = Repository.query()\
368 .filter(Repository.group_id == c.group.group_id)\
369 .order_by(func.lower(Repository.repo_name))\
370 .all()
363 c.repos_list = Repository.query()\
364 .filter(Repository.group_id == c.group.group_id)\
365 .order_by(func.lower(Repository.repo_name))\
366 .all()
371 367
372 repos_data = RepoModel().get_repos_as_dict(repos_list=c.repos_list,
373 admin=False)
374 #json used to render the grid
375 c.data = json.dumps(repos_data)
368 repos_data = RepoModel().get_repos_as_dict(repos_list=c.repos_list,
369 admin=False)
370 #json used to render the grid
371 c.data = json.dumps(repos_data)
376 372
377 373 return render('admin/repos_groups/repos_groups.html')
378 374
379 375 @HasReposGroupPermissionAnyDecorator('group.admin')
380 376 def edit(self, group_name, format='html'):
381 377 """GET /repos_groups/group_name/edit: Form to edit an existing item"""
382 378 # url('edit_repos_group', group_name=GROUP_NAME)
383 379
384 380 c.repos_group = ReposGroupModel()._get_repo_group(group_name)
385 381 #we can only allow moving empty group if it's already a top-level
386 382 #group, ie has no parents, or we're admin
387 383 if HasPermissionAll('hg.admin')('group edit'):
388 384 #we're global admin, we're ok and we can create TOP level groups
389 385 allow_empty_group = True
390 386 elif not c.repos_group.parent_group:
391 387 allow_empty_group = True
392 388 else:
393 389 allow_empty_group = False
394 390
395 391 self.__load_defaults(allow_empty_group=allow_empty_group,
396 392 exclude_group_ids=[c.repos_group.group_id])
397 393 defaults = self.__load_data(c.repos_group.group_id)
398 394
399 395 return htmlfill.render(
400 396 render('admin/repos_groups/repos_groups_edit.html'),
401 397 defaults=defaults,
402 398 encoding="UTF-8",
403 399 force_defaults=False
404 400 )
@@ -1,513 +1,508 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.admin.settings
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 settings controller for rhodecode admin
7 7
8 8 :created_on: Jul 14, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import logging
27 27 import traceback
28 28 import formencode
29 29 import pkg_resources
30 30 import platform
31 31
32 32 from sqlalchemy import func
33 33 from formencode import htmlfill
34 34 from pylons import request, session, tmpl_context as c, url, config
35 35 from pylons.controllers.util import abort, redirect
36 36 from pylons.i18n.translation import _
37 37
38 38 from rhodecode.lib import helpers as h
39 39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
40 40 HasPermissionAnyDecorator, NotAnonymous, HasPermissionAny,\
41 41 HasReposGroupPermissionAll, HasReposGroupPermissionAny, AuthUser
42 42 from rhodecode.lib.base import BaseController, render
43 43 from rhodecode.lib.celerylib import tasks, run_task
44 44 from rhodecode.lib.utils import repo2db_mapper, set_rhodecode_config, \
45 45 check_git_version
46 46 from rhodecode.model.db import RhodeCodeUi, Repository, RepoGroup, \
47 47 RhodeCodeSetting, PullRequest, PullRequestReviewers
48 48 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
49 49 ApplicationUiSettingsForm, ApplicationVisualisationForm
50 50 from rhodecode.model.scm import ScmModel, RepoGroupList
51 51 from rhodecode.model.user import UserModel
52 52 from rhodecode.model.repo import RepoModel
53 53 from rhodecode.model.db import User
54 54 from rhodecode.model.notification import EmailNotificationModel
55 55 from rhodecode.model.meta import Session
56 56 from rhodecode.lib.utils2 import str2bool, safe_unicode
57 57 from rhodecode.lib.compat import json
58 58 log = logging.getLogger(__name__)
59 59
60 60
61 61 class SettingsController(BaseController):
62 62 """REST Controller styled on the Atom Publishing Protocol"""
63 63 # To properly map this controller, ensure your config/routing.py
64 64 # file has a resource setup:
65 65 # map.resource('setting', 'settings', controller='admin/settings',
66 66 # path_prefix='/admin', name_prefix='admin_')
67 67
68 68 @LoginRequired()
69 69 def __before__(self):
70 70 super(SettingsController, self).__before__()
71 71 c.modules = sorted([(p.project_name, p.version)
72 72 for p in pkg_resources.working_set]
73 73 + [('git', check_git_version())],
74 74 key=lambda k: k[0].lower())
75 75 c.py_version = platform.python_version()
76 76 c.platform = platform.platform()
77 77
78 78 @HasPermissionAllDecorator('hg.admin')
79 79 def index(self, format='html'):
80 80 """GET /admin/settings: All items in the collection"""
81 81 # url('admin_settings')
82 82
83 83 defaults = RhodeCodeSetting.get_app_settings()
84 84 defaults.update(self._get_hg_ui_settings())
85 85
86 86 return htmlfill.render(
87 87 render('admin/settings/settings.html'),
88 88 defaults=defaults,
89 89 encoding="UTF-8",
90 90 force_defaults=False
91 91 )
92 92
93 93 @HasPermissionAllDecorator('hg.admin')
94 94 def create(self):
95 95 """POST /admin/settings: Create a new item"""
96 96 # url('admin_settings')
97 97
98 98 @HasPermissionAllDecorator('hg.admin')
99 99 def new(self, format='html'):
100 100 """GET /admin/settings/new: Form to create a new item"""
101 101 # url('admin_new_setting')
102 102
103 103 @HasPermissionAllDecorator('hg.admin')
104 104 def update(self, setting_id):
105 105 """PUT /admin/settings/setting_id: Update an existing item"""
106 106 # Forms posted to this method should contain a hidden field:
107 107 # <input type="hidden" name="_method" value="PUT" />
108 108 # Or using helpers:
109 109 # h.form(url('admin_setting', setting_id=ID),
110 110 # method='put')
111 111 # url('admin_setting', setting_id=ID)
112 112
113 113 if setting_id == 'mapping':
114 114 rm_obsolete = request.POST.get('destroy', False)
115 115 log.debug('Rescanning directories with destroy=%s' % rm_obsolete)
116 116 initial = ScmModel().repo_scan()
117 117 log.debug('invalidating all repositories')
118 118 for repo_name in initial.keys():
119 119 ScmModel().mark_for_invalidation(repo_name)
120 120
121 121 added, removed = repo2db_mapper(initial, rm_obsolete)
122 122 _repr = lambda l: ', '.join(map(safe_unicode, l)) or '-'
123 123 h.flash(_('Repositories successfully '
124 124 'rescanned added: %s ; removed: %s') %
125 125 (_repr(added), _repr(removed)),
126 126 category='success')
127 127
128 128 if setting_id == 'whoosh':
129 129 repo_location = self._get_hg_ui_settings()['paths_root_path']
130 130 full_index = request.POST.get('full_index', False)
131 131 run_task(tasks.whoosh_index, repo_location, full_index)
132 132 h.flash(_('Whoosh reindex task scheduled'), category='success')
133 133
134 134 if setting_id == 'global':
135 135
136 136 application_form = ApplicationSettingsForm()()
137 137 try:
138 138 form_result = application_form.to_python(dict(request.POST))
139 139 except formencode.Invalid, errors:
140 140 return htmlfill.render(
141 141 render('admin/settings/settings.html'),
142 142 defaults=errors.value,
143 143 errors=errors.error_dict or {},
144 144 prefix_error=False,
145 145 encoding="UTF-8"
146 146 )
147 147
148 148 try:
149 149 sett1 = RhodeCodeSetting.get_by_name_or_create('title')
150 150 sett1.app_settings_value = form_result['rhodecode_title']
151 151 Session().add(sett1)
152 152
153 153 sett2 = RhodeCodeSetting.get_by_name_or_create('realm')
154 154 sett2.app_settings_value = form_result['rhodecode_realm']
155 155 Session().add(sett2)
156 156
157 157 sett3 = RhodeCodeSetting.get_by_name_or_create('ga_code')
158 158 sett3.app_settings_value = form_result['rhodecode_ga_code']
159 159 Session().add(sett3)
160 160
161 161 Session().commit()
162 162 set_rhodecode_config(config)
163 163 h.flash(_('Updated application settings'), category='success')
164 164
165 165 except Exception:
166 166 log.error(traceback.format_exc())
167 167 h.flash(_('Error occurred during updating '
168 168 'application settings'),
169 169 category='error')
170 170
171 171 if setting_id == 'visual':
172 172
173 173 application_form = ApplicationVisualisationForm()()
174 174 try:
175 175 form_result = application_form.to_python(dict(request.POST))
176 176 except formencode.Invalid, errors:
177 177 return htmlfill.render(
178 178 render('admin/settings/settings.html'),
179 179 defaults=errors.value,
180 180 errors=errors.error_dict or {},
181 181 prefix_error=False,
182 182 encoding="UTF-8"
183 183 )
184 184
185 185 try:
186 186 sett1 = RhodeCodeSetting.get_by_name_or_create('show_public_icon')
187 187 sett1.app_settings_value = \
188 188 form_result['rhodecode_show_public_icon']
189 189 Session().add(sett1)
190 190
191 191 sett2 = RhodeCodeSetting.get_by_name_or_create('show_private_icon')
192 192 sett2.app_settings_value = \
193 193 form_result['rhodecode_show_private_icon']
194 194 Session().add(sett2)
195 195
196 196 sett3 = RhodeCodeSetting.get_by_name_or_create('stylify_metatags')
197 197 sett3.app_settings_value = \
198 198 form_result['rhodecode_stylify_metatags']
199 199 Session().add(sett3)
200 200
201 sett4 = RhodeCodeSetting.get_by_name_or_create('lightweight_dashboard')
202 sett4.app_settings_value = \
203 form_result['rhodecode_lightweight_dashboard']
204 Session().add(sett4)
205
206 201 sett4 = RhodeCodeSetting.get_by_name_or_create('repository_fields')
207 202 sett4.app_settings_value = \
208 203 form_result['rhodecode_repository_fields']
209 204 Session().add(sett4)
210 205
211 206 Session().commit()
212 207 set_rhodecode_config(config)
213 208 h.flash(_('Updated visualisation settings'),
214 209 category='success')
215 210
216 211 except Exception:
217 212 log.error(traceback.format_exc())
218 213 h.flash(_('Error occurred during updating '
219 214 'visualisation settings'),
220 215 category='error')
221 216
222 217 if setting_id == 'vcs':
223 218 application_form = ApplicationUiSettingsForm()()
224 219 try:
225 220 form_result = application_form.to_python(dict(request.POST))
226 221 except formencode.Invalid, errors:
227 222 return htmlfill.render(
228 223 render('admin/settings/settings.html'),
229 224 defaults=errors.value,
230 225 errors=errors.error_dict or {},
231 226 prefix_error=False,
232 227 encoding="UTF-8"
233 228 )
234 229
235 230 try:
236 231 sett = RhodeCodeUi.get_by_key('push_ssl')
237 232 sett.ui_value = form_result['web_push_ssl']
238 233 Session().add(sett)
239 234
240 235 sett = RhodeCodeUi.get_by_key('/')
241 236 sett.ui_value = form_result['paths_root_path']
242 237 Session().add(sett)
243 238
244 239 #HOOKS
245 240 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_UPDATE)
246 241 sett.ui_active = form_result['hooks_changegroup_update']
247 242 Session().add(sett)
248 243
249 244 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_REPO_SIZE)
250 245 sett.ui_active = form_result['hooks_changegroup_repo_size']
251 246 Session().add(sett)
252 247
253 248 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_PUSH)
254 249 sett.ui_active = form_result['hooks_changegroup_push_logger']
255 250 Session().add(sett)
256 251
257 252 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_PULL)
258 253 sett.ui_active = form_result['hooks_outgoing_pull_logger']
259 254
260 255 Session().add(sett)
261 256
262 257 ## EXTENSIONS
263 258 sett = RhodeCodeUi.get_by_key('largefiles')
264 259 if not sett:
265 260 #make one if it's not there !
266 261 sett = RhodeCodeUi()
267 262 sett.ui_key = 'largefiles'
268 263 sett.ui_section = 'extensions'
269 264 sett.ui_active = form_result['extensions_largefiles']
270 265 Session().add(sett)
271 266
272 267 sett = RhodeCodeUi.get_by_key('hgsubversion')
273 268 if not sett:
274 269 #make one if it's not there !
275 270 sett = RhodeCodeUi()
276 271 sett.ui_key = 'hgsubversion'
277 272 sett.ui_section = 'extensions'
278 273
279 274 sett.ui_active = form_result['extensions_hgsubversion']
280 275 Session().add(sett)
281 276
282 277 # sett = RhodeCodeUi.get_by_key('hggit')
283 278 # if not sett:
284 279 # #make one if it's not there !
285 280 # sett = RhodeCodeUi()
286 281 # sett.ui_key = 'hggit'
287 282 # sett.ui_section = 'extensions'
288 283 #
289 284 # sett.ui_active = form_result['extensions_hggit']
290 285 # Session().add(sett)
291 286
292 287 Session().commit()
293 288
294 289 h.flash(_('Updated VCS settings'), category='success')
295 290
296 291 except Exception:
297 292 log.error(traceback.format_exc())
298 293 h.flash(_('Error occurred during updating '
299 294 'application settings'), category='error')
300 295
301 296 if setting_id == 'hooks':
302 297 ui_key = request.POST.get('new_hook_ui_key')
303 298 ui_value = request.POST.get('new_hook_ui_value')
304 299 try:
305 300
306 301 if ui_value and ui_key:
307 302 RhodeCodeUi.create_or_update_hook(ui_key, ui_value)
308 303 h.flash(_('Added new hook'),
309 304 category='success')
310 305
311 306 # check for edits
312 307 update = False
313 308 _d = request.POST.dict_of_lists()
314 309 for k, v in zip(_d.get('hook_ui_key', []),
315 310 _d.get('hook_ui_value_new', [])):
316 311 RhodeCodeUi.create_or_update_hook(k, v)
317 312 update = True
318 313
319 314 if update:
320 315 h.flash(_('Updated hooks'), category='success')
321 316 Session().commit()
322 317 except Exception:
323 318 log.error(traceback.format_exc())
324 319 h.flash(_('Error occurred during hook creation'),
325 320 category='error')
326 321
327 322 return redirect(url('admin_edit_setting', setting_id='hooks'))
328 323
329 324 if setting_id == 'email':
330 325 test_email = request.POST.get('test_email')
331 326 test_email_subj = 'RhodeCode TestEmail'
332 327 test_email_body = 'RhodeCode Email test'
333 328
334 329 test_email_html_body = EmailNotificationModel()\
335 330 .get_email_tmpl(EmailNotificationModel.TYPE_DEFAULT,
336 331 body=test_email_body)
337 332
338 333 recipients = [test_email] if test_email else None
339 334
340 335 run_task(tasks.send_email, recipients, test_email_subj,
341 336 test_email_body, test_email_html_body)
342 337
343 338 h.flash(_('Email task created'), category='success')
344 339 return redirect(url('admin_settings'))
345 340
346 341 @HasPermissionAllDecorator('hg.admin')
347 342 def delete(self, setting_id):
348 343 """DELETE /admin/settings/setting_id: Delete an existing item"""
349 344 # Forms posted to this method should contain a hidden field:
350 345 # <input type="hidden" name="_method" value="DELETE" />
351 346 # Or using helpers:
352 347 # h.form(url('admin_setting', setting_id=ID),
353 348 # method='delete')
354 349 # url('admin_setting', setting_id=ID)
355 350 if setting_id == 'hooks':
356 351 hook_id = request.POST.get('hook_id')
357 352 RhodeCodeUi.delete(hook_id)
358 353 Session().commit()
359 354
360 355 @HasPermissionAllDecorator('hg.admin')
361 356 def show(self, setting_id, format='html'):
362 357 """
363 358 GET /admin/settings/setting_id: Show a specific item"""
364 359 # url('admin_setting', setting_id=ID)
365 360
366 361 @HasPermissionAllDecorator('hg.admin')
367 362 def edit(self, setting_id, format='html'):
368 363 """
369 364 GET /admin/settings/setting_id/edit: Form to
370 365 edit an existing item"""
371 366 # url('admin_edit_setting', setting_id=ID)
372 367
373 368 c.hooks = RhodeCodeUi.get_builtin_hooks()
374 369 c.custom_hooks = RhodeCodeUi.get_custom_hooks()
375 370
376 371 return htmlfill.render(
377 372 render('admin/settings/hooks.html'),
378 373 defaults={},
379 374 encoding="UTF-8",
380 375 force_defaults=False
381 376 )
382 377
383 378 def _load_my_repos_data(self):
384 379 repos_list = Session().query(Repository)\
385 380 .filter(Repository.user_id ==
386 381 self.rhodecode_user.user_id)\
387 382 .order_by(func.lower(Repository.repo_name)).all()
388 383
389 384 repos_data = RepoModel().get_repos_as_dict(repos_list=repos_list,
390 385 admin=True)
391 386 #json used to render the grid
392 387 return json.dumps(repos_data)
393 388
394 389 @NotAnonymous()
395 390 def my_account(self):
396 391 """
397 392 GET /_admin/my_account Displays info about my account
398 393 """
399 394 # url('admin_settings_my_account')
400 395
401 396 c.user = User.get(self.rhodecode_user.user_id)
402 397 c.perm_user = AuthUser(user_id=self.rhodecode_user.user_id,
403 398 ip_addr=self.ip_addr)
404 399 c.ldap_dn = c.user.ldap_dn
405 400
406 401 if c.user.username == 'default':
407 402 h.flash(_("You can't edit this user since it's"
408 403 " crucial for entire application"), category='warning')
409 404 return redirect(url('users'))
410 405
411 406 #json used to render the grid
412 407 c.data = self._load_my_repos_data()
413 408
414 409 defaults = c.user.get_dict()
415 410
416 411 c.form = htmlfill.render(
417 412 render('admin/users/user_edit_my_account_form.html'),
418 413 defaults=defaults,
419 414 encoding="UTF-8",
420 415 force_defaults=False
421 416 )
422 417 return render('admin/users/user_edit_my_account.html')
423 418
424 419 @NotAnonymous()
425 420 def my_account_update(self):
426 421 """PUT /_admin/my_account_update: Update an existing item"""
427 422 # Forms posted to this method should contain a hidden field:
428 423 # <input type="hidden" name="_method" value="PUT" />
429 424 # Or using helpers:
430 425 # h.form(url('admin_settings_my_account_update'),
431 426 # method='put')
432 427 # url('admin_settings_my_account_update', id=ID)
433 428 uid = self.rhodecode_user.user_id
434 429 c.user = User.get(self.rhodecode_user.user_id)
435 430 c.perm_user = AuthUser(user_id=self.rhodecode_user.user_id,
436 431 ip_addr=self.ip_addr)
437 432 c.ldap_dn = c.user.ldap_dn
438 433 email = self.rhodecode_user.email
439 434 _form = UserForm(edit=True,
440 435 old_data={'user_id': uid, 'email': email})()
441 436 form_result = {}
442 437 try:
443 438 form_result = _form.to_python(dict(request.POST))
444 439 skip_attrs = ['admin', 'active'] # skip attr for my account
445 440 if c.ldap_dn:
446 441 #forbid updating username for ldap accounts
447 442 skip_attrs.append('username')
448 443 UserModel().update(uid, form_result, skip_attrs=skip_attrs)
449 444 h.flash(_('Your account was updated successfully'),
450 445 category='success')
451 446 Session().commit()
452 447 except formencode.Invalid, errors:
453 448 #json used to render the grid
454 449 c.data = self._load_my_repos_data()
455 450 c.form = htmlfill.render(
456 451 render('admin/users/user_edit_my_account_form.html'),
457 452 defaults=errors.value,
458 453 errors=errors.error_dict or {},
459 454 prefix_error=False,
460 455 encoding="UTF-8")
461 456 return render('admin/users/user_edit_my_account.html')
462 457 except Exception:
463 458 log.error(traceback.format_exc())
464 459 h.flash(_('Error occurred during update of user %s') \
465 460 % form_result.get('username'), category='error')
466 461
467 462 return redirect(url('my_account'))
468 463
469 464 @NotAnonymous()
470 465 def my_account_my_pullrequests(self):
471 466 c.show_closed = request.GET.get('pr_show_closed')
472 467
473 468 def _filter(pr):
474 469 s = sorted(pr, key=lambda o: o.created_on, reverse=True)
475 470 if not c.show_closed:
476 471 s = filter(lambda p: p.status != PullRequest.STATUS_CLOSED, s)
477 472 return s
478 473
479 474 c.my_pull_requests = _filter(PullRequest.query()\
480 475 .filter(PullRequest.user_id ==
481 476 self.rhodecode_user.user_id)\
482 477 .all())
483 478
484 479 c.participate_in_pull_requests = _filter([
485 480 x.pull_request for x in PullRequestReviewers.query()\
486 481 .filter(PullRequestReviewers.user_id ==
487 482 self.rhodecode_user.user_id).all()])
488 483
489 484 return render('admin/users/user_edit_my_account_pullrequests.html')
490 485
491 486 def _get_hg_ui_settings(self):
492 487 ret = RhodeCodeUi.query().all()
493 488
494 489 if not ret:
495 490 raise Exception('Could not get application ui settings !')
496 491 settings = {}
497 492 for each in ret:
498 493 k = each.ui_key
499 494 v = each.ui_value
500 495 if k == '/':
501 496 k = 'root_path'
502 497
503 498 if k == 'push_ssl':
504 499 v = str2bool(v)
505 500
506 501 if k.find('.') != -1:
507 502 k = k.replace('.', '_')
508 503
509 504 if each.ui_section in ['hooks', 'extensions']:
510 505 v = each.ui_active
511 506
512 507 settings[each.ui_section + '_' + k] = v
513 508 return settings
@@ -1,89 +1,85 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.home
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Home controller for Rhodecode
7 7
8 8 :created_on: Feb 18, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import logging
27 27
28 28 from pylons import tmpl_context as c, request
29 29 from pylons.i18n.translation import _
30 30 from webob.exc import HTTPBadRequest
31 31 from sqlalchemy.sql.expression import func
32 32
33 33 import rhodecode
34 34 from rhodecode.lib import helpers as h
35 35 from rhodecode.lib.compat import json
36 36 from rhodecode.lib.auth import LoginRequired
37 37 from rhodecode.lib.base import BaseController, render
38 38 from rhodecode.model.db import Repository
39 39 from rhodecode.model.repo import RepoModel
40 40
41 41
42 42 log = logging.getLogger(__name__)
43 43
44 44
45 45 class HomeController(BaseController):
46 46
47 47 def __before__(self):
48 48 super(HomeController, self).__before__()
49 49
50 50 @LoginRequired()
51 51 def index(self):
52 52 c.groups = self.scm_model.get_repos_groups()
53 53 c.group = None
54 54
55 if not c.visual.lightweight_dashboard:
56 c.repos_list = self.scm_model.get_repos()
57 ## lightweight version of dashboard
58 else:
59 c.repos_list = Repository.query()\
60 .filter(Repository.group_id == None)\
61 .order_by(func.lower(Repository.repo_name))\
62 .all()
55 c.repos_list = Repository.query()\
56 .filter(Repository.group_id == None)\
57 .order_by(func.lower(Repository.repo_name))\
58 .all()
63 59
64 repos_data = RepoModel().get_repos_as_dict(repos_list=c.repos_list,
65 admin=False)
66 #json used to render the grid
67 c.data = json.dumps(repos_data)
60 repos_data = RepoModel().get_repos_as_dict(repos_list=c.repos_list,
61 admin=False)
62 #json used to render the grid
63 c.data = json.dumps(repos_data)
68 64
69 65 return render('/index.html')
70 66
71 67 @LoginRequired()
72 68 def repo_switcher(self):
73 69 if request.is_xhr:
74 70 all_repos = Repository.query().order_by(Repository.repo_name).all()
75 71 c.repos_list = self.scm_model.get_repos(all_repos,
76 72 sort_key='name_sort',
77 73 simple=True)
78 74 return render('/repo_switcher_list.html')
79 75 else:
80 76 raise HTTPBadRequest()
81 77
82 78 @LoginRequired()
83 79 def branch_tag_switcher(self, repo_name):
84 80 if request.is_xhr:
85 81 c.rhodecode_db_repo = Repository.get_by_repo_name(c.repo_name)
86 82 if c.rhodecode_db_repo:
87 83 c.rhodecode_repo = c.rhodecode_db_repo.scm_instance
88 84 return render('/switch_to_list.html')
89 85 raise HTTPBadRequest()
@@ -1,345 +1,344 b''
1 1 """The base Controller API
2 2
3 3 Provides the BaseController class for subclassing.
4 4 """
5 5 import logging
6 6 import time
7 7 import traceback
8 8
9 9 from paste.auth.basic import AuthBasicAuthenticator
10 10 from paste.httpexceptions import HTTPUnauthorized, HTTPForbidden
11 11 from paste.httpheaders import WWW_AUTHENTICATE, AUTHORIZATION
12 12
13 13 from pylons import config, tmpl_context as c, request, session, url
14 14 from pylons.controllers import WSGIController
15 15 from pylons.controllers.util import redirect
16 16 from pylons.templating import render_mako as render
17 17
18 18 from rhodecode import __version__, BACKENDS
19 19
20 20 from rhodecode.lib.utils2 import str2bool, safe_unicode, AttributeDict,\
21 21 safe_str, safe_int
22 22 from rhodecode.lib.auth import AuthUser, get_container_username, authfunc,\
23 23 HasPermissionAnyMiddleware, CookieStoreWrapper
24 24 from rhodecode.lib.utils import get_repo_slug
25 25 from rhodecode.model import meta
26 26
27 27 from rhodecode.model.db import Repository, RhodeCodeUi, User, RhodeCodeSetting
28 28 from rhodecode.model.notification import NotificationModel
29 29 from rhodecode.model.scm import ScmModel
30 30 from rhodecode.model.meta import Session
31 31
32 32 log = logging.getLogger(__name__)
33 33
34 34
35 35 def _filter_proxy(ip):
36 36 """
37 37 HEADERS can have mutliple ips inside the left-most being the original
38 38 client, and each successive proxy that passed the request adding the IP
39 39 address where it received the request from.
40 40
41 41 :param ip:
42 42 """
43 43 if ',' in ip:
44 44 _ips = ip.split(',')
45 45 _first_ip = _ips[0].strip()
46 46 log.debug('Got multiple IPs %s, using %s' % (','.join(_ips), _first_ip))
47 47 return _first_ip
48 48 return ip
49 49
50 50
51 51 def _get_ip_addr(environ):
52 52 proxy_key = 'HTTP_X_REAL_IP'
53 53 proxy_key2 = 'HTTP_X_FORWARDED_FOR'
54 54 def_key = 'REMOTE_ADDR'
55 55
56 56 ip = environ.get(proxy_key)
57 57 if ip:
58 58 return _filter_proxy(ip)
59 59
60 60 ip = environ.get(proxy_key2)
61 61 if ip:
62 62 return _filter_proxy(ip)
63 63
64 64 ip = environ.get(def_key, '0.0.0.0')
65 65 return _filter_proxy(ip)
66 66
67 67
68 68 def _get_access_path(environ):
69 69 path = environ.get('PATH_INFO')
70 70 org_req = environ.get('pylons.original_request')
71 71 if org_req:
72 72 path = org_req.environ.get('PATH_INFO')
73 73 return path
74 74
75 75
76 76 class BasicAuth(AuthBasicAuthenticator):
77 77
78 78 def __init__(self, realm, authfunc, auth_http_code=None):
79 79 self.realm = realm
80 80 self.authfunc = authfunc
81 81 self._rc_auth_http_code = auth_http_code
82 82
83 83 def build_authentication(self):
84 84 head = WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm)
85 85 if self._rc_auth_http_code and self._rc_auth_http_code == '403':
86 86 # return 403 if alternative http return code is specified in
87 87 # RhodeCode config
88 88 return HTTPForbidden(headers=head)
89 89 return HTTPUnauthorized(headers=head)
90 90
91 91 def authenticate(self, environ):
92 92 authorization = AUTHORIZATION(environ)
93 93 if not authorization:
94 94 return self.build_authentication()
95 95 (authmeth, auth) = authorization.split(' ', 1)
96 96 if 'basic' != authmeth.lower():
97 97 return self.build_authentication()
98 98 auth = auth.strip().decode('base64')
99 99 _parts = auth.split(':', 1)
100 100 if len(_parts) == 2:
101 101 username, password = _parts
102 102 if self.authfunc(environ, username, password):
103 103 return username
104 104 return self.build_authentication()
105 105
106 106 __call__ = authenticate
107 107
108 108
109 109 class BaseVCSController(object):
110 110
111 111 def __init__(self, application, config):
112 112 self.application = application
113 113 self.config = config
114 114 # base path of repo locations
115 115 self.basepath = self.config['base_path']
116 116 #authenticate this mercurial request using authfunc
117 117 self.authenticate = BasicAuth('', authfunc,
118 118 config.get('auth_ret_code'))
119 119 self.ip_addr = '0.0.0.0'
120 120
121 121 def _handle_request(self, environ, start_response):
122 122 raise NotImplementedError()
123 123
124 124 def _get_by_id(self, repo_name):
125 125 """
126 126 Get's a special pattern _<ID> from clone url and tries to replace it
127 127 with a repository_name for support of _<ID> non changable urls
128 128
129 129 :param repo_name:
130 130 """
131 131 try:
132 132 data = repo_name.split('/')
133 133 if len(data) >= 2:
134 134 by_id = data[1].split('_')
135 135 if len(by_id) == 2 and by_id[1].isdigit():
136 136 _repo_name = Repository.get(by_id[1]).repo_name
137 137 data[1] = _repo_name
138 138 except Exception:
139 139 log.debug('Failed to extract repo_name from id %s' % (
140 140 traceback.format_exc()
141 141 )
142 142 )
143 143
144 144 return '/'.join(data)
145 145
146 146 def _invalidate_cache(self, repo_name):
147 147 """
148 148 Set's cache for this repository for invalidation on next access
149 149
150 150 :param repo_name: full repo name, also a cache key
151 151 """
152 152 ScmModel().mark_for_invalidation(repo_name)
153 153
154 154 def _check_permission(self, action, user, repo_name, ip_addr=None):
155 155 """
156 156 Checks permissions using action (push/pull) user and repository
157 157 name
158 158
159 159 :param action: push or pull action
160 160 :param user: user instance
161 161 :param repo_name: repository name
162 162 """
163 163 #check IP
164 164 authuser = AuthUser(user_id=user.user_id, ip_addr=ip_addr)
165 165 if not authuser.ip_allowed:
166 166 return False
167 167 else:
168 168 log.info('Access for IP:%s allowed' % (ip_addr))
169 169 if action == 'push':
170 170 if not HasPermissionAnyMiddleware('repository.write',
171 171 'repository.admin')(user,
172 172 repo_name):
173 173 return False
174 174
175 175 else:
176 176 #any other action need at least read permission
177 177 if not HasPermissionAnyMiddleware('repository.read',
178 178 'repository.write',
179 179 'repository.admin')(user,
180 180 repo_name):
181 181 return False
182 182
183 183 return True
184 184
185 185 def _get_ip_addr(self, environ):
186 186 return _get_ip_addr(environ)
187 187
188 188 def _check_ssl(self, environ, start_response):
189 189 """
190 190 Checks the SSL check flag and returns False if SSL is not present
191 191 and required True otherwise
192 192 """
193 193 org_proto = environ['wsgi._org_proto']
194 194 #check if we have SSL required ! if not it's a bad request !
195 195 require_ssl = str2bool(RhodeCodeUi.get_by_key('push_ssl').ui_value)
196 196 if require_ssl and org_proto == 'http':
197 197 log.debug('proto is %s and SSL is required BAD REQUEST !'
198 198 % org_proto)
199 199 return False
200 200 return True
201 201
202 202 def _check_locking_state(self, environ, action, repo, user_id):
203 203 """
204 204 Checks locking on this repository, if locking is enabled and lock is
205 205 present returns a tuple of make_lock, locked, locked_by.
206 206 make_lock can have 3 states None (do nothing) True, make lock
207 207 False release lock, This value is later propagated to hooks, which
208 208 do the locking. Think about this as signals passed to hooks what to do.
209 209
210 210 """
211 211 locked = False # defines that locked error should be thrown to user
212 212 make_lock = None
213 213 repo = Repository.get_by_repo_name(repo)
214 214 user = User.get(user_id)
215 215
216 216 # this is kind of hacky, but due to how mercurial handles client-server
217 217 # server see all operation on changeset; bookmarks, phases and
218 218 # obsolescence marker in different transaction, we don't want to check
219 219 # locking on those
220 220 obsolete_call = environ['QUERY_STRING'] in ['cmd=listkeys',]
221 221 locked_by = repo.locked
222 222 if repo and repo.enable_locking and not obsolete_call:
223 223 if action == 'push':
224 224 #check if it's already locked !, if it is compare users
225 225 user_id, _date = repo.locked
226 226 if user.user_id == user_id:
227 227 log.debug('Got push from user %s, now unlocking' % (user))
228 228 # unlock if we have push from user who locked
229 229 make_lock = False
230 230 else:
231 231 # we're not the same user who locked, ban with 423 !
232 232 locked = True
233 233 if action == 'pull':
234 234 if repo.locked[0] and repo.locked[1]:
235 235 locked = True
236 236 else:
237 237 log.debug('Setting lock on repo %s by %s' % (repo, user))
238 238 make_lock = True
239 239
240 240 else:
241 241 log.debug('Repository %s do not have locking enabled' % (repo))
242 242 log.debug('FINAL locking values make_lock:%s,locked:%s,locked_by:%s'
243 243 % (make_lock, locked, locked_by))
244 244 return make_lock, locked, locked_by
245 245
246 246 def __call__(self, environ, start_response):
247 247 start = time.time()
248 248 try:
249 249 return self._handle_request(environ, start_response)
250 250 finally:
251 251 log = logging.getLogger('rhodecode.' + self.__class__.__name__)
252 252 log.debug('Request time: %.3fs' % (time.time() - start))
253 253 meta.Session.remove()
254 254
255 255
256 256 class BaseController(WSGIController):
257 257
258 258 def __before__(self):
259 259 """
260 260 __before__ is called before controller methods and after __call__
261 261 """
262 262 c.rhodecode_version = __version__
263 263 c.rhodecode_instanceid = config.get('instance_id')
264 264 c.rhodecode_name = config.get('rhodecode_title')
265 265 c.use_gravatar = str2bool(config.get('use_gravatar'))
266 266 c.ga_code = config.get('rhodecode_ga_code')
267 267 # Visual options
268 268 c.visual = AttributeDict({})
269 269 rc_config = RhodeCodeSetting.get_app_settings()
270 270
271 271 c.visual.show_public_icon = str2bool(rc_config.get('rhodecode_show_public_icon'))
272 272 c.visual.show_private_icon = str2bool(rc_config.get('rhodecode_show_private_icon'))
273 273 c.visual.stylify_metatags = str2bool(rc_config.get('rhodecode_stylify_metatags'))
274 c.visual.lightweight_dashboard = str2bool(rc_config.get('rhodecode_lightweight_dashboard'))
275 c.visual.lightweight_dashboard_items = safe_int(config.get('dashboard_items', 100))
274 c.visual.dashboard_items = safe_int(config.get('dashboard_items', 100))
276 275 c.visual.repository_fields = str2bool(rc_config.get('rhodecode_repository_fields'))
277 276 c.repo_name = get_repo_slug(request) # can be empty
278 277 c.backends = BACKENDS.keys()
279 278 c.unread_notifications = NotificationModel()\
280 279 .get_unread_cnt_for_user(c.rhodecode_user.user_id)
281 280 self.cut_off_limit = int(config.get('cut_off_limit'))
282 281
283 282 self.sa = meta.Session
284 283 self.scm_model = ScmModel(self.sa)
285 284
286 285 def __call__(self, environ, start_response):
287 286 """Invoke the Controller"""
288 287 # WSGIController.__call__ dispatches to the Controller method
289 288 # the request is routed to. This routing information is
290 289 # available in environ['pylons.routes_dict']
291 290 try:
292 291 self.ip_addr = _get_ip_addr(environ)
293 292 # make sure that we update permissions each time we call controller
294 293 api_key = request.GET.get('api_key')
295 294 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
296 295 user_id = cookie_store.get('user_id', None)
297 296 username = get_container_username(environ, config)
298 297 auth_user = AuthUser(user_id, api_key, username, self.ip_addr)
299 298 request.user = auth_user
300 299 self.rhodecode_user = c.rhodecode_user = auth_user
301 300 if not self.rhodecode_user.is_authenticated and \
302 301 self.rhodecode_user.user_id is not None:
303 302 self.rhodecode_user.set_authenticated(
304 303 cookie_store.get('is_authenticated')
305 304 )
306 305 log.info('IP: %s User: %s accessed %s' % (
307 306 self.ip_addr, auth_user, safe_unicode(_get_access_path(environ)))
308 307 )
309 308 return WSGIController.__call__(self, environ, start_response)
310 309 finally:
311 310 meta.Session.remove()
312 311
313 312
314 313 class BaseRepoController(BaseController):
315 314 """
316 315 Base class for controllers responsible for loading all needed data for
317 316 repository loaded items are
318 317
319 318 c.rhodecode_repo: instance of scm repository
320 319 c.rhodecode_db_repo: instance of db
321 320 c.repository_followers: number of followers
322 321 c.repository_forks: number of forks
323 322 c.repository_following: weather the current user is following the current repo
324 323 """
325 324
326 325 def __before__(self):
327 326 super(BaseRepoController, self).__before__()
328 327 if c.repo_name:
329 328
330 329 dbr = c.rhodecode_db_repo = Repository.get_by_repo_name(c.repo_name)
331 330 c.rhodecode_repo = c.rhodecode_db_repo.scm_instance
332 331 # update last change according to VCS data
333 332 dbr.update_changeset_cache(dbr.get_changeset())
334 333 if c.rhodecode_repo is None:
335 334 log.error('%s this repository is present in database but it '
336 335 'cannot be created as an scm instance', c.repo_name)
337 336
338 337 redirect(url('home'))
339 338
340 339 # some globals counter for menu
341 340 c.repository_followers = self.scm_model.get_followers(dbr)
342 341 c.repository_forks = self.scm_model.get_forks(dbr)
343 342 c.repository_pull_requests = self.scm_model.get_pull_requests(dbr)
344 343 c.repository_following = self.scm_model.is_following_repo(c.repo_name,
345 344 self.rhodecode_user.user_id)
@@ -1,421 +1,420 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 import logging
23 23
24 24 import formencode
25 25 from formencode import All
26 26
27 27 from pylons.i18n.translation import _
28 28
29 29 from rhodecode.model import validators as v
30 30 from rhodecode import BACKENDS
31 31
32 32 log = logging.getLogger(__name__)
33 33
34 34
35 35 class LoginForm(formencode.Schema):
36 36 allow_extra_fields = True
37 37 filter_extra_fields = True
38 38 username = v.UnicodeString(
39 39 strip=True,
40 40 min=1,
41 41 not_empty=True,
42 42 messages={
43 43 'empty': _(u'Please enter a login'),
44 44 'tooShort': _(u'Enter a value %(min)i characters long or more')}
45 45 )
46 46
47 47 password = v.UnicodeString(
48 48 strip=False,
49 49 min=3,
50 50 not_empty=True,
51 51 messages={
52 52 'empty': _(u'Please enter a password'),
53 53 'tooShort': _(u'Enter %(min)i characters or more')}
54 54 )
55 55
56 56 remember = v.StringBoolean(if_missing=False)
57 57
58 58 chained_validators = [v.ValidAuth()]
59 59
60 60
61 61 def UserForm(edit=False, old_data={}):
62 62 class _UserForm(formencode.Schema):
63 63 allow_extra_fields = True
64 64 filter_extra_fields = True
65 65 username = All(v.UnicodeString(strip=True, min=1, not_empty=True),
66 66 v.ValidUsername(edit, old_data))
67 67 if edit:
68 68 new_password = All(
69 69 v.ValidPassword(),
70 70 v.UnicodeString(strip=False, min=6, not_empty=False)
71 71 )
72 72 password_confirmation = All(
73 73 v.ValidPassword(),
74 74 v.UnicodeString(strip=False, min=6, not_empty=False),
75 75 )
76 76 admin = v.StringBoolean(if_missing=False)
77 77 else:
78 78 password = All(
79 79 v.ValidPassword(),
80 80 v.UnicodeString(strip=False, min=6, not_empty=True)
81 81 )
82 82 password_confirmation = All(
83 83 v.ValidPassword(),
84 84 v.UnicodeString(strip=False, min=6, not_empty=False)
85 85 )
86 86
87 87 active = v.StringBoolean(if_missing=False)
88 88 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
89 89 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
90 90 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
91 91
92 92 chained_validators = [v.ValidPasswordsMatch()]
93 93
94 94 return _UserForm
95 95
96 96
97 97 def UserGroupForm(edit=False, old_data={}, available_members=[]):
98 98 class _UserGroupForm(formencode.Schema):
99 99 allow_extra_fields = True
100 100 filter_extra_fields = True
101 101
102 102 users_group_name = All(
103 103 v.UnicodeString(strip=True, min=1, not_empty=True),
104 104 v.ValidUserGroup(edit, old_data)
105 105 )
106 106
107 107 users_group_active = v.StringBoolean(if_missing=False)
108 108
109 109 if edit:
110 110 users_group_members = v.OneOf(
111 111 available_members, hideList=False, testValueList=True,
112 112 if_missing=None, not_empty=False
113 113 )
114 114
115 115 return _UserGroupForm
116 116
117 117
118 118 def ReposGroupForm(edit=False, old_data={}, available_groups=[],
119 119 can_create_in_root=False):
120 120 class _ReposGroupForm(formencode.Schema):
121 121 allow_extra_fields = True
122 122 filter_extra_fields = False
123 123
124 124 group_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
125 125 v.SlugifyName())
126 126 group_description = v.UnicodeString(strip=True, min=1,
127 127 not_empty=False)
128 128 if edit:
129 129 #FIXME: do a special check that we cannot move a group to one of
130 130 #it's children
131 131 pass
132 132 group_parent_id = All(v.CanCreateGroup(can_create_in_root),
133 133 v.OneOf(available_groups, hideList=False,
134 134 testValueList=True,
135 135 if_missing=None, not_empty=True))
136 136 enable_locking = v.StringBoolean(if_missing=False)
137 137 chained_validators = [v.ValidReposGroup(edit, old_data)]
138 138
139 139 return _ReposGroupForm
140 140
141 141
142 142 def RegisterForm(edit=False, old_data={}):
143 143 class _RegisterForm(formencode.Schema):
144 144 allow_extra_fields = True
145 145 filter_extra_fields = True
146 146 username = All(
147 147 v.ValidUsername(edit, old_data),
148 148 v.UnicodeString(strip=True, min=1, not_empty=True)
149 149 )
150 150 password = All(
151 151 v.ValidPassword(),
152 152 v.UnicodeString(strip=False, min=6, not_empty=True)
153 153 )
154 154 password_confirmation = All(
155 155 v.ValidPassword(),
156 156 v.UnicodeString(strip=False, min=6, not_empty=True)
157 157 )
158 158 active = v.StringBoolean(if_missing=False)
159 159 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
160 160 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
161 161 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
162 162
163 163 chained_validators = [v.ValidPasswordsMatch()]
164 164
165 165 return _RegisterForm
166 166
167 167
168 168 def PasswordResetForm():
169 169 class _PasswordResetForm(formencode.Schema):
170 170 allow_extra_fields = True
171 171 filter_extra_fields = True
172 172 email = All(v.ValidSystemEmail(), v.Email(not_empty=True))
173 173 return _PasswordResetForm
174 174
175 175
176 176 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
177 177 repo_groups=[], landing_revs=[]):
178 178 class _RepoForm(formencode.Schema):
179 179 allow_extra_fields = True
180 180 filter_extra_fields = False
181 181 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
182 182 v.SlugifyName())
183 183 repo_group = All(v.CanWriteGroup(old_data),
184 184 v.OneOf(repo_groups, hideList=True))
185 185 repo_type = v.OneOf(supported_backends)
186 186 repo_description = v.UnicodeString(strip=True, min=1, not_empty=False)
187 187 repo_private = v.StringBoolean(if_missing=False)
188 188 repo_landing_rev = v.OneOf(landing_revs, hideList=True)
189 189 clone_uri = All(v.UnicodeString(strip=True, min=1, not_empty=False))
190 190
191 191 repo_enable_statistics = v.StringBoolean(if_missing=False)
192 192 repo_enable_downloads = v.StringBoolean(if_missing=False)
193 193 repo_enable_locking = v.StringBoolean(if_missing=False)
194 194
195 195 if edit:
196 196 #this is repo owner
197 197 user = All(v.UnicodeString(not_empty=True), v.ValidRepoUser())
198 198
199 199 chained_validators = [v.ValidCloneUri(),
200 200 v.ValidRepoName(edit, old_data)]
201 201 return _RepoForm
202 202
203 203
204 204 def RepoPermsForm():
205 205 class _RepoPermsForm(formencode.Schema):
206 206 allow_extra_fields = True
207 207 filter_extra_fields = False
208 208 chained_validators = [v.ValidPerms(type_='repo')]
209 209 return _RepoPermsForm
210 210
211 211
212 212 def RepoGroupPermsForm():
213 213 class _RepoGroupPermsForm(formencode.Schema):
214 214 allow_extra_fields = True
215 215 filter_extra_fields = False
216 216 recursive = v.StringBoolean(if_missing=False)
217 217 chained_validators = [v.ValidPerms(type_='repo_group')]
218 218 return _RepoGroupPermsForm
219 219
220 220
221 221 def UserGroupPermsForm():
222 222 class _UserPermsForm(formencode.Schema):
223 223 allow_extra_fields = True
224 224 filter_extra_fields = False
225 225 chained_validators = [v.ValidPerms(type_='user_group')]
226 226 return _UserPermsForm
227 227
228 228
229 229 def RepoFieldForm():
230 230 class _RepoFieldForm(formencode.Schema):
231 231 filter_extra_fields = True
232 232 allow_extra_fields = True
233 233
234 234 new_field_key = All(v.FieldKey(),
235 235 v.UnicodeString(strip=True, min=3, not_empty=True))
236 236 new_field_value = v.UnicodeString(not_empty=False, if_missing='')
237 237 new_field_type = v.OneOf(['str', 'unicode', 'list', 'tuple'],
238 238 if_missing='str')
239 239 new_field_label = v.UnicodeString(not_empty=False)
240 240 new_field_desc = v.UnicodeString(not_empty=False)
241 241
242 242 return _RepoFieldForm
243 243
244 244
245 245 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
246 246 repo_groups=[], landing_revs=[]):
247 247 class _RepoForkForm(formencode.Schema):
248 248 allow_extra_fields = True
249 249 filter_extra_fields = False
250 250 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
251 251 v.SlugifyName())
252 252 repo_group = All(v.CanWriteGroup(),
253 253 v.OneOf(repo_groups, hideList=True))
254 254 repo_type = All(v.ValidForkType(old_data), v.OneOf(supported_backends))
255 255 description = v.UnicodeString(strip=True, min=1, not_empty=True)
256 256 private = v.StringBoolean(if_missing=False)
257 257 copy_permissions = v.StringBoolean(if_missing=False)
258 258 update_after_clone = v.StringBoolean(if_missing=False)
259 259 fork_parent_id = v.UnicodeString()
260 260 chained_validators = [v.ValidForkName(edit, old_data)]
261 261 landing_rev = v.OneOf(landing_revs, hideList=True)
262 262
263 263 return _RepoForkForm
264 264
265 265
266 266 def ApplicationSettingsForm():
267 267 class _ApplicationSettingsForm(formencode.Schema):
268 268 allow_extra_fields = True
269 269 filter_extra_fields = False
270 270 rhodecode_title = v.UnicodeString(strip=True, min=1, not_empty=True)
271 271 rhodecode_realm = v.UnicodeString(strip=True, min=1, not_empty=True)
272 272 rhodecode_ga_code = v.UnicodeString(strip=True, min=1, not_empty=False)
273 273
274 274 return _ApplicationSettingsForm
275 275
276 276
277 277 def ApplicationVisualisationForm():
278 278 class _ApplicationVisualisationForm(formencode.Schema):
279 279 allow_extra_fields = True
280 280 filter_extra_fields = False
281 281 rhodecode_show_public_icon = v.StringBoolean(if_missing=False)
282 282 rhodecode_show_private_icon = v.StringBoolean(if_missing=False)
283 283 rhodecode_stylify_metatags = v.StringBoolean(if_missing=False)
284 284
285 rhodecode_lightweight_dashboard = v.StringBoolean(if_missing=False)
286 285 rhodecode_repository_fields = v.StringBoolean(if_missing=False)
287 286 rhodecode_lightweight_journal = v.StringBoolean(if_missing=False)
288 287
289 288 return _ApplicationVisualisationForm
290 289
291 290
292 291 def ApplicationUiSettingsForm():
293 292 class _ApplicationUiSettingsForm(formencode.Schema):
294 293 allow_extra_fields = True
295 294 filter_extra_fields = False
296 295 web_push_ssl = v.StringBoolean(if_missing=False)
297 296 paths_root_path = All(
298 297 v.ValidPath(),
299 298 v.UnicodeString(strip=True, min=1, not_empty=True)
300 299 )
301 300 hooks_changegroup_update = v.StringBoolean(if_missing=False)
302 301 hooks_changegroup_repo_size = v.StringBoolean(if_missing=False)
303 302 hooks_changegroup_push_logger = v.StringBoolean(if_missing=False)
304 303 hooks_outgoing_pull_logger = v.StringBoolean(if_missing=False)
305 304
306 305 extensions_largefiles = v.StringBoolean(if_missing=False)
307 306 extensions_hgsubversion = v.StringBoolean(if_missing=False)
308 307 extensions_hggit = v.StringBoolean(if_missing=False)
309 308
310 309 return _ApplicationUiSettingsForm
311 310
312 311
313 312 def DefaultPermissionsForm(repo_perms_choices, group_perms_choices,
314 313 user_group_perms_choices, create_choices,
315 314 repo_group_create_choices, user_group_create_choices,
316 315 fork_choices, register_choices):
317 316 class _DefaultPermissionsForm(formencode.Schema):
318 317 allow_extra_fields = True
319 318 filter_extra_fields = True
320 319 overwrite_default_repo = v.StringBoolean(if_missing=False)
321 320 overwrite_default_group = v.StringBoolean(if_missing=False)
322 321 overwrite_default_user_group = v.StringBoolean(if_missing=False)
323 322 anonymous = v.StringBoolean(if_missing=False)
324 323 default_repo_perm = v.OneOf(repo_perms_choices)
325 324 default_group_perm = v.OneOf(group_perms_choices)
326 325 default_user_group_perm = v.OneOf(user_group_perms_choices)
327 326
328 327 default_repo_create = v.OneOf(create_choices)
329 328 default_user_group_create = v.OneOf(user_group_create_choices)
330 329 #default_repo_group_create = v.OneOf(repo_group_create_choices) #not impl. yet
331 330 default_fork = v.OneOf(fork_choices)
332 331
333 332 default_register = v.OneOf(register_choices)
334 333 return _DefaultPermissionsForm
335 334
336 335
337 336 def CustomDefaultPermissionsForm():
338 337 class _CustomDefaultPermissionsForm(formencode.Schema):
339 338 filter_extra_fields = True
340 339 allow_extra_fields = True
341 340 inherit_default_permissions = v.StringBoolean(if_missing=False)
342 341
343 342 create_repo_perm = v.StringBoolean(if_missing=False)
344 343 create_user_group_perm = v.StringBoolean(if_missing=False)
345 344 #create_repo_group_perm Impl. later
346 345
347 346 fork_repo_perm = v.StringBoolean(if_missing=False)
348 347
349 348 return _CustomDefaultPermissionsForm
350 349
351 350
352 351 def DefaultsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
353 352 class _DefaultsForm(formencode.Schema):
354 353 allow_extra_fields = True
355 354 filter_extra_fields = True
356 355 default_repo_type = v.OneOf(supported_backends)
357 356 default_repo_private = v.StringBoolean(if_missing=False)
358 357 default_repo_enable_statistics = v.StringBoolean(if_missing=False)
359 358 default_repo_enable_downloads = v.StringBoolean(if_missing=False)
360 359 default_repo_enable_locking = v.StringBoolean(if_missing=False)
361 360
362 361 return _DefaultsForm
363 362
364 363
365 364 def LdapSettingsForm(tls_reqcert_choices, search_scope_choices,
366 365 tls_kind_choices):
367 366 class _LdapSettingsForm(formencode.Schema):
368 367 allow_extra_fields = True
369 368 filter_extra_fields = True
370 369 #pre_validators = [LdapLibValidator]
371 370 ldap_active = v.StringBoolean(if_missing=False)
372 371 ldap_host = v.UnicodeString(strip=True,)
373 372 ldap_port = v.Number(strip=True,)
374 373 ldap_tls_kind = v.OneOf(tls_kind_choices)
375 374 ldap_tls_reqcert = v.OneOf(tls_reqcert_choices)
376 375 ldap_dn_user = v.UnicodeString(strip=True,)
377 376 ldap_dn_pass = v.UnicodeString(strip=True,)
378 377 ldap_base_dn = v.UnicodeString(strip=True,)
379 378 ldap_filter = v.UnicodeString(strip=True,)
380 379 ldap_search_scope = v.OneOf(search_scope_choices)
381 380 ldap_attr_login = v.AttrLoginValidator()(not_empty=True)
382 381 ldap_attr_firstname = v.UnicodeString(strip=True,)
383 382 ldap_attr_lastname = v.UnicodeString(strip=True,)
384 383 ldap_attr_email = v.UnicodeString(strip=True,)
385 384
386 385 return _LdapSettingsForm
387 386
388 387
389 388 def UserExtraEmailForm():
390 389 class _UserExtraEmailForm(formencode.Schema):
391 390 email = All(v.UniqSystemEmail(), v.Email(not_empty=True))
392 391 return _UserExtraEmailForm
393 392
394 393
395 394 def UserExtraIpForm():
396 395 class _UserExtraIpForm(formencode.Schema):
397 396 ip = v.ValidIp()(not_empty=True)
398 397 return _UserExtraIpForm
399 398
400 399
401 400 def PullRequestForm(repo_id):
402 401 class _PullRequestForm(formencode.Schema):
403 402 allow_extra_fields = True
404 403 filter_extra_fields = True
405 404
406 405 user = v.UnicodeString(strip=True, required=True)
407 406 org_repo = v.UnicodeString(strip=True, required=True)
408 407 org_ref = v.UnicodeString(strip=True, required=True)
409 408 other_repo = v.UnicodeString(strip=True, required=True)
410 409 other_ref = v.UnicodeString(strip=True, required=True)
411 410 revisions = All(#v.NotReviewedRevisions(repo_id)(),
412 411 v.UniqueList(not_empty=True))
413 412 review_members = v.UniqueList(not_empty=True)
414 413
415 414 pullrequest_title = v.UnicodeString(strip=True, required=True, min=3)
416 415 pullrequest_desc = v.UnicodeString(strip=True, required=False)
417 416
418 417 ancestor_rev = v.UnicodeString(strip=True, required=True)
419 418 merge_rev = v.UnicodeString(strip=True, required=True)
420 419
421 420 return _PullRequestForm
@@ -1,347 +1,341 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.html"/>
3 3
4 4 <%def name="title()">
5 5 ${_('Settings administration')} &middot; ${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 ${_('Settings')}
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 <!-- end box / title -->
25 25
26 26 <h3>${_('Remap and rescan repositories')}</h3>
27 27 ${h.form(url('admin_setting', setting_id='mapping'),method='put')}
28 28 <div class="form">
29 29 <!-- fields -->
30 30
31 31 <div class="fields">
32 32 <div class="field">
33 33 <div class="label label-checkbox">
34 34 <label for="destroy">${_('Rescan option')}:</label>
35 35 </div>
36 36 <div class="checkboxes">
37 37 <div class="checkbox">
38 38 ${h.checkbox('destroy',True)}
39 39 <label for="destroy">
40 40 <span class="tooltip" title="${h.tooltip(_('In case a repository was deleted from filesystem and there are leftovers in the database check this option to scan obsolete data in database and remove it.'))}">
41 41 ${_('Destroy old data')}</span> </label>
42 42 </div>
43 43 <span class="help-block">${_('Rescan repositories location for new repositories. Also deletes obsolete if `destroy` flag is checked ')}</span>
44 44 </div>
45 45 </div>
46 46
47 47 <div class="buttons">
48 48 ${h.submit('rescan',_('Rescan repositories'),class_="ui-btn large")}
49 49 </div>
50 50 </div>
51 51 </div>
52 52 ${h.end_form()}
53 53
54 54 <h3>${_('Whoosh indexing')}</h3>
55 55 ${h.form(url('admin_setting', setting_id='whoosh'),method='put')}
56 56 <div class="form">
57 57 <!-- fields -->
58 58
59 59 <div class="fields">
60 60 <div class="field">
61 61 <div class="label label-checkbox">
62 62 <label>${_('Index build option')}:</label>
63 63 </div>
64 64 <div class="checkboxes">
65 65 <div class="checkbox">
66 66 ${h.checkbox('full_index',True)}
67 67 <label for="full_index">${_('Build from scratch')}</label>
68 68 </div>
69 69 </div>
70 70 </div>
71 71
72 72 <div class="buttons">
73 73 ${h.submit('reindex',_('Reindex'),class_="ui-btn large")}
74 74 </div>
75 75 </div>
76 76 </div>
77 77 ${h.end_form()}
78 78
79 79 <h3>${_('Global application settings')}</h3>
80 80 ${h.form(url('admin_setting', setting_id='global'),method='put')}
81 81 <div class="form">
82 82 <!-- fields -->
83 83
84 84 <div class="fields">
85 85
86 86 <div class="field">
87 87 <div class="label">
88 88 <label for="rhodecode_title">${_('Site branding')}:</label>
89 89 </div>
90 90 <div class="input">
91 91 ${h.text('rhodecode_title',size=30)}
92 92 </div>
93 93 </div>
94 94
95 95 <div class="field">
96 96 <div class="label">
97 97 <label for="rhodecode_realm">${_('HTTP authentication realm')}:</label>
98 98 </div>
99 99 <div class="input">
100 100 ${h.text('rhodecode_realm',size=30)}
101 101 </div>
102 102 </div>
103 103
104 104 <div class="field">
105 105 <div class="label">
106 106 <label for="rhodecode_ga_code">${_('Google Analytics code')}:</label>
107 107 </div>
108 108 <div class="input">
109 109 ${h.text('rhodecode_ga_code',size=30)}
110 110 </div>
111 111 </div>
112 112
113 113 <div class="buttons">
114 114 ${h.submit('save',_('Save settings'),class_="ui-btn large")}
115 115 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
116 116 </div>
117 117 </div>
118 118 </div>
119 119 ${h.end_form()}
120 120
121 121 <h3>${_('Visualisation settings')}</h3>
122 122 ${h.form(url('admin_setting', setting_id='visual'),method='put')}
123 123 <div class="form">
124 124 <!-- fields -->
125 125
126 126 <div class="fields">
127 127 <div class="field">
128 128 <div class="label label-checkbox">
129 129 <label>${_('General')}:</label>
130 130 </div>
131 131 <div class="checkboxes">
132 132 <div class="checkbox">
133 ${h.checkbox('rhodecode_lightweight_dashboard','True')}
134 <label for="rhodecode_lightweight_dashboard">${_('Use lightweight dashboard')}</label>
135 </div>
136 </div>
137 <div class="checkboxes">
138 <div class="checkbox">
139 133 ${h.checkbox('rhodecode_repository_fields','True')}
140 134 <label for="rhodecode_repository_fields">${_('Use repository extra fields')}</label>
141 135 </div>
142 136 </div>
143 137 </div>
144 138
145 139 <div class="field">
146 140 <div class="label label-checkbox">
147 141 <label>${_('Icons')}:</label>
148 142 </div>
149 143 <div class="checkboxes">
150 144 <div class="checkbox">
151 145 ${h.checkbox('rhodecode_show_public_icon','True')}
152 146 <label for="rhodecode_show_public_icon">${_('Show public repo icon on repositories')}</label>
153 147 </div>
154 148 <div class="checkbox">
155 149 ${h.checkbox('rhodecode_show_private_icon','True')}
156 150 <label for="rhodecode_show_private_icon">${_('Show private repo icon on repositories')}</label>
157 151 </div>
158 152 </div>
159 153 </div>
160 154
161 155 <div class="field">
162 156 <div class="label label-checkbox">
163 157 <label>${_('Meta-Tagging')}:</label>
164 158 </div>
165 159 <div class="checkboxes">
166 160 <div class="checkbox">
167 161 ${h.checkbox('rhodecode_stylify_metatags','True')}
168 162 <label for="rhodecode_stylify_metatags">${_('Stylify recognised metatags:')}</label>
169 163 </div>
170 164 <div style="padding-left: 20px;">
171 165 <ul> <!-- Fix style here -->
172 166 <li>[featured] <span class="metatag" tag="featured">featured</span></li>
173 167 <li>[stale] <span class="metatag" tag="stale">stale</span></li>
174 168 <li>[dead] <span class="metatag" tag="dead">dead</span></li>
175 169 <li>[lang =&gt; lang] <span class="metatag" tag="lang" >lang</span></li>
176 170 <li>[license =&gt; License] <span class="metatag" tag="license"><a href="http://www.opensource.org/licenses/License" >License</a></span></li>
177 171 <li>[requires =&gt; Repo] <span class="metatag" tag="requires" >requires =&gt; <a href="#" >Repo</a></span></li>
178 172 <li>[recommends =&gt; Repo] <span class="metatag" tag="recommends" >recommends =&gt; <a href="#" >Repo</a></span></li>
179 173 <li>[see =&gt; URI] <span class="metatag" tag="see">see =&gt; <a href="#">URI</a> </span></li>
180 174 </ul>
181 175 </div>
182 176 </div>
183 177 </div>
184 178
185 179 <div class="buttons">
186 180 ${h.submit('save',_('Save settings'),class_="ui-btn large")}
187 181 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
188 182 </div>
189 183
190 184 </div>
191 185 </div>
192 186 ${h.end_form()}
193 187
194 188
195 189 <h3>${_('VCS settings')}</h3>
196 190 ${h.form(url('admin_setting', setting_id='vcs'),method='put')}
197 191 <div class="form">
198 192 <!-- fields -->
199 193
200 194 <div class="fields">
201 195
202 196 <div class="field">
203 197 <div class="label label-checkbox">
204 198 <label>${_('Web')}:</label>
205 199 </div>
206 200 <div class="checkboxes">
207 201 <div class="checkbox">
208 202 ${h.checkbox('web_push_ssl', 'True')}
209 203 <label for="web_push_ssl">${_('Require SSL for vcs operations')}</label>
210 204 </div>
211 205 <span class="help-block">${_('RhodeCode will require SSL for pushing or pulling. If SSL is missing it will return HTTP Error 406: Not Acceptable')}</span>
212 206 </div>
213 207 </div>
214 208
215 209 <div class="field">
216 210 <div class="label label-checkbox">
217 211 <label>${_('Hooks')}:</label>
218 212 </div>
219 213 <div class="checkboxes">
220 214 <div class="checkbox">
221 215 ${h.checkbox('hooks_changegroup_update','True')}
222 216 <label for="hooks_changegroup_update">${_('Update repository after push (hg update)')}</label>
223 217 </div>
224 218 <div class="checkbox">
225 219 ${h.checkbox('hooks_changegroup_repo_size','True')}
226 220 <label for="hooks_changegroup_repo_size">${_('Show repository size after push')}</label>
227 221 </div>
228 222 <div class="checkbox">
229 223 ${h.checkbox('hooks_changegroup_push_logger','True')}
230 224 <label for="hooks_changegroup_push_logger">${_('Log user push commands')}</label>
231 225 </div>
232 226 <div class="checkbox">
233 227 ${h.checkbox('hooks_outgoing_pull_logger','True')}
234 228 <label for="hooks_outgoing_pull_logger">${_('Log user pull commands')}</label>
235 229 </div>
236 230 </div>
237 231 <div class="input" style="margin-top:10px">
238 232 ${h.link_to(_('Advanced setup'),url('admin_edit_setting',setting_id='hooks'))}
239 233 </div>
240 234 </div>
241 235 <div class="field">
242 236 <div class="label label-checkbox">
243 237 <label>${_('Mercurial Extensions')}:</label>
244 238 </div>
245 239 <div class="checkboxes">
246 240 <div class="checkbox">
247 241 ${h.checkbox('extensions_largefiles','True')}
248 242 <label for="extensions_largefiles">${_('Enable largefiles extension')}</label>
249 243 </div>
250 244 <div class="checkbox">
251 245 ${h.checkbox('extensions_hgsubversion','True')}
252 246 <label for="extensions_hgsubversion">${_('Enable hgsubversion extension')}</label>
253 247 </div>
254 248 <span class="help-block">${_('Requires hgsubversion library installed. Allows cloning from svn remote locations')}</span>
255 249 ##<div class="checkbox">
256 250 ## ${h.checkbox('extensions_hggit','True')}
257 251 ## <label for="extensions_hggit">${_('Enable hg-git extension')}</label>
258 252 ##</div>
259 253 ##<span class="help-block">${_('Requires hg-git library installed. Allows cloning from git remote locations')}</span>
260 254 </div>
261 255 </div>
262 256 <div class="field">
263 257 <div class="label">
264 258 <label for="paths_root_path">${_('Repositories location')}:</label>
265 259 </div>
266 260 <div class="input">
267 261 ${h.text('paths_root_path',size=30,readonly="readonly")}
268 262 <span id="path_unlock" class="tooltip"
269 263 title="${h.tooltip(_('This a crucial application setting. If you are really sure you need to change this, you must restart application in order to make this setting take effect. Click this label to unlock.'))}">
270 264 ${_('Unlock')}
271 265 </span>
272 266 <span class="help-block">${_('Location where repositories are stored. After changing this value a restart, and rescan is required')}</span>
273 267 </div>
274 268 </div>
275 269
276 270 <div class="buttons">
277 271 ${h.submit('save',_('Save settings'),class_="ui-btn large")}
278 272 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
279 273 </div>
280 274 </div>
281 275 </div>
282 276 ${h.end_form()}
283 277
284 278 <script type="text/javascript">
285 279 YAHOO.util.Event.onDOMReady(function(){
286 280 YAHOO.util.Event.addListener('path_unlock','click',function(){
287 281 YAHOO.util.Dom.get('paths_root_path').removeAttribute('readonly');
288 282 });
289 283 });
290 284 </script>
291 285
292 286 <h3>${_('Test Email')}</h3>
293 287 ${h.form(url('admin_setting', setting_id='email'),method='put')}
294 288 <div class="form">
295 289 <!-- fields -->
296 290
297 291 <div class="fields">
298 292 <div class="field">
299 293 <div class="label">
300 294 <label for="test_email">${_('Email to')}:</label>
301 295 </div>
302 296 <div class="input">
303 297 ${h.text('test_email',size=30)}
304 298 </div>
305 299 </div>
306 300
307 301 <div class="buttons">
308 302 ${h.submit('send',_('Send'),class_="ui-btn large")}
309 303 </div>
310 304 </div>
311 305 </div>
312 306 ${h.end_form()}
313 307
314 308 <h3>${_('System Info and Packages')}</h3>
315 309 <div class="form">
316 310 <div>
317 311 <h5 id="expand_modules" style="cursor: pointer">&darr; ${_('Show')} &darr;</h5>
318 312 </div>
319 313 <div id="expand_modules_table" style="display:none">
320 314 <h5>Python - ${c.py_version}</h5>
321 315 <h5>System - ${c.platform}</h5>
322 316
323 317 <table class="table" style="margin:0px 0px 0px 20px">
324 318 <colgroup>
325 319 <col style="width:220px">
326 320 </colgroup>
327 321 <tbody>
328 322 %for key, value in c.modules:
329 323 <tr>
330 324 <th style="text-align: right;padding-right:5px;">${key}</th>
331 325 <td>${value}</td>
332 326 </tr>
333 327 %endfor
334 328 </tbody>
335 329 </table>
336 330 </div>
337 331 </div>
338 332
339 333 <script type="text/javascript">
340 334 YUE.on('expand_modules','click',function(e){
341 335 YUD.setStyle('expand_modules_table','display','');
342 336 YUD.setStyle('expand_modules','display','none');
343 337 })
344 338 </script>
345 339
346 340 </div>
347 341 </%def>
@@ -1,339 +1,191 b''
1 1 <%page args="parent" />
2 2 <div class="box">
3 3 <!-- box / title -->
4 4 <div class="title">
5 5 <h5>
6 6 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/> ${parent.breadcrumbs()} <span id="repo_count">0</span> ${_('repositories')}
7 7 </h5>
8 8 %if c.rhodecode_user.username != 'default':
9 9 <ul class="links">
10 10 %if h.HasPermissionAny('hg.admin','hg.create.repository')() or h.HasReposGroupPermissionAny('group.write', 'group.admin')(c.group.group_name if c.group else None):
11 11 <li>
12 12 %if c.group:
13 13 <span>${h.link_to(_('Add repository'),h.url('new_repo',parent_group=c.group.group_id))}</span>
14 14 %if h.HasPermissionAny('hg.admin')() or h.HasReposGroupPermissionAny('group.admin')(c.group.group_name):
15 15 <span>${h.link_to(_(u'Add group'),h.url('new_repos_group', parent_group=c.group.group_id))}</span>
16 16 %endif
17 17 %else:
18 18 <span>${h.link_to(_('Add repository'),h.url('new_repo'))}</span>
19 19 %if h.HasPermissionAny('hg.admin')():
20 20 <span>${h.link_to(_(u'Add group'),h.url('new_repos_group'))}</span>
21 21 %endif
22 22 %endif
23 23 </li>
24 24 %endif
25 25 %if c.group and h.HasReposGroupPermissionAny('group.admin')(c.group.group_name):
26 26 <li>
27 27 <span>${h.link_to(_('Edit group'),h.url('edit_repos_group',group_name=c.group.group_name), title=_('You have admin right to this group, and can edit it'))}</span>
28 28 </li>
29 29 %endif
30 30 </ul>
31 31 %endif
32 32 </div>
33 33 <!-- end box / title -->
34 34 <div class="table">
35 35 % if c.groups:
36 36 <div id='groups_list_wrap' class="yui-skin-sam">
37 37 <table id="groups_list">
38 38 <thead>
39 39 <tr>
40 40 <th class="left"><a href="#">${_('Group name')}</a></th>
41 41 <th class="left"><a href="#">${_('Description')}</a></th>
42 42 ##<th class="left"><a href="#">${_('Number of repositories')}</a></th>
43 43 </tr>
44 44 </thead>
45 45
46 46 ## REPO GROUPS
47 47 % for gr in c.groups:
48 48 <tr>
49 49 <td>
50 50 <div style="white-space: nowrap">
51 51 <img class="icon" alt="${_('Repository group')}" src="${h.url('/images/icons/database_link.png')}"/>
52 52 ${h.link_to(gr.name,url('repos_group_home',group_name=gr.group_name))}
53 53 </div>
54 54 </td>
55 55 %if c.visual.stylify_metatags:
56 56 <td>${h.urlify_text(h.desc_stylize(gr.group_description))}</td>
57 57 %else:
58 58 <td>${gr.group_description}</td>
59 59 %endif
60 60 ## this is commented out since for multi nested repos can be HEAVY!
61 61 ## in number of executed queries during traversing uncomment at will
62 62 ##<td><b>${gr.repositories_recursive_count}</b></td>
63 63 </tr>
64 64 % endfor
65 65 </table>
66 66 </div>
67 67 <div id="group-user-paginator" style="padding: 0px 0px 0px 0px"></div>
68 68 <div style="height: 20px"></div>
69 69 % endif
70 70 <div id="welcome" style="display:none;text-align:center">
71 71 <h1><a href="${h.url('home')}">${c.rhodecode_name} ${c.rhodecode_version}</a></h1>
72 72 </div>
73 <%cnt=0%>
74 <%namespace name="dt" file="/data_table/_dt_elements.html"/>
75 % if not c.visual.lightweight_dashboard:
76 ## old full detailed version
77 <div id='repos_list_wrap' class="yui-skin-sam">
78 <table id="repos_list">
79 <thead>
80 <tr>
81 <th class="left"></th>
82 <th class="left">${_('Name')}</th>
83 <th class="left">${_('Description')}</th>
84 <th class="left">${_('Last change')}</th>
85 <th class="left">${_('Tip')}</th>
86 <th class="left">${_('Owner')}</th>
87 <th class="left">${_('Atom')}</th>
88 </tr>
89 </thead>
90 <tbody>
91 %for cnt,repo in enumerate(c.repos_list):
92 <tr class="parity${(cnt+1)%2}">
93 ##QUICK MENU
94 <td class="quick_repo_menu">
95 ${dt.quick_menu(repo['name'])}
96 </td>
97 ##REPO NAME AND ICONS
98 <td class="reponame">
99 ${dt.repo_name(repo['name'],repo['dbrepo']['repo_type'],repo['dbrepo']['private'],h.AttributeDict(repo['dbrepo_fork']),pageargs.get('short_repo_names'))}
100 </td>
101 ##DESCRIPTION
102 <td><span class="tooltip" title="${h.tooltip(repo['description'])}">
103 %if c.visual.stylify_metatags:
104 ${h.urlify_text(h.desc_stylize(h.truncate(repo['description'],60)))}</span>
105 %else:
106 ${h.truncate(repo['description'],60)}</span>
107 %endif
108 </td>
109 ##LAST CHANGE DATE
110 <td>
111 ${dt.last_change(repo['last_change'])}
112 </td>
113 ##LAST REVISION
114 <td>
115 ${dt.revision(repo['name'],repo['rev'],repo['tip'],repo['author'],repo['last_msg'])}
116 </td>
117 ##
118 <td title="${repo['contact']}">${h.person(repo['contact'])}</td>
119 <td>
120 ${dt.atom(repo['name'])}
121 </td>
122 </tr>
123 %endfor
124 </tbody>
125 </table>
126 </div>
127 % else:
128 ## lightweight version
129 <div class="yui-skin-sam" id="repos_list_wrap"></div>
130 <div id="user-paginator" style="padding: 0px 0px 0px 0px"></div>
131 % endif
73 <%cnt=0%>
74 <%namespace name="dt" file="/data_table/_dt_elements.html"/>
75 <div class="yui-skin-sam" id="repos_list_wrap"></div>
76 <div id="user-paginator" style="padding: 0px 0px 0px 0px"></div>
132 77 </div>
133 78 </div>
134 % if not c.visual.lightweight_dashboard:
135 <script>
136 YUD.get('repo_count').innerHTML = ${cnt+1 if cnt else 0};
137 79
138 // groups table sorting
139 var myColumnDefs = [
140 {key:"name",label:"${_('Group name')}",sortable:true,
141 sortOptions: { sortFunction: groupNameSort }},
142 {key:"desc",label:"${_('Description')}",sortable:true},
143 ];
144
145 var myDataSource = new YAHOO.util.DataSource(YUD.get("groups_list"));
146
147 myDataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
148 myDataSource.responseSchema = {
149 fields: [
150 {key:"name"},
151 {key:"desc"},
152 ]
153 };
154
155 var myDataTable = new YAHOO.widget.DataTable("groups_list_wrap", myColumnDefs, myDataSource,{
156 sortedBy:{key:"name",dir:"asc"},
157 paginator: new YAHOO.widget.Paginator({
158 rowsPerPage: 50,
159 alwaysVisible: false,
160 template : "{PreviousPageLink} {FirstPageLink} {PageLinks} {LastPageLink} {NextPageLink}",
161 pageLinks: 5,
162 containerClass: 'pagination-wh',
163 currentPageClass: 'pager_curpage',
164 pageLinkClass: 'pager_link',
165 nextPageLinkLabel: '&gt;',
166 previousPageLinkLabel: '&lt;',
167 firstPageLinkLabel: '&lt;&lt;',
168 lastPageLinkLabel: '&gt;&gt;',
169 containers:['group-user-paginator']
170 }),
171 MSG_SORTASC:"${_('Click to sort ascending')}",
172 MSG_SORTDESC:"${_('Click to sort descending')}"
173 });
174
175 // main table sorting
176 var myColumnDefs = [
177 {key:"menu",label:"",sortable:false,className:"quick_repo_menu hidden"},
178 {key:"name",label:"${_('Name')}",sortable:true,
179 sortOptions: { sortFunction: nameSort }},
180 {key:"desc",label:"${_('Description')}",sortable:true},
181 {key:"last_change",label:"${_('Last Change')}",sortable:true,
182 sortOptions: { sortFunction: ageSort }},
183 {key:"tip",label:"${_('Tip')}",sortable:true,
184 sortOptions: { sortFunction: revisionSort }},
185 {key:"owner",label:"${_('Owner')}",sortable:true},
186 {key:"atom",label:"",sortable:false},
187 ];
188
189 var myDataSource = new YAHOO.util.DataSource(YUD.get("repos_list"));
190
191 myDataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
192
193 myDataSource.responseSchema = {
194 fields: [
195 {key:"menu"},
196 //{key:"raw_name"},
197 {key:"name"},
198 {key:"desc"},
199 {key:"last_change"},
200 {key:"tip"},
201 {key:"owner"},
202 {key:"atom"},
203 ]
204 };
205
206 var myDataTable = new YAHOO.widget.DataTable("repos_list_wrap", myColumnDefs, myDataSource,
207 {
208 sortedBy:{key:"name",dir:"asc"},
209 MSG_SORTASC:"${_('Click to sort ascending')}",
210 MSG_SORTDESC:"${_('Click to sort descending')}",
211 MSG_EMPTY:"${_('No records found.')}",
212 MSG_ERROR:"${_('Data error.')}",
213 MSG_LOADING:"${_('Loading...')}",
214 }
215 );
216 myDataTable.subscribe('postRenderEvent',function(oArgs) {
217 tooltip_activate();
218 quick_repo_menu();
219 var func = function(node){
220 return node.parentNode.parentNode.parentNode.parentNode;
221 }
222 q_filter('q_filter',YUQ('div.table tr td a.repo_name'),func);
223 });
224
225 </script>
226 % else:
227 80 <script>
228 81 var data = ${c.data|n};
229 82 var myDataSource = new YAHOO.util.DataSource(data);
230 83 myDataSource.responseType = YAHOO.util.DataSource.TYPE_JSON;
231 84
232 85 myDataSource.responseSchema = {
233 86 resultsList: "records",
234 87 fields: [
235 88 {key:"menu"},
236 89 {key:"raw_name"},
237 90 {key:"name"},
238 91 {key:"desc"},
239 92 {key:"last_change"},
240 93 {key:"last_changeset"},
241 94 {key:"owner"},
242 95 {key:"atom"},
243 96 ]
244 97 };
245 98 myDataSource.doBeforeCallback = function(req,raw,res,cb) {
246 99 // This is the filter function
247 100 var data = res.results || [],
248 101 filtered = [],
249 102 i,l;
250 103
251 104 if (req) {
252 105 req = req.toLowerCase();
253 106 for (i = 0; i<data.length; i++) {
254 107 var pos = data[i].raw_name.toLowerCase().indexOf(req)
255 108 if (pos != -1) {
256 109 filtered.push(data[i]);
257 110 }
258 111 }
259 112 res.results = filtered;
260 113 }
261 114 YUD.get('repo_count').innerHTML = res.results.length;
262 115 return res;
263 116 }
264 117
265 118 // main table sorting
266 119 var myColumnDefs = [
267 120 {key:"menu",label:"",sortable:false,className:"quick_repo_menu hidden"},
268 121 {key:"name",label:"${_('Name')}",sortable:true,
269 122 sortOptions: { sortFunction: nameSort }},
270 123 {key:"desc",label:"${_('Description')}",sortable:true},
271 124 {key:"last_change",label:"${_('Last Change')}",sortable:true,
272 125 sortOptions: { sortFunction: ageSort }},
273 126 {key:"last_changeset",label:"${_('Tip')}",sortable:true,
274 127 sortOptions: { sortFunction: revisionSort }},
275 128 {key:"owner",label:"${_('Owner')}",sortable:true},
276 129 {key:"atom",label:"",sortable:false},
277 130 ];
278 131
279 132 var myDataTable = new YAHOO.widget.DataTable("repos_list_wrap", myColumnDefs, myDataSource,{
280 133 sortedBy:{key:"name",dir:"asc"},
281 134 paginator: new YAHOO.widget.Paginator({
282 rowsPerPage: ${c.visual.lightweight_dashboard_items},
135 rowsPerPage: ${c.visual.dashboard_items},
283 136 alwaysVisible: false,
284 137 template : "{PreviousPageLink} {FirstPageLink} {PageLinks} {LastPageLink} {NextPageLink}",
285 138 pageLinks: 5,
286 139 containerClass: 'pagination-wh',
287 140 currentPageClass: 'pager_curpage',
288 141 pageLinkClass: 'pager_link',
289 142 nextPageLinkLabel: '&gt;',
290 143 previousPageLinkLabel: '&lt;',
291 144 firstPageLinkLabel: '&lt;&lt;',
292 145 lastPageLinkLabel: '&gt;&gt;',
293 146 containers:['user-paginator']
294 147 }),
295 148
296 149 MSG_SORTASC:"${_('Click to sort ascending')}",
297 150 MSG_SORTDESC:"${_('Click to sort descending')}",
298 151 MSG_EMPTY:"${_('No repositories found.')}",
299 152 MSG_ERROR:"${_('Data error.')}",
300 153 MSG_LOADING:"${_('Loading...')}",
301 154 }
302 155 );
303 156 myDataTable.subscribe('postRenderEvent',function(oArgs) {
304 157 tooltip_activate();
305 158 quick_repo_menu();
306 159 });
307 160
308 161 var filterTimeout = null;
309 162
310 163 updateFilter = function () {
311 164 // Reset timeout
312 165 filterTimeout = null;
313 166
314 167 // Reset sort
315 168 var state = myDataTable.getState();
316 169 state.sortedBy = {key:'name', dir:YAHOO.widget.DataTable.CLASS_ASC};
317 170
318 171 // Get filtered data
319 172 myDataSource.sendRequest(YUD.get('q_filter').value,{
320 173 success : myDataTable.onDataReturnInitializeTable,
321 174 failure : myDataTable.onDataReturnInitializeTable,
322 175 scope : myDataTable,
323 176 argument: state
324 177 });
325 178
326 179 };
327 180 YUE.on('q_filter','click',function(){
328 181 if(!YUD.hasClass('q_filter', 'loaded')){
329 182 //TODO: load here full list later to do search within groups
330 183 YUD.addClass('q_filter', 'loaded');
331 184 }
332 185 });
333 186
334 187 YUE.on('q_filter','keyup',function (e) {
335 188 clearTimeout(filterTimeout);
336 189 filterTimeout = setTimeout(updateFilter,600);
337 190 });
338 191 </script>
339 % endif
@@ -1,111 +1,83 b''
1 1 import time
2 2 from rhodecode.tests import *
3 3 from rhodecode.tests.fixture import Fixture
4 4 from rhodecode.model.meta import Session
5 5 from rhodecode.model.db import User, Repository
6 6 from rhodecode.model.repo import RepoModel
7 7 from rhodecode.model.repos_group import ReposGroupModel
8 8
9 9
10 10 fixture = Fixture()
11 11
12 12
13 13 class TestHomeController(TestController):
14 14
15 15 def test_index(self):
16 16 self.log_user()
17 17 response = self.app.get(url(controller='home', action='index'))
18 18 #if global permission is set
19 19 response.mustcontain('Add repository')
20 response.mustcontain('href="/%s"' % HG_REPO)
20 # html in javascript variable:
21 response.mustcontain("""var data = {"totalRecords": %s"""
22 % len(Repository.getAll()))
23 response.mustcontain(r'href=\"/%s\"' % HG_REPO)
21 24
22 response.mustcontain("""<img class="icon" title="Mercurial repository" """
23 """alt="Mercurial repository" src="/images/icons/hg"""
24 """icon.png"/>""")
25 response.mustcontain("""<img class="icon" title="Public repository" """
26 """alt="Public repository" src="/images/icons/lock_"""
27 """open.png"/>""")
25 response.mustcontain(r"""<img class=\"icon\" title=\"Mercurial repository\" """
26 r"""alt=\"Mercurial repository\" src=\"/images/icons/hg"""
27 r"""icon.png\"/>""")
28 response.mustcontain(r"""<img class=\"icon\" title=\"Public repository\" """
29 r"""alt=\"Public repository\" src=\"/images/icons/lock_"""
30 r"""open.png\"/>""")
28 31
29 32 response.mustcontain(
30 """<a title="Marcin Kuzminski &amp;lt;marcin@python-works.com&amp;gt;:\n
31 merge" class="tooltip" href="/vcs_test_hg/changeset/27cd5cce30c96924232"""
32 """dffcd24178a07ffeb5dfc">r173:27cd5cce30c9</a>"""
33 r"""<a title=\"Marcin Kuzminski &amp;lt;marcin@python-works.com&amp;gt;:\n\n"""
34 r"""merge\" class=\"tooltip\" href=\"/vcs_test_hg/changeset/27cd5cce30c96924232"""
35 r"""dffcd24178a07ffeb5dfc\">r173:27cd5cce30c9</a>"""
33 36 )
34 37
35 38 def test_repo_summary_with_anonymous_access_disabled(self):
36 39 anon = User.get_default_user()
37 40 anon.active = False
38 41 Session().add(anon)
39 42 Session().commit()
40 43 time.sleep(1.5) # must sleep for cache (1s to expire)
41 44 try:
42 45 response = self.app.get(url(controller='summary',
43 46 action='index', repo_name=HG_REPO),
44 47 status=302)
45 48 assert 'login' in response.location
46 49
47 50 finally:
48 51 anon = User.get_default_user()
49 52 anon.active = True
50 53 Session().add(anon)
51 54 Session().commit()
52 55
53 56 def test_index_with_anonymous_access_disabled(self):
54 57 anon = User.get_default_user()
55 58 anon.active = False
56 59 Session().add(anon)
57 60 Session().commit()
58 61 time.sleep(1.5) # must sleep for cache (1s to expire)
59 62 try:
60 63 response = self.app.get(url(controller='home', action='index'),
61 64 status=302)
62 65 assert 'login' in response.location
63 66 finally:
64 67 anon = User.get_default_user()
65 68 anon.active = True
66 69 Session().add(anon)
67 70 Session().commit()
68 71
69 def _set_l_dash(self, set_to):
70 self.app.post(url('admin_setting', setting_id='visual'),
71 params=dict(_method='put',
72 rhodecode_lightweight_dashboard=set_to,))
73
74 def test_index_with_lightweight_dashboard(self):
75 self.log_user()
76 self._set_l_dash(True)
77
78 try:
79 response = self.app.get(url(controller='home', action='index'))
80 response.mustcontain("""var data = {"totalRecords": %s"""
81 % len(Repository.getAll()))
82 finally:
83 self._set_l_dash(False)
84
85 72 def test_index_page_on_groups(self):
86 73 self.log_user()
87 74 gr = fixture.create_group('gr1')
88 75 fixture.create_repo(name='gr1/repo_in_group', repos_group=gr)
89 76 response = self.app.get(url('repos_group_home', group_name='gr1'))
90 77
91 78 try:
92 79 response.mustcontain("gr1/repo_in_group")
93 80 finally:
94 81 RepoModel().delete('gr1/repo_in_group')
95 82 ReposGroupModel().delete(repos_group='gr1', force_delete=True)
96 83 Session().commit()
97
98 def test_index_page_on_groups_with_lightweight_dashboard(self):
99 self.log_user()
100 self._set_l_dash(True)
101 fixture.create_repo(name='gr1/repo_in_group',
102 repos_group=fixture.create_group('gr1'))
103 response = self.app.get(url('repos_group_home', group_name='gr1'))
104
105 try:
106 response.mustcontain("""gr1/repo_in_group""")
107 finally:
108 self._set_l_dash(False)
109 RepoModel().delete('gr1/repo_in_group')
110 ReposGroupModel().delete(repos_group='gr1', force_delete=True)
111 Session().commit()
General Comments 0
You need to be logged in to leave comments. Login now