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