##// END OF EJS Templates
cleanup: imports cleanup.
marcink -
r1796:b0e7443a default
parent child Browse files
Show More
@@ -1,404 +1,404 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 """
22 """
23 Repository groups controller for RhodeCode
23 Repository groups controller for RhodeCode
24 """
24 """
25
25
26 import logging
26 import logging
27 import formencode
27 import formencode
28
28
29 from formencode import htmlfill
29 from formencode import htmlfill
30
30
31 from pylons import request, tmpl_context as c, url
31 from pylons import request, tmpl_context as c, url
32 from pylons.controllers.util import abort, redirect
32 from pylons.controllers.util import abort, redirect
33 from pylons.i18n.translation import _, ungettext
33 from pylons.i18n.translation import _, ungettext
34
34
35 from rhodecode.lib import auth
35 from rhodecode.lib import auth
36 from rhodecode.lib import helpers as h
36 from rhodecode.lib import helpers as h
37 from rhodecode.lib.ext_json import json
37 from rhodecode.lib.ext_json import json
38 from rhodecode.lib.auth import (
38 from rhodecode.lib.auth import (
39 LoginRequired, NotAnonymous, HasPermissionAll,
39 LoginRequired, NotAnonymous, HasPermissionAll,
40 HasRepoGroupPermissionAll, HasRepoGroupPermissionAnyDecorator)
40 HasRepoGroupPermissionAll, HasRepoGroupPermissionAnyDecorator)
41 from rhodecode.lib.base import BaseController, render
41 from rhodecode.lib.base import BaseController, render
42 from rhodecode.lib.utils2 import safe_int
42 from rhodecode.model.db import RepoGroup, User
43 from rhodecode.model.db import RepoGroup, User
43 from rhodecode.model.scm import RepoGroupList
44 from rhodecode.model.scm import RepoGroupList
44 from rhodecode.model.repo_group import RepoGroupModel
45 from rhodecode.model.repo_group import RepoGroupModel
45 from rhodecode.model.forms import RepoGroupForm, RepoGroupPermsForm
46 from rhodecode.model.forms import RepoGroupForm, RepoGroupPermsForm
46 from rhodecode.model.meta import Session
47 from rhodecode.model.meta import Session
47 from rhodecode.lib.utils2 import safe_int
48
48
49
49
50 log = logging.getLogger(__name__)
50 log = logging.getLogger(__name__)
51
51
52
52
53 class RepoGroupsController(BaseController):
53 class RepoGroupsController(BaseController):
54 """REST Controller styled on the Atom Publishing Protocol"""
54 """REST Controller styled on the Atom Publishing Protocol"""
55
55
56 @LoginRequired()
56 @LoginRequired()
57 def __before__(self):
57 def __before__(self):
58 super(RepoGroupsController, self).__before__()
58 super(RepoGroupsController, self).__before__()
59
59
60 def __load_defaults(self, allow_empty_group=False, repo_group=None):
60 def __load_defaults(self, allow_empty_group=False, repo_group=None):
61 if self._can_create_repo_group():
61 if self._can_create_repo_group():
62 # we're global admin, we're ok and we can create TOP level groups
62 # we're global admin, we're ok and we can create TOP level groups
63 allow_empty_group = True
63 allow_empty_group = True
64
64
65 # override the choices for this form, we need to filter choices
65 # override the choices for this form, we need to filter choices
66 # and display only those we have ADMIN right
66 # and display only those we have ADMIN right
67 groups_with_admin_rights = RepoGroupList(
67 groups_with_admin_rights = RepoGroupList(
68 RepoGroup.query().all(),
68 RepoGroup.query().all(),
69 perm_set=['group.admin'])
69 perm_set=['group.admin'])
70 c.repo_groups = RepoGroup.groups_choices(
70 c.repo_groups = RepoGroup.groups_choices(
71 groups=groups_with_admin_rights,
71 groups=groups_with_admin_rights,
72 show_empty_group=allow_empty_group)
72 show_empty_group=allow_empty_group)
73
73
74 if repo_group:
74 if repo_group:
75 # exclude filtered ids
75 # exclude filtered ids
76 exclude_group_ids = [repo_group.group_id]
76 exclude_group_ids = [repo_group.group_id]
77 c.repo_groups = filter(lambda x: x[0] not in exclude_group_ids,
77 c.repo_groups = filter(lambda x: x[0] not in exclude_group_ids,
78 c.repo_groups)
78 c.repo_groups)
79 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
79 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
80 parent_group = repo_group.parent_group
80 parent_group = repo_group.parent_group
81
81
82 add_parent_group = (parent_group and (
82 add_parent_group = (parent_group and (
83 unicode(parent_group.group_id) not in c.repo_groups_choices))
83 unicode(parent_group.group_id) not in c.repo_groups_choices))
84 if add_parent_group:
84 if add_parent_group:
85 c.repo_groups_choices.append(unicode(parent_group.group_id))
85 c.repo_groups_choices.append(unicode(parent_group.group_id))
86 c.repo_groups.append(RepoGroup._generate_choice(parent_group))
86 c.repo_groups.append(RepoGroup._generate_choice(parent_group))
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 owner
98 # fill owner
99 if repo_group.user:
99 if repo_group.user:
100 data.update({'user': repo_group.user.username})
100 data.update({'user': repo_group.user.username})
101 else:
101 else:
102 replacement_user = User.get_first_super_admin().username
102 replacement_user = User.get_first_super_admin().username
103 data.update({'user': replacement_user})
103 data.update({'user': replacement_user})
104
104
105 # fill repository group users
105 # fill repository group users
106 for p in repo_group.repo_group_to_perm:
106 for p in repo_group.repo_group_to_perm:
107 data.update({
107 data.update({
108 'u_perm_%s' % p.user.user_id: p.permission.permission_name})
108 'u_perm_%s' % p.user.user_id: p.permission.permission_name})
109
109
110 # fill repository group user groups
110 # fill repository group user groups
111 for p in repo_group.users_group_to_perm:
111 for p in repo_group.users_group_to_perm:
112 data.update({
112 data.update({
113 'g_perm_%s' % p.users_group.users_group_id:
113 'g_perm_%s' % p.users_group.users_group_id:
114 p.permission.permission_name})
114 p.permission.permission_name})
115 # html and form expects -1 as empty parent group
115 # html and form expects -1 as empty parent group
116 data['group_parent_id'] = data['group_parent_id'] or -1
116 data['group_parent_id'] = data['group_parent_id'] or -1
117 return data
117 return data
118
118
119 def _revoke_perms_on_yourself(self, form_result):
119 def _revoke_perms_on_yourself(self, form_result):
120 _updates = filter(lambda u: c.rhodecode_user.user_id == int(u[0]),
120 _updates = filter(lambda u: c.rhodecode_user.user_id == int(u[0]),
121 form_result['perm_updates'])
121 form_result['perm_updates'])
122 _additions = filter(lambda u: c.rhodecode_user.user_id == int(u[0]),
122 _additions = filter(lambda u: c.rhodecode_user.user_id == int(u[0]),
123 form_result['perm_additions'])
123 form_result['perm_additions'])
124 _deletions = filter(lambda u: c.rhodecode_user.user_id == int(u[0]),
124 _deletions = filter(lambda u: c.rhodecode_user.user_id == int(u[0]),
125 form_result['perm_deletions'])
125 form_result['perm_deletions'])
126 admin_perm = 'group.admin'
126 admin_perm = 'group.admin'
127 if _updates and _updates[0][1] != admin_perm or \
127 if _updates and _updates[0][1] != admin_perm or \
128 _additions and _additions[0][1] != admin_perm or \
128 _additions and _additions[0][1] != admin_perm or \
129 _deletions and _deletions[0][1] != admin_perm:
129 _deletions and _deletions[0][1] != admin_perm:
130 return True
130 return True
131 return False
131 return False
132
132
133 def _can_create_repo_group(self, parent_group_id=None):
133 def _can_create_repo_group(self, parent_group_id=None):
134 is_admin = HasPermissionAll('hg.admin')('group create controller')
134 is_admin = HasPermissionAll('hg.admin')('group create controller')
135 create_repo_group = HasPermissionAll(
135 create_repo_group = HasPermissionAll(
136 'hg.repogroup.create.true')('group create controller')
136 'hg.repogroup.create.true')('group create controller')
137 if is_admin or (create_repo_group and not parent_group_id):
137 if is_admin or (create_repo_group and not parent_group_id):
138 # we're global admin, or we have global repo group create
138 # we're global admin, or we have global repo group create
139 # permission
139 # permission
140 # we're ok and we can create TOP level groups
140 # we're ok and we can create TOP level groups
141 return True
141 return True
142 elif parent_group_id:
142 elif parent_group_id:
143 # we check the permission if we can write to parent group
143 # we check the permission if we can write to parent group
144 group = RepoGroup.get(parent_group_id)
144 group = RepoGroup.get(parent_group_id)
145 group_name = group.group_name if group else None
145 group_name = group.group_name if group else None
146 if HasRepoGroupPermissionAll('group.admin')(
146 if HasRepoGroupPermissionAll('group.admin')(
147 group_name, 'check if user is an admin of group'):
147 group_name, 'check if user is an admin of group'):
148 # we're an admin of passed in group, we're ok.
148 # we're an admin of passed in group, we're ok.
149 return True
149 return True
150 else:
150 else:
151 return False
151 return False
152 return False
152 return False
153
153
154 @NotAnonymous()
154 @NotAnonymous()
155 def index(self):
155 def index(self):
156 """GET /repo_groups: All items in the collection"""
156 """GET /repo_groups: All items in the collection"""
157 # url('repo_groups')
157 # url('repo_groups')
158
158
159 repo_group_list = RepoGroup.get_all_repo_groups()
159 repo_group_list = RepoGroup.get_all_repo_groups()
160 _perms = ['group.admin']
160 _perms = ['group.admin']
161 repo_group_list_acl = RepoGroupList(repo_group_list, perm_set=_perms)
161 repo_group_list_acl = RepoGroupList(repo_group_list, perm_set=_perms)
162 repo_group_data = RepoGroupModel().get_repo_groups_as_dict(
162 repo_group_data = RepoGroupModel().get_repo_groups_as_dict(
163 repo_group_list=repo_group_list_acl, admin=True)
163 repo_group_list=repo_group_list_acl, admin=True)
164 c.data = json.dumps(repo_group_data)
164 c.data = json.dumps(repo_group_data)
165 return render('admin/repo_groups/repo_groups.mako')
165 return render('admin/repo_groups/repo_groups.mako')
166
166
167 # perm checks inside
167 # perm checks inside
168 @NotAnonymous()
168 @NotAnonymous()
169 @auth.CSRFRequired()
169 @auth.CSRFRequired()
170 def create(self):
170 def create(self):
171 """POST /repo_groups: Create a new item"""
171 """POST /repo_groups: Create a new item"""
172 # url('repo_groups')
172 # url('repo_groups')
173
173
174 parent_group_id = safe_int(request.POST.get('group_parent_id'))
174 parent_group_id = safe_int(request.POST.get('group_parent_id'))
175 can_create = self._can_create_repo_group(parent_group_id)
175 can_create = self._can_create_repo_group(parent_group_id)
176
176
177 self.__load_defaults()
177 self.__load_defaults()
178 # permissions for can create group based on parent_id are checked
178 # permissions for can create group based on parent_id are checked
179 # here in the Form
179 # here in the Form
180 available_groups = map(lambda k: unicode(k[0]), c.repo_groups)
180 available_groups = map(lambda k: unicode(k[0]), c.repo_groups)
181 repo_group_form = RepoGroupForm(available_groups=available_groups,
181 repo_group_form = RepoGroupForm(available_groups=available_groups,
182 can_create_in_root=can_create)()
182 can_create_in_root=can_create)()
183 try:
183 try:
184 owner = c.rhodecode_user
184 owner = c.rhodecode_user
185 form_result = repo_group_form.to_python(dict(request.POST))
185 form_result = repo_group_form.to_python(dict(request.POST))
186 RepoGroupModel().create(
186 RepoGroupModel().create(
187 group_name=form_result['group_name_full'],
187 group_name=form_result['group_name_full'],
188 group_description=form_result['group_description'],
188 group_description=form_result['group_description'],
189 owner=owner.user_id,
189 owner=owner.user_id,
190 copy_permissions=form_result['group_copy_permissions']
190 copy_permissions=form_result['group_copy_permissions']
191 )
191 )
192 Session().commit()
192 Session().commit()
193 _new_group_name = form_result['group_name_full']
193 _new_group_name = form_result['group_name_full']
194 repo_group_url = h.link_to(
194 repo_group_url = h.link_to(
195 _new_group_name,
195 _new_group_name,
196 h.route_path('repo_group_home', repo_group_name=_new_group_name))
196 h.route_path('repo_group_home', repo_group_name=_new_group_name))
197 h.flash(h.literal(_('Created repository group %s')
197 h.flash(h.literal(_('Created repository group %s')
198 % repo_group_url), category='success')
198 % repo_group_url), category='success')
199 # TODO: in future action_logger(, '', '', '', self.sa)
199 # TODO: in future action_logger(, '', '', '', self.sa)
200 except formencode.Invalid as errors:
200 except formencode.Invalid as errors:
201 return htmlfill.render(
201 return htmlfill.render(
202 render('admin/repo_groups/repo_group_add.mako'),
202 render('admin/repo_groups/repo_group_add.mako'),
203 defaults=errors.value,
203 defaults=errors.value,
204 errors=errors.error_dict or {},
204 errors=errors.error_dict or {},
205 prefix_error=False,
205 prefix_error=False,
206 encoding="UTF-8",
206 encoding="UTF-8",
207 force_defaults=False)
207 force_defaults=False)
208 except Exception:
208 except Exception:
209 log.exception("Exception during creation of repository group")
209 log.exception("Exception during creation of repository group")
210 h.flash(_('Error occurred during creation of repository group %s')
210 h.flash(_('Error occurred during creation of repository group %s')
211 % request.POST.get('group_name'), category='error')
211 % request.POST.get('group_name'), category='error')
212
212
213 # TODO: maybe we should get back to the main view, not the admin one
213 # TODO: maybe we should get back to the main view, not the admin one
214 return redirect(url('repo_groups', parent_group=parent_group_id))
214 return redirect(url('repo_groups', parent_group=parent_group_id))
215
215
216 # perm checks inside
216 # perm checks inside
217 @NotAnonymous()
217 @NotAnonymous()
218 def new(self):
218 def new(self):
219 """GET /repo_groups/new: Form to create a new item"""
219 """GET /repo_groups/new: Form to create a new item"""
220 # url('new_repo_group')
220 # url('new_repo_group')
221 # perm check for admin, create_group perm or admin of parent_group
221 # perm check for admin, create_group perm or admin of parent_group
222 parent_group_id = safe_int(request.GET.get('parent_group'))
222 parent_group_id = safe_int(request.GET.get('parent_group'))
223 if not self._can_create_repo_group(parent_group_id):
223 if not self._can_create_repo_group(parent_group_id):
224 return abort(403)
224 return abort(403)
225
225
226 self.__load_defaults()
226 self.__load_defaults()
227 return render('admin/repo_groups/repo_group_add.mako')
227 return render('admin/repo_groups/repo_group_add.mako')
228
228
229 @HasRepoGroupPermissionAnyDecorator('group.admin')
229 @HasRepoGroupPermissionAnyDecorator('group.admin')
230 @auth.CSRFRequired()
230 @auth.CSRFRequired()
231 def update(self, group_name):
231 def update(self, group_name):
232 """PUT /repo_groups/group_name: Update an existing item"""
232 """PUT /repo_groups/group_name: Update an existing item"""
233 # Forms posted to this method should contain a hidden field:
233 # Forms posted to this method should contain a hidden field:
234 # <input type="hidden" name="_method" value="PUT" />
234 # <input type="hidden" name="_method" value="PUT" />
235 # Or using helpers:
235 # Or using helpers:
236 # h.form(url('repos_group', group_name=GROUP_NAME), method='put')
236 # h.form(url('repos_group', group_name=GROUP_NAME), method='put')
237
237
238 c.repo_group = RepoGroupModel()._get_repo_group(group_name)
238 c.repo_group = RepoGroupModel()._get_repo_group(group_name)
239 can_create_in_root = self._can_create_repo_group()
239 can_create_in_root = self._can_create_repo_group()
240 show_root_location = can_create_in_root
240 show_root_location = can_create_in_root
241 if not c.repo_group.parent_group:
241 if not c.repo_group.parent_group:
242 # this group don't have a parrent so we should show empty value
242 # this group don't have a parrent so we should show empty value
243 show_root_location = True
243 show_root_location = True
244 self.__load_defaults(allow_empty_group=show_root_location,
244 self.__load_defaults(allow_empty_group=show_root_location,
245 repo_group=c.repo_group)
245 repo_group=c.repo_group)
246
246
247 repo_group_form = RepoGroupForm(
247 repo_group_form = RepoGroupForm(
248 edit=True, old_data=c.repo_group.get_dict(),
248 edit=True, old_data=c.repo_group.get_dict(),
249 available_groups=c.repo_groups_choices,
249 available_groups=c.repo_groups_choices,
250 can_create_in_root=can_create_in_root, allow_disabled=True)()
250 can_create_in_root=can_create_in_root, allow_disabled=True)()
251
251
252 try:
252 try:
253 form_result = repo_group_form.to_python(dict(request.POST))
253 form_result = repo_group_form.to_python(dict(request.POST))
254 gr_name = form_result['group_name']
254 gr_name = form_result['group_name']
255 new_gr = RepoGroupModel().update(group_name, form_result)
255 new_gr = RepoGroupModel().update(group_name, form_result)
256 Session().commit()
256 Session().commit()
257 h.flash(_('Updated repository group %s') % (gr_name,),
257 h.flash(_('Updated repository group %s') % (gr_name,),
258 category='success')
258 category='success')
259 # we now have new name !
259 # we now have new name !
260 group_name = new_gr.group_name
260 group_name = new_gr.group_name
261 # TODO: in future action_logger(, '', '', '', self.sa)
261 # TODO: in future action_logger(, '', '', '', self.sa)
262 except formencode.Invalid as errors:
262 except formencode.Invalid as errors:
263 c.active = 'settings'
263 c.active = 'settings'
264 return htmlfill.render(
264 return htmlfill.render(
265 render('admin/repo_groups/repo_group_edit.mako'),
265 render('admin/repo_groups/repo_group_edit.mako'),
266 defaults=errors.value,
266 defaults=errors.value,
267 errors=errors.error_dict or {},
267 errors=errors.error_dict or {},
268 prefix_error=False,
268 prefix_error=False,
269 encoding="UTF-8",
269 encoding="UTF-8",
270 force_defaults=False)
270 force_defaults=False)
271 except Exception:
271 except Exception:
272 log.exception("Exception during update or repository group")
272 log.exception("Exception during update or repository group")
273 h.flash(_('Error occurred during update of repository group %s')
273 h.flash(_('Error occurred during update of repository group %s')
274 % request.POST.get('group_name'), category='error')
274 % request.POST.get('group_name'), category='error')
275
275
276 return redirect(url('edit_repo_group', group_name=group_name))
276 return redirect(url('edit_repo_group', group_name=group_name))
277
277
278 @HasRepoGroupPermissionAnyDecorator('group.admin')
278 @HasRepoGroupPermissionAnyDecorator('group.admin')
279 @auth.CSRFRequired()
279 @auth.CSRFRequired()
280 def delete(self, group_name):
280 def delete(self, group_name):
281 """DELETE /repo_groups/group_name: Delete an existing item"""
281 """DELETE /repo_groups/group_name: Delete an existing item"""
282 # Forms posted to this method should contain a hidden field:
282 # Forms posted to this method should contain a hidden field:
283 # <input type="hidden" name="_method" value="DELETE" />
283 # <input type="hidden" name="_method" value="DELETE" />
284 # Or using helpers:
284 # Or using helpers:
285 # h.form(url('repos_group', group_name=GROUP_NAME), method='delete')
285 # h.form(url('repos_group', group_name=GROUP_NAME), method='delete')
286
286
287 gr = c.repo_group = RepoGroupModel()._get_repo_group(group_name)
287 gr = c.repo_group = RepoGroupModel()._get_repo_group(group_name)
288 repos = gr.repositories.all()
288 repos = gr.repositories.all()
289 if repos:
289 if repos:
290 msg = ungettext(
290 msg = ungettext(
291 'This group contains %(num)d repository and cannot be deleted',
291 'This group contains %(num)d repository and cannot be deleted',
292 'This group contains %(num)d repositories and cannot be'
292 'This group contains %(num)d repositories and cannot be'
293 ' deleted',
293 ' deleted',
294 len(repos)) % {'num': len(repos)}
294 len(repos)) % {'num': len(repos)}
295 h.flash(msg, category='warning')
295 h.flash(msg, category='warning')
296 return redirect(url('repo_groups'))
296 return redirect(url('repo_groups'))
297
297
298 children = gr.children.all()
298 children = gr.children.all()
299 if children:
299 if children:
300 msg = ungettext(
300 msg = ungettext(
301 'This group contains %(num)d subgroup and cannot be deleted',
301 'This group contains %(num)d subgroup and cannot be deleted',
302 'This group contains %(num)d subgroups and cannot be deleted',
302 'This group contains %(num)d subgroups and cannot be deleted',
303 len(children)) % {'num': len(children)}
303 len(children)) % {'num': len(children)}
304 h.flash(msg, category='warning')
304 h.flash(msg, category='warning')
305 return redirect(url('repo_groups'))
305 return redirect(url('repo_groups'))
306
306
307 try:
307 try:
308 RepoGroupModel().delete(group_name)
308 RepoGroupModel().delete(group_name)
309 Session().commit()
309 Session().commit()
310 h.flash(_('Removed repository group %s') % group_name,
310 h.flash(_('Removed repository group %s') % group_name,
311 category='success')
311 category='success')
312 # TODO: in future action_logger(, '', '', '', self.sa)
312 # TODO: in future action_logger(, '', '', '', self.sa)
313 except Exception:
313 except Exception:
314 log.exception("Exception during deletion of repository group")
314 log.exception("Exception during deletion of repository group")
315 h.flash(_('Error occurred during deletion of repository group %s')
315 h.flash(_('Error occurred during deletion of repository group %s')
316 % group_name, category='error')
316 % group_name, category='error')
317
317
318 return redirect(url('repo_groups'))
318 return redirect(url('repo_groups'))
319
319
320 @HasRepoGroupPermissionAnyDecorator('group.admin')
320 @HasRepoGroupPermissionAnyDecorator('group.admin')
321 def edit(self, group_name):
321 def edit(self, group_name):
322 """GET /repo_groups/group_name/edit: Form to edit an existing item"""
322 """GET /repo_groups/group_name/edit: Form to edit an existing item"""
323 # url('edit_repo_group', group_name=GROUP_NAME)
323 # url('edit_repo_group', group_name=GROUP_NAME)
324 c.active = 'settings'
324 c.active = 'settings'
325
325
326 c.repo_group = RepoGroupModel()._get_repo_group(group_name)
326 c.repo_group = RepoGroupModel()._get_repo_group(group_name)
327 # we can only allow moving empty group if it's already a top-level
327 # we can only allow moving empty group if it's already a top-level
328 # group, ie has no parents, or we're admin
328 # group, ie has no parents, or we're admin
329 can_create_in_root = self._can_create_repo_group()
329 can_create_in_root = self._can_create_repo_group()
330 show_root_location = can_create_in_root
330 show_root_location = can_create_in_root
331 if not c.repo_group.parent_group:
331 if not c.repo_group.parent_group:
332 # this group don't have a parrent so we should show empty value
332 # this group don't have a parrent so we should show empty value
333 show_root_location = True
333 show_root_location = True
334 self.__load_defaults(allow_empty_group=show_root_location,
334 self.__load_defaults(allow_empty_group=show_root_location,
335 repo_group=c.repo_group)
335 repo_group=c.repo_group)
336 defaults = self.__load_data(c.repo_group.group_id)
336 defaults = self.__load_data(c.repo_group.group_id)
337
337
338 return htmlfill.render(
338 return htmlfill.render(
339 render('admin/repo_groups/repo_group_edit.mako'),
339 render('admin/repo_groups/repo_group_edit.mako'),
340 defaults=defaults,
340 defaults=defaults,
341 encoding="UTF-8",
341 encoding="UTF-8",
342 force_defaults=False
342 force_defaults=False
343 )
343 )
344
344
345 @HasRepoGroupPermissionAnyDecorator('group.admin')
345 @HasRepoGroupPermissionAnyDecorator('group.admin')
346 def edit_repo_group_advanced(self, group_name):
346 def edit_repo_group_advanced(self, group_name):
347 """GET /repo_groups/group_name/edit: Form to edit an existing item"""
347 """GET /repo_groups/group_name/edit: Form to edit an existing item"""
348 # url('edit_repo_group', group_name=GROUP_NAME)
348 # url('edit_repo_group', group_name=GROUP_NAME)
349 c.active = 'advanced'
349 c.active = 'advanced'
350 c.repo_group = RepoGroupModel()._get_repo_group(group_name)
350 c.repo_group = RepoGroupModel()._get_repo_group(group_name)
351
351
352 return render('admin/repo_groups/repo_group_edit.mako')
352 return render('admin/repo_groups/repo_group_edit.mako')
353
353
354 @HasRepoGroupPermissionAnyDecorator('group.admin')
354 @HasRepoGroupPermissionAnyDecorator('group.admin')
355 def edit_repo_group_perms(self, group_name):
355 def edit_repo_group_perms(self, group_name):
356 """GET /repo_groups/group_name/edit: Form to edit an existing item"""
356 """GET /repo_groups/group_name/edit: Form to edit an existing item"""
357 # url('edit_repo_group', group_name=GROUP_NAME)
357 # url('edit_repo_group', group_name=GROUP_NAME)
358 c.active = 'perms'
358 c.active = 'perms'
359 c.repo_group = RepoGroupModel()._get_repo_group(group_name)
359 c.repo_group = RepoGroupModel()._get_repo_group(group_name)
360 self.__load_defaults()
360 self.__load_defaults()
361 defaults = self.__load_data(c.repo_group.group_id)
361 defaults = self.__load_data(c.repo_group.group_id)
362
362
363 return htmlfill.render(
363 return htmlfill.render(
364 render('admin/repo_groups/repo_group_edit.mako'),
364 render('admin/repo_groups/repo_group_edit.mako'),
365 defaults=defaults,
365 defaults=defaults,
366 encoding="UTF-8",
366 encoding="UTF-8",
367 force_defaults=False
367 force_defaults=False
368 )
368 )
369
369
370 @HasRepoGroupPermissionAnyDecorator('group.admin')
370 @HasRepoGroupPermissionAnyDecorator('group.admin')
371 @auth.CSRFRequired()
371 @auth.CSRFRequired()
372 def update_perms(self, group_name):
372 def update_perms(self, group_name):
373 """
373 """
374 Update permissions for given repository group
374 Update permissions for given repository group
375
375
376 :param group_name:
376 :param group_name:
377 """
377 """
378
378
379 c.repo_group = RepoGroupModel()._get_repo_group(group_name)
379 c.repo_group = RepoGroupModel()._get_repo_group(group_name)
380 valid_recursive_choices = ['none', 'repos', 'groups', 'all']
380 valid_recursive_choices = ['none', 'repos', 'groups', 'all']
381 form = RepoGroupPermsForm(valid_recursive_choices)().to_python(
381 form = RepoGroupPermsForm(valid_recursive_choices)().to_python(
382 request.POST)
382 request.POST)
383
383
384 if not c.rhodecode_user.is_admin:
384 if not c.rhodecode_user.is_admin:
385 if self._revoke_perms_on_yourself(form):
385 if self._revoke_perms_on_yourself(form):
386 msg = _('Cannot change permission for yourself as admin')
386 msg = _('Cannot change permission for yourself as admin')
387 h.flash(msg, category='warning')
387 h.flash(msg, category='warning')
388 return redirect(
388 return redirect(
389 url('edit_repo_group_perms', group_name=group_name))
389 url('edit_repo_group_perms', group_name=group_name))
390
390
391 # iterate over all members(if in recursive mode) of this groups and
391 # iterate over all members(if in recursive mode) of this groups and
392 # set the permissions !
392 # set the permissions !
393 # this can be potentially heavy operation
393 # this can be potentially heavy operation
394 RepoGroupModel().update_permissions(
394 RepoGroupModel().update_permissions(
395 c.repo_group,
395 c.repo_group,
396 form['perm_additions'], form['perm_updates'],
396 form['perm_additions'], form['perm_updates'],
397 form['perm_deletions'], form['recursive'])
397 form['perm_deletions'], form['recursive'])
398
398
399 # TODO: implement this
399 # TODO: implement this
400 # action_logger(c.rhodecode_user, 'admin_changed_repo_permissions',
400 # action_logger(c.rhodecode_user, 'admin_changed_repo_permissions',
401 # repo_name, self.ip_addr, self.sa)
401 # repo_name, self.ip_addr, self.sa)
402 Session().commit()
402 Session().commit()
403 h.flash(_('Repository Group permissions updated'), category='success')
403 h.flash(_('Repository Group permissions updated'), category='success')
404 return redirect(url('edit_repo_group_perms', group_name=group_name))
404 return redirect(url('edit_repo_group_perms', group_name=group_name))
@@ -1,610 +1,606 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2013-2017 RhodeCode GmbH
3 # Copyright (C) 2013-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 """
22 """
23 Repositories controller for RhodeCode
23 Repositories controller for RhodeCode
24 """
24 """
25
25
26 import logging
26 import logging
27 import traceback
27 import traceback
28
28
29 import formencode
29 import formencode
30 from formencode import htmlfill
30 from formencode import htmlfill
31 from pylons import request, tmpl_context as c, url
31 from pylons import request, tmpl_context as c, url
32 from pylons.controllers.util import redirect
32 from pylons.controllers.util import redirect
33 from pylons.i18n.translation import _
33 from pylons.i18n.translation import _
34 from webob.exc import HTTPForbidden, HTTPNotFound, HTTPBadRequest
34 from webob.exc import HTTPForbidden, HTTPNotFound, HTTPBadRequest
35
35
36 import rhodecode
36 import rhodecode
37 from rhodecode.lib import auth, helpers as h
37 from rhodecode.lib import auth, helpers as h
38 from rhodecode.lib.auth import (
38 from rhodecode.lib.auth import (
39 LoginRequired, HasPermissionAllDecorator,
39 LoginRequired, HasPermissionAllDecorator,
40 HasRepoPermissionAllDecorator, NotAnonymous, HasPermissionAny,
40 HasRepoPermissionAllDecorator, NotAnonymous, HasPermissionAny,
41 HasRepoGroupPermissionAny, HasRepoPermissionAnyDecorator)
41 HasRepoGroupPermissionAny, HasRepoPermissionAnyDecorator)
42 from rhodecode.lib.base import BaseRepoController, render
42 from rhodecode.lib.base import BaseRepoController, render
43 from rhodecode.lib.ext_json import json
43 from rhodecode.lib.ext_json import json
44 from rhodecode.lib.exceptions import AttachedForksError
44 from rhodecode.lib.utils import repo_name_slug, jsonify
45 from rhodecode.lib.utils import action_logger, repo_name_slug, jsonify
46 from rhodecode.lib.utils2 import safe_int, str2bool
45 from rhodecode.lib.utils2 import safe_int, str2bool
47 from rhodecode.lib.vcs import RepositoryError
46 from rhodecode.model.db import (Repository, RepoGroup, RepositoryField)
48 from rhodecode.model.db import (
49 User, Repository, UserFollowing, RepoGroup, RepositoryField)
50 from rhodecode.model.forms import (
47 from rhodecode.model.forms import (
51 RepoForm, RepoFieldForm, RepoPermsForm, RepoVcsSettingsForm,
48 RepoForm, RepoFieldForm, RepoVcsSettingsForm, IssueTrackerPatternsForm)
52 IssueTrackerPatternsForm)
53 from rhodecode.model.meta import Session
49 from rhodecode.model.meta import Session
54 from rhodecode.model.repo import RepoModel
50 from rhodecode.model.repo import RepoModel
55 from rhodecode.model.scm import ScmModel, RepoGroupList, RepoList
51 from rhodecode.model.scm import ScmModel, RepoGroupList, RepoList
56 from rhodecode.model.settings import (
52 from rhodecode.model.settings import (
57 SettingsModel, IssueTrackerSettingsModel, VcsSettingsModel,
53 SettingsModel, IssueTrackerSettingsModel, VcsSettingsModel,
58 SettingNotFound)
54 SettingNotFound)
59
55
60 log = logging.getLogger(__name__)
56 log = logging.getLogger(__name__)
61
57
62
58
63 class ReposController(BaseRepoController):
59 class ReposController(BaseRepoController):
64 """
60 """
65 REST Controller styled on the Atom Publishing Protocol"""
61 REST Controller styled on the Atom Publishing Protocol"""
66 # To properly map this controller, ensure your config/routing.py
62 # To properly map this controller, ensure your config/routing.py
67 # file has a resource setup:
63 # file has a resource setup:
68 # map.resource('repo', 'repos')
64 # map.resource('repo', 'repos')
69
65
70 @LoginRequired()
66 @LoginRequired()
71 def __before__(self):
67 def __before__(self):
72 super(ReposController, self).__before__()
68 super(ReposController, self).__before__()
73
69
74 def _load_repo(self, repo_name):
70 def _load_repo(self, repo_name):
75 repo_obj = Repository.get_by_repo_name(repo_name)
71 repo_obj = Repository.get_by_repo_name(repo_name)
76
72
77 if repo_obj is None:
73 if repo_obj is None:
78 h.not_mapped_error(repo_name)
74 h.not_mapped_error(repo_name)
79 return redirect(url('repos'))
75 return redirect(url('repos'))
80
76
81 return repo_obj
77 return repo_obj
82
78
83 def __load_defaults(self, repo=None):
79 def __load_defaults(self, repo=None):
84 acl_groups = RepoGroupList(RepoGroup.query().all(),
80 acl_groups = RepoGroupList(RepoGroup.query().all(),
85 perm_set=['group.write', 'group.admin'])
81 perm_set=['group.write', 'group.admin'])
86 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
82 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
87 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)
88
84
89 # in case someone no longer have a group.write access to a repository
85 # in case someone no longer have a group.write access to a repository
90 # pre fill the list with this entry, we don't care if this is the same
86 # pre fill the list with this entry, we don't care if this is the same
91 # but it will allow saving repo data properly.
87 # but it will allow saving repo data properly.
92
88
93 repo_group = None
89 repo_group = None
94 if repo:
90 if repo:
95 repo_group = repo.group
91 repo_group = repo.group
96 if repo_group and unicode(repo_group.group_id) not in c.repo_groups_choices:
92 if repo_group and unicode(repo_group.group_id) not in c.repo_groups_choices:
97 c.repo_groups_choices.append(unicode(repo_group.group_id))
93 c.repo_groups_choices.append(unicode(repo_group.group_id))
98 c.repo_groups.append(RepoGroup._generate_choice(repo_group))
94 c.repo_groups.append(RepoGroup._generate_choice(repo_group))
99
95
100 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
96 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
101 c.landing_revs_choices = choices
97 c.landing_revs_choices = choices
102
98
103 def __load_data(self, repo_name=None):
99 def __load_data(self, repo_name=None):
104 """
100 """
105 Load defaults settings for edit, and update
101 Load defaults settings for edit, and update
106
102
107 :param repo_name:
103 :param repo_name:
108 """
104 """
109 c.repo_info = self._load_repo(repo_name)
105 c.repo_info = self._load_repo(repo_name)
110 self.__load_defaults(c.repo_info)
106 self.__load_defaults(c.repo_info)
111
107
112 # override defaults for exact repo info here git/hg etc
108 # override defaults for exact repo info here git/hg etc
113 if not c.repository_requirements_missing:
109 if not c.repository_requirements_missing:
114 choices, c.landing_revs = ScmModel().get_repo_landing_revs(
110 choices, c.landing_revs = ScmModel().get_repo_landing_revs(
115 c.repo_info)
111 c.repo_info)
116 c.landing_revs_choices = choices
112 c.landing_revs_choices = choices
117 defaults = RepoModel()._get_defaults(repo_name)
113 defaults = RepoModel()._get_defaults(repo_name)
118
114
119 return defaults
115 return defaults
120
116
121 def _log_creation_exception(self, e, repo_name):
117 def _log_creation_exception(self, e, repo_name):
122 reason = None
118 reason = None
123 if len(e.args) == 2:
119 if len(e.args) == 2:
124 reason = e.args[1]
120 reason = e.args[1]
125
121
126 if reason == 'INVALID_CERTIFICATE':
122 if reason == 'INVALID_CERTIFICATE':
127 log.exception(
123 log.exception(
128 'Exception creating a repository: invalid certificate')
124 'Exception creating a repository: invalid certificate')
129 msg = (_('Error creating repository %s: invalid certificate')
125 msg = (_('Error creating repository %s: invalid certificate')
130 % repo_name)
126 % repo_name)
131 else:
127 else:
132 log.exception("Exception creating a repository")
128 log.exception("Exception creating a repository")
133 msg = (_('Error creating repository %s')
129 msg = (_('Error creating repository %s')
134 % repo_name)
130 % repo_name)
135
131
136 return msg
132 return msg
137
133
138 @NotAnonymous()
134 @NotAnonymous()
139 def index(self, format='html'):
135 def index(self, format='html'):
140 """GET /repos: All items in the collection"""
136 """GET /repos: All items in the collection"""
141 # url('repos')
137 # url('repos')
142
138
143 repo_list = Repository.get_all_repos()
139 repo_list = Repository.get_all_repos()
144 c.repo_list = RepoList(repo_list, perm_set=['repository.admin'])
140 c.repo_list = RepoList(repo_list, perm_set=['repository.admin'])
145 repos_data = RepoModel().get_repos_as_dict(
141 repos_data = RepoModel().get_repos_as_dict(
146 repo_list=c.repo_list, admin=True, super_user_actions=True)
142 repo_list=c.repo_list, admin=True, super_user_actions=True)
147 # json used to render the grid
143 # json used to render the grid
148 c.data = json.dumps(repos_data)
144 c.data = json.dumps(repos_data)
149
145
150 return render('admin/repos/repos.mako')
146 return render('admin/repos/repos.mako')
151
147
152 # perms check inside
148 # perms check inside
153 @NotAnonymous()
149 @NotAnonymous()
154 @auth.CSRFRequired()
150 @auth.CSRFRequired()
155 def create(self):
151 def create(self):
156 """
152 """
157 POST /repos: Create a new item"""
153 POST /repos: Create a new item"""
158 # url('repos')
154 # url('repos')
159
155
160 self.__load_defaults()
156 self.__load_defaults()
161 form_result = {}
157 form_result = {}
162 task_id = None
158 task_id = None
163 c.personal_repo_group = c.rhodecode_user.personal_repo_group
159 c.personal_repo_group = c.rhodecode_user.personal_repo_group
164 try:
160 try:
165 # CanWriteToGroup validators checks permissions of this POST
161 # CanWriteToGroup validators checks permissions of this POST
166 form_result = RepoForm(repo_groups=c.repo_groups_choices,
162 form_result = RepoForm(repo_groups=c.repo_groups_choices,
167 landing_revs=c.landing_revs_choices)()\
163 landing_revs=c.landing_revs_choices)()\
168 .to_python(dict(request.POST))
164 .to_python(dict(request.POST))
169
165
170 # create is done sometimes async on celery, db transaction
166 # create is done sometimes async on celery, db transaction
171 # management is handled there.
167 # management is handled there.
172 task = RepoModel().create(form_result, c.rhodecode_user.user_id)
168 task = RepoModel().create(form_result, c.rhodecode_user.user_id)
173 from celery.result import BaseAsyncResult
169 from celery.result import BaseAsyncResult
174 if isinstance(task, BaseAsyncResult):
170 if isinstance(task, BaseAsyncResult):
175 task_id = task.task_id
171 task_id = task.task_id
176 except formencode.Invalid as errors:
172 except formencode.Invalid as errors:
177 return htmlfill.render(
173 return htmlfill.render(
178 render('admin/repos/repo_add.mako'),
174 render('admin/repos/repo_add.mako'),
179 defaults=errors.value,
175 defaults=errors.value,
180 errors=errors.error_dict or {},
176 errors=errors.error_dict or {},
181 prefix_error=False,
177 prefix_error=False,
182 encoding="UTF-8",
178 encoding="UTF-8",
183 force_defaults=False)
179 force_defaults=False)
184
180
185 except Exception as e:
181 except Exception as e:
186 msg = self._log_creation_exception(e, form_result.get('repo_name'))
182 msg = self._log_creation_exception(e, form_result.get('repo_name'))
187 h.flash(msg, category='error')
183 h.flash(msg, category='error')
188 return redirect(h.route_path('home'))
184 return redirect(h.route_path('home'))
189
185
190 return redirect(h.url('repo_creating_home',
186 return redirect(h.url('repo_creating_home',
191 repo_name=form_result['repo_name_full'],
187 repo_name=form_result['repo_name_full'],
192 task_id=task_id))
188 task_id=task_id))
193
189
194 # perms check inside
190 # perms check inside
195 @NotAnonymous()
191 @NotAnonymous()
196 def create_repository(self):
192 def create_repository(self):
197 """GET /_admin/create_repository: Form to create a new item"""
193 """GET /_admin/create_repository: Form to create a new item"""
198 new_repo = request.GET.get('repo', '')
194 new_repo = request.GET.get('repo', '')
199 parent_group = safe_int(request.GET.get('parent_group'))
195 parent_group = safe_int(request.GET.get('parent_group'))
200 _gr = RepoGroup.get(parent_group)
196 _gr = RepoGroup.get(parent_group)
201
197
202 if not HasPermissionAny('hg.admin', 'hg.create.repository')():
198 if not HasPermissionAny('hg.admin', 'hg.create.repository')():
203 # you're not super admin nor have global create permissions,
199 # you're not super admin nor have global create permissions,
204 # but maybe you have at least write permission to a parent group ?
200 # but maybe you have at least write permission to a parent group ?
205
201
206 gr_name = _gr.group_name if _gr else None
202 gr_name = _gr.group_name if _gr else None
207 # create repositories with write permission on group is set to true
203 # create repositories with write permission on group is set to true
208 create_on_write = HasPermissionAny('hg.create.write_on_repogroup.true')()
204 create_on_write = HasPermissionAny('hg.create.write_on_repogroup.true')()
209 group_admin = HasRepoGroupPermissionAny('group.admin')(group_name=gr_name)
205 group_admin = HasRepoGroupPermissionAny('group.admin')(group_name=gr_name)
210 group_write = HasRepoGroupPermissionAny('group.write')(group_name=gr_name)
206 group_write = HasRepoGroupPermissionAny('group.write')(group_name=gr_name)
211 if not (group_admin or (group_write and create_on_write)):
207 if not (group_admin or (group_write and create_on_write)):
212 raise HTTPForbidden
208 raise HTTPForbidden
213
209
214 acl_groups = RepoGroupList(RepoGroup.query().all(),
210 acl_groups = RepoGroupList(RepoGroup.query().all(),
215 perm_set=['group.write', 'group.admin'])
211 perm_set=['group.write', 'group.admin'])
216 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
212 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
217 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
213 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
218 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
214 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
219 c.personal_repo_group = c.rhodecode_user.personal_repo_group
215 c.personal_repo_group = c.rhodecode_user.personal_repo_group
220 c.new_repo = repo_name_slug(new_repo)
216 c.new_repo = repo_name_slug(new_repo)
221
217
222 # apply the defaults from defaults page
218 # apply the defaults from defaults page
223 defaults = SettingsModel().get_default_repo_settings(strip_prefix=True)
219 defaults = SettingsModel().get_default_repo_settings(strip_prefix=True)
224 # set checkbox to autochecked
220 # set checkbox to autochecked
225 defaults['repo_copy_permissions'] = True
221 defaults['repo_copy_permissions'] = True
226
222
227 parent_group_choice = '-1'
223 parent_group_choice = '-1'
228 if not c.rhodecode_user.is_admin and c.rhodecode_user.personal_repo_group:
224 if not c.rhodecode_user.is_admin and c.rhodecode_user.personal_repo_group:
229 parent_group_choice = c.rhodecode_user.personal_repo_group
225 parent_group_choice = c.rhodecode_user.personal_repo_group
230
226
231 if parent_group and _gr:
227 if parent_group and _gr:
232 if parent_group in [x[0] for x in c.repo_groups]:
228 if parent_group in [x[0] for x in c.repo_groups]:
233 parent_group_choice = unicode(parent_group)
229 parent_group_choice = unicode(parent_group)
234
230
235 defaults.update({'repo_group': parent_group_choice})
231 defaults.update({'repo_group': parent_group_choice})
236
232
237 return htmlfill.render(
233 return htmlfill.render(
238 render('admin/repos/repo_add.mako'),
234 render('admin/repos/repo_add.mako'),
239 defaults=defaults,
235 defaults=defaults,
240 errors={},
236 errors={},
241 prefix_error=False,
237 prefix_error=False,
242 encoding="UTF-8",
238 encoding="UTF-8",
243 force_defaults=False
239 force_defaults=False
244 )
240 )
245
241
246 @NotAnonymous()
242 @NotAnonymous()
247 def repo_creating(self, repo_name):
243 def repo_creating(self, repo_name):
248 c.repo = repo_name
244 c.repo = repo_name
249 c.task_id = request.GET.get('task_id')
245 c.task_id = request.GET.get('task_id')
250 if not c.repo:
246 if not c.repo:
251 raise HTTPNotFound()
247 raise HTTPNotFound()
252 return render('admin/repos/repo_creating.mako')
248 return render('admin/repos/repo_creating.mako')
253
249
254 @NotAnonymous()
250 @NotAnonymous()
255 @jsonify
251 @jsonify
256 def repo_check(self, repo_name):
252 def repo_check(self, repo_name):
257 c.repo = repo_name
253 c.repo = repo_name
258 task_id = request.GET.get('task_id')
254 task_id = request.GET.get('task_id')
259
255
260 if task_id and task_id not in ['None']:
256 if task_id and task_id not in ['None']:
261 import rhodecode
257 import rhodecode
262 from celery.result import AsyncResult
258 from celery.result import AsyncResult
263 if rhodecode.CELERY_ENABLED:
259 if rhodecode.CELERY_ENABLED:
264 task = AsyncResult(task_id)
260 task = AsyncResult(task_id)
265 if task.failed():
261 if task.failed():
266 msg = self._log_creation_exception(task.result, c.repo)
262 msg = self._log_creation_exception(task.result, c.repo)
267 h.flash(msg, category='error')
263 h.flash(msg, category='error')
268 return redirect(h.route_path('home'), code=501)
264 return redirect(h.route_path('home'), code=501)
269
265
270 repo = Repository.get_by_repo_name(repo_name)
266 repo = Repository.get_by_repo_name(repo_name)
271 if repo and repo.repo_state == Repository.STATE_CREATED:
267 if repo and repo.repo_state == Repository.STATE_CREATED:
272 if repo.clone_uri:
268 if repo.clone_uri:
273 clone_uri = repo.clone_uri_hidden
269 clone_uri = repo.clone_uri_hidden
274 h.flash(_('Created repository %s from %s')
270 h.flash(_('Created repository %s from %s')
275 % (repo.repo_name, clone_uri), category='success')
271 % (repo.repo_name, clone_uri), category='success')
276 else:
272 else:
277 repo_url = h.link_to(
273 repo_url = h.link_to(
278 repo.repo_name,
274 repo.repo_name,
279 h.route_path('repo_summary',repo_name=repo.repo_name))
275 h.route_path('repo_summary',repo_name=repo.repo_name))
280 fork = repo.fork
276 fork = repo.fork
281 if fork:
277 if fork:
282 fork_name = fork.repo_name
278 fork_name = fork.repo_name
283 h.flash(h.literal(_('Forked repository %s as %s')
279 h.flash(h.literal(_('Forked repository %s as %s')
284 % (fork_name, repo_url)), category='success')
280 % (fork_name, repo_url)), category='success')
285 else:
281 else:
286 h.flash(h.literal(_('Created repository %s') % repo_url),
282 h.flash(h.literal(_('Created repository %s') % repo_url),
287 category='success')
283 category='success')
288 return {'result': True}
284 return {'result': True}
289 return {'result': False}
285 return {'result': False}
290
286
291 @HasPermissionAllDecorator('hg.admin')
287 @HasPermissionAllDecorator('hg.admin')
292 def show(self, repo_name, format='html'):
288 def show(self, repo_name, format='html'):
293 """GET /repos/repo_name: Show a specific item"""
289 """GET /repos/repo_name: Show a specific item"""
294 # url('repo', repo_name=ID)
290 # url('repo', repo_name=ID)
295
291
296 @HasRepoPermissionAllDecorator('repository.admin')
292 @HasRepoPermissionAllDecorator('repository.admin')
297 def edit_fields(self, repo_name):
293 def edit_fields(self, repo_name):
298 """GET /repo_name/settings: Form to edit an existing item"""
294 """GET /repo_name/settings: Form to edit an existing item"""
299 c.repo_info = self._load_repo(repo_name)
295 c.repo_info = self._load_repo(repo_name)
300 c.repo_fields = RepositoryField.query()\
296 c.repo_fields = RepositoryField.query()\
301 .filter(RepositoryField.repository == c.repo_info).all()
297 .filter(RepositoryField.repository == c.repo_info).all()
302 c.active = 'fields'
298 c.active = 'fields'
303 if request.POST:
299 if request.POST:
304
300
305 return redirect(url('repo_edit_fields'))
301 return redirect(url('repo_edit_fields'))
306 return render('admin/repos/repo_edit.mako')
302 return render('admin/repos/repo_edit.mako')
307
303
308 @HasRepoPermissionAllDecorator('repository.admin')
304 @HasRepoPermissionAllDecorator('repository.admin')
309 @auth.CSRFRequired()
305 @auth.CSRFRequired()
310 def create_repo_field(self, repo_name):
306 def create_repo_field(self, repo_name):
311 try:
307 try:
312 form_result = RepoFieldForm()().to_python(dict(request.POST))
308 form_result = RepoFieldForm()().to_python(dict(request.POST))
313 RepoModel().add_repo_field(
309 RepoModel().add_repo_field(
314 repo_name, form_result['new_field_key'],
310 repo_name, form_result['new_field_key'],
315 field_type=form_result['new_field_type'],
311 field_type=form_result['new_field_type'],
316 field_value=form_result['new_field_value'],
312 field_value=form_result['new_field_value'],
317 field_label=form_result['new_field_label'],
313 field_label=form_result['new_field_label'],
318 field_desc=form_result['new_field_desc'])
314 field_desc=form_result['new_field_desc'])
319
315
320 Session().commit()
316 Session().commit()
321 except Exception as e:
317 except Exception as e:
322 log.exception("Exception creating field")
318 log.exception("Exception creating field")
323 msg = _('An error occurred during creation of field')
319 msg = _('An error occurred during creation of field')
324 if isinstance(e, formencode.Invalid):
320 if isinstance(e, formencode.Invalid):
325 msg += ". " + e.msg
321 msg += ". " + e.msg
326 h.flash(msg, category='error')
322 h.flash(msg, category='error')
327 return redirect(url('edit_repo_fields', repo_name=repo_name))
323 return redirect(url('edit_repo_fields', repo_name=repo_name))
328
324
329 @HasRepoPermissionAllDecorator('repository.admin')
325 @HasRepoPermissionAllDecorator('repository.admin')
330 @auth.CSRFRequired()
326 @auth.CSRFRequired()
331 def delete_repo_field(self, repo_name, field_id):
327 def delete_repo_field(self, repo_name, field_id):
332 field = RepositoryField.get_or_404(field_id)
328 field = RepositoryField.get_or_404(field_id)
333 try:
329 try:
334 RepoModel().delete_repo_field(repo_name, field.field_key)
330 RepoModel().delete_repo_field(repo_name, field.field_key)
335 Session().commit()
331 Session().commit()
336 except Exception as e:
332 except Exception as e:
337 log.exception("Exception during removal of field")
333 log.exception("Exception during removal of field")
338 msg = _('An error occurred during removal of field')
334 msg = _('An error occurred during removal of field')
339 h.flash(msg, category='error')
335 h.flash(msg, category='error')
340 return redirect(url('edit_repo_fields', repo_name=repo_name))
336 return redirect(url('edit_repo_fields', repo_name=repo_name))
341
337
342 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
338 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
343 @auth.CSRFRequired()
339 @auth.CSRFRequired()
344 def toggle_locking(self, repo_name):
340 def toggle_locking(self, repo_name):
345 """
341 """
346 Toggle locking of repository by simple GET call to url
342 Toggle locking of repository by simple GET call to url
347
343
348 :param repo_name:
344 :param repo_name:
349 """
345 """
350
346
351 try:
347 try:
352 repo = Repository.get_by_repo_name(repo_name)
348 repo = Repository.get_by_repo_name(repo_name)
353
349
354 if repo.enable_locking:
350 if repo.enable_locking:
355 if repo.locked[0]:
351 if repo.locked[0]:
356 Repository.unlock(repo)
352 Repository.unlock(repo)
357 action = _('Unlocked')
353 action = _('Unlocked')
358 else:
354 else:
359 Repository.lock(repo, c.rhodecode_user.user_id,
355 Repository.lock(repo, c.rhodecode_user.user_id,
360 lock_reason=Repository.LOCK_WEB)
356 lock_reason=Repository.LOCK_WEB)
361 action = _('Locked')
357 action = _('Locked')
362
358
363 h.flash(_('Repository has been %s') % action,
359 h.flash(_('Repository has been %s') % action,
364 category='success')
360 category='success')
365 except Exception:
361 except Exception:
366 log.exception("Exception during unlocking")
362 log.exception("Exception during unlocking")
367 h.flash(_('An error occurred during unlocking'),
363 h.flash(_('An error occurred during unlocking'),
368 category='error')
364 category='error')
369 return redirect(h.route_path('repo_summary', repo_name=repo_name))
365 return redirect(h.route_path('repo_summary', repo_name=repo_name))
370
366
371 @HasRepoPermissionAllDecorator('repository.admin')
367 @HasRepoPermissionAllDecorator('repository.admin')
372 @auth.CSRFRequired()
368 @auth.CSRFRequired()
373 def edit_remote(self, repo_name):
369 def edit_remote(self, repo_name):
374 """PUT /{repo_name}/settings/remote: edit the repo remote."""
370 """PUT /{repo_name}/settings/remote: edit the repo remote."""
375 try:
371 try:
376 ScmModel().pull_changes(repo_name, c.rhodecode_user.username)
372 ScmModel().pull_changes(repo_name, c.rhodecode_user.username)
377 h.flash(_('Pulled from remote location'), category='success')
373 h.flash(_('Pulled from remote location'), category='success')
378 except Exception:
374 except Exception:
379 log.exception("Exception during pull from remote")
375 log.exception("Exception during pull from remote")
380 h.flash(_('An error occurred during pull from remote location'),
376 h.flash(_('An error occurred during pull from remote location'),
381 category='error')
377 category='error')
382 return redirect(url('edit_repo_remote', repo_name=c.repo_name))
378 return redirect(url('edit_repo_remote', repo_name=c.repo_name))
383
379
384 @HasRepoPermissionAllDecorator('repository.admin')
380 @HasRepoPermissionAllDecorator('repository.admin')
385 def edit_remote_form(self, repo_name):
381 def edit_remote_form(self, repo_name):
386 """GET /repo_name/settings: Form to edit an existing item"""
382 """GET /repo_name/settings: Form to edit an existing item"""
387 c.repo_info = self._load_repo(repo_name)
383 c.repo_info = self._load_repo(repo_name)
388 c.active = 'remote'
384 c.active = 'remote'
389
385
390 return render('admin/repos/repo_edit.mako')
386 return render('admin/repos/repo_edit.mako')
391
387
392 @HasRepoPermissionAllDecorator('repository.admin')
388 @HasRepoPermissionAllDecorator('repository.admin')
393 @auth.CSRFRequired()
389 @auth.CSRFRequired()
394 def edit_statistics(self, repo_name):
390 def edit_statistics(self, repo_name):
395 """PUT /{repo_name}/settings/statistics: reset the repo statistics."""
391 """PUT /{repo_name}/settings/statistics: reset the repo statistics."""
396 try:
392 try:
397 RepoModel().delete_stats(repo_name)
393 RepoModel().delete_stats(repo_name)
398 Session().commit()
394 Session().commit()
399 except Exception as e:
395 except Exception as e:
400 log.error(traceback.format_exc())
396 log.error(traceback.format_exc())
401 h.flash(_('An error occurred during deletion of repository stats'),
397 h.flash(_('An error occurred during deletion of repository stats'),
402 category='error')
398 category='error')
403 return redirect(url('edit_repo_statistics', repo_name=c.repo_name))
399 return redirect(url('edit_repo_statistics', repo_name=c.repo_name))
404
400
405 @HasRepoPermissionAllDecorator('repository.admin')
401 @HasRepoPermissionAllDecorator('repository.admin')
406 def edit_statistics_form(self, repo_name):
402 def edit_statistics_form(self, repo_name):
407 """GET /repo_name/settings: Form to edit an existing item"""
403 """GET /repo_name/settings: Form to edit an existing item"""
408 c.repo_info = self._load_repo(repo_name)
404 c.repo_info = self._load_repo(repo_name)
409 repo = c.repo_info.scm_instance()
405 repo = c.repo_info.scm_instance()
410
406
411 if c.repo_info.stats:
407 if c.repo_info.stats:
412 # this is on what revision we ended up so we add +1 for count
408 # this is on what revision we ended up so we add +1 for count
413 last_rev = c.repo_info.stats.stat_on_revision + 1
409 last_rev = c.repo_info.stats.stat_on_revision + 1
414 else:
410 else:
415 last_rev = 0
411 last_rev = 0
416 c.stats_revision = last_rev
412 c.stats_revision = last_rev
417
413
418 c.repo_last_rev = repo.count()
414 c.repo_last_rev = repo.count()
419
415
420 if last_rev == 0 or c.repo_last_rev == 0:
416 if last_rev == 0 or c.repo_last_rev == 0:
421 c.stats_percentage = 0
417 c.stats_percentage = 0
422 else:
418 else:
423 c.stats_percentage = '%.2f' % ((float((last_rev)) / c.repo_last_rev) * 100)
419 c.stats_percentage = '%.2f' % ((float((last_rev)) / c.repo_last_rev) * 100)
424
420
425 c.active = 'statistics'
421 c.active = 'statistics'
426
422
427 return render('admin/repos/repo_edit.mako')
423 return render('admin/repos/repo_edit.mako')
428
424
429 @HasRepoPermissionAllDecorator('repository.admin')
425 @HasRepoPermissionAllDecorator('repository.admin')
430 @auth.CSRFRequired()
426 @auth.CSRFRequired()
431 def repo_issuetracker_test(self, repo_name):
427 def repo_issuetracker_test(self, repo_name):
432 if request.is_xhr:
428 if request.is_xhr:
433 return h.urlify_commit_message(
429 return h.urlify_commit_message(
434 request.POST.get('test_text', ''),
430 request.POST.get('test_text', ''),
435 repo_name)
431 repo_name)
436 else:
432 else:
437 raise HTTPBadRequest()
433 raise HTTPBadRequest()
438
434
439 @HasRepoPermissionAllDecorator('repository.admin')
435 @HasRepoPermissionAllDecorator('repository.admin')
440 @auth.CSRFRequired()
436 @auth.CSRFRequired()
441 def repo_issuetracker_delete(self, repo_name):
437 def repo_issuetracker_delete(self, repo_name):
442 uid = request.POST.get('uid')
438 uid = request.POST.get('uid')
443 repo_settings = IssueTrackerSettingsModel(repo=repo_name)
439 repo_settings = IssueTrackerSettingsModel(repo=repo_name)
444 try:
440 try:
445 repo_settings.delete_entries(uid)
441 repo_settings.delete_entries(uid)
446 except Exception:
442 except Exception:
447 h.flash(_('Error occurred during deleting issue tracker entry'),
443 h.flash(_('Error occurred during deleting issue tracker entry'),
448 category='error')
444 category='error')
449 else:
445 else:
450 h.flash(_('Removed issue tracker entry'), category='success')
446 h.flash(_('Removed issue tracker entry'), category='success')
451 return redirect(url('repo_settings_issuetracker',
447 return redirect(url('repo_settings_issuetracker',
452 repo_name=repo_name))
448 repo_name=repo_name))
453
449
454 def _update_patterns(self, form, repo_settings):
450 def _update_patterns(self, form, repo_settings):
455 for uid in form['delete_patterns']:
451 for uid in form['delete_patterns']:
456 repo_settings.delete_entries(uid)
452 repo_settings.delete_entries(uid)
457
453
458 for pattern in form['patterns']:
454 for pattern in form['patterns']:
459 for setting, value, type_ in pattern:
455 for setting, value, type_ in pattern:
460 sett = repo_settings.create_or_update_setting(
456 sett = repo_settings.create_or_update_setting(
461 setting, value, type_)
457 setting, value, type_)
462 Session().add(sett)
458 Session().add(sett)
463
459
464 Session().commit()
460 Session().commit()
465
461
466 @HasRepoPermissionAllDecorator('repository.admin')
462 @HasRepoPermissionAllDecorator('repository.admin')
467 @auth.CSRFRequired()
463 @auth.CSRFRequired()
468 def repo_issuetracker_save(self, repo_name):
464 def repo_issuetracker_save(self, repo_name):
469 # Save inheritance
465 # Save inheritance
470 repo_settings = IssueTrackerSettingsModel(repo=repo_name)
466 repo_settings = IssueTrackerSettingsModel(repo=repo_name)
471 inherited = (request.POST.get('inherit_global_issuetracker')
467 inherited = (request.POST.get('inherit_global_issuetracker')
472 == "inherited")
468 == "inherited")
473 repo_settings.inherit_global_settings = inherited
469 repo_settings.inherit_global_settings = inherited
474 Session().commit()
470 Session().commit()
475
471
476 form = IssueTrackerPatternsForm()().to_python(request.POST)
472 form = IssueTrackerPatternsForm()().to_python(request.POST)
477 if form:
473 if form:
478 self._update_patterns(form, repo_settings)
474 self._update_patterns(form, repo_settings)
479
475
480 h.flash(_('Updated issue tracker entries'), category='success')
476 h.flash(_('Updated issue tracker entries'), category='success')
481 return redirect(url('repo_settings_issuetracker',
477 return redirect(url('repo_settings_issuetracker',
482 repo_name=repo_name))
478 repo_name=repo_name))
483
479
484 @HasRepoPermissionAllDecorator('repository.admin')
480 @HasRepoPermissionAllDecorator('repository.admin')
485 def repo_issuetracker(self, repo_name):
481 def repo_issuetracker(self, repo_name):
486 """GET /admin/settings/issue-tracker: All items in the collection"""
482 """GET /admin/settings/issue-tracker: All items in the collection"""
487 c.active = 'issuetracker'
483 c.active = 'issuetracker'
488 c.data = 'data'
484 c.data = 'data'
489 c.repo_info = self._load_repo(repo_name)
485 c.repo_info = self._load_repo(repo_name)
490
486
491 repo = Repository.get_by_repo_name(repo_name)
487 repo = Repository.get_by_repo_name(repo_name)
492 c.settings_model = IssueTrackerSettingsModel(repo=repo)
488 c.settings_model = IssueTrackerSettingsModel(repo=repo)
493 c.global_patterns = c.settings_model.get_global_settings()
489 c.global_patterns = c.settings_model.get_global_settings()
494 c.repo_patterns = c.settings_model.get_repo_settings()
490 c.repo_patterns = c.settings_model.get_repo_settings()
495
491
496 return render('admin/repos/repo_edit.mako')
492 return render('admin/repos/repo_edit.mako')
497
493
498 @HasRepoPermissionAllDecorator('repository.admin')
494 @HasRepoPermissionAllDecorator('repository.admin')
499 def repo_settings_vcs(self, repo_name):
495 def repo_settings_vcs(self, repo_name):
500 """GET /{repo_name}/settings/vcs/: All items in the collection"""
496 """GET /{repo_name}/settings/vcs/: All items in the collection"""
501
497
502 model = VcsSettingsModel(repo=repo_name)
498 model = VcsSettingsModel(repo=repo_name)
503
499
504 c.active = 'vcs'
500 c.active = 'vcs'
505 c.global_svn_branch_patterns = model.get_global_svn_branch_patterns()
501 c.global_svn_branch_patterns = model.get_global_svn_branch_patterns()
506 c.global_svn_tag_patterns = model.get_global_svn_tag_patterns()
502 c.global_svn_tag_patterns = model.get_global_svn_tag_patterns()
507 c.svn_branch_patterns = model.get_repo_svn_branch_patterns()
503 c.svn_branch_patterns = model.get_repo_svn_branch_patterns()
508 c.svn_tag_patterns = model.get_repo_svn_tag_patterns()
504 c.svn_tag_patterns = model.get_repo_svn_tag_patterns()
509 c.repo_info = self._load_repo(repo_name)
505 c.repo_info = self._load_repo(repo_name)
510 defaults = self._vcs_form_defaults(repo_name)
506 defaults = self._vcs_form_defaults(repo_name)
511 c.inherit_global_settings = defaults['inherit_global_settings']
507 c.inherit_global_settings = defaults['inherit_global_settings']
512 c.labs_active = str2bool(
508 c.labs_active = str2bool(
513 rhodecode.CONFIG.get('labs_settings_active', 'true'))
509 rhodecode.CONFIG.get('labs_settings_active', 'true'))
514
510
515 return htmlfill.render(
511 return htmlfill.render(
516 render('admin/repos/repo_edit.mako'),
512 render('admin/repos/repo_edit.mako'),
517 defaults=defaults,
513 defaults=defaults,
518 encoding="UTF-8",
514 encoding="UTF-8",
519 force_defaults=False)
515 force_defaults=False)
520
516
521 @HasRepoPermissionAllDecorator('repository.admin')
517 @HasRepoPermissionAllDecorator('repository.admin')
522 @auth.CSRFRequired()
518 @auth.CSRFRequired()
523 def repo_settings_vcs_update(self, repo_name):
519 def repo_settings_vcs_update(self, repo_name):
524 """POST /{repo_name}/settings/vcs/: All items in the collection"""
520 """POST /{repo_name}/settings/vcs/: All items in the collection"""
525 c.active = 'vcs'
521 c.active = 'vcs'
526
522
527 model = VcsSettingsModel(repo=repo_name)
523 model = VcsSettingsModel(repo=repo_name)
528 c.global_svn_branch_patterns = model.get_global_svn_branch_patterns()
524 c.global_svn_branch_patterns = model.get_global_svn_branch_patterns()
529 c.global_svn_tag_patterns = model.get_global_svn_tag_patterns()
525 c.global_svn_tag_patterns = model.get_global_svn_tag_patterns()
530 c.svn_branch_patterns = model.get_repo_svn_branch_patterns()
526 c.svn_branch_patterns = model.get_repo_svn_branch_patterns()
531 c.svn_tag_patterns = model.get_repo_svn_tag_patterns()
527 c.svn_tag_patterns = model.get_repo_svn_tag_patterns()
532 c.repo_info = self._load_repo(repo_name)
528 c.repo_info = self._load_repo(repo_name)
533 defaults = self._vcs_form_defaults(repo_name)
529 defaults = self._vcs_form_defaults(repo_name)
534 c.inherit_global_settings = defaults['inherit_global_settings']
530 c.inherit_global_settings = defaults['inherit_global_settings']
535
531
536 application_form = RepoVcsSettingsForm(repo_name)()
532 application_form = RepoVcsSettingsForm(repo_name)()
537 try:
533 try:
538 form_result = application_form.to_python(dict(request.POST))
534 form_result = application_form.to_python(dict(request.POST))
539 except formencode.Invalid as errors:
535 except formencode.Invalid as errors:
540 h.flash(
536 h.flash(
541 _("Some form inputs contain invalid data."),
537 _("Some form inputs contain invalid data."),
542 category='error')
538 category='error')
543 return htmlfill.render(
539 return htmlfill.render(
544 render('admin/repos/repo_edit.mako'),
540 render('admin/repos/repo_edit.mako'),
545 defaults=errors.value,
541 defaults=errors.value,
546 errors=errors.error_dict or {},
542 errors=errors.error_dict or {},
547 prefix_error=False,
543 prefix_error=False,
548 encoding="UTF-8",
544 encoding="UTF-8",
549 force_defaults=False
545 force_defaults=False
550 )
546 )
551
547
552 try:
548 try:
553 inherit_global_settings = form_result['inherit_global_settings']
549 inherit_global_settings = form_result['inherit_global_settings']
554 model.create_or_update_repo_settings(
550 model.create_or_update_repo_settings(
555 form_result, inherit_global_settings=inherit_global_settings)
551 form_result, inherit_global_settings=inherit_global_settings)
556 except Exception:
552 except Exception:
557 log.exception("Exception while updating settings")
553 log.exception("Exception while updating settings")
558 h.flash(
554 h.flash(
559 _('Error occurred during updating repository VCS settings'),
555 _('Error occurred during updating repository VCS settings'),
560 category='error')
556 category='error')
561 else:
557 else:
562 Session().commit()
558 Session().commit()
563 h.flash(_('Updated VCS settings'), category='success')
559 h.flash(_('Updated VCS settings'), category='success')
564 return redirect(url('repo_vcs_settings', repo_name=repo_name))
560 return redirect(url('repo_vcs_settings', repo_name=repo_name))
565
561
566 return htmlfill.render(
562 return htmlfill.render(
567 render('admin/repos/repo_edit.mako'),
563 render('admin/repos/repo_edit.mako'),
568 defaults=self._vcs_form_defaults(repo_name),
564 defaults=self._vcs_form_defaults(repo_name),
569 encoding="UTF-8",
565 encoding="UTF-8",
570 force_defaults=False)
566 force_defaults=False)
571
567
572 @HasRepoPermissionAllDecorator('repository.admin')
568 @HasRepoPermissionAllDecorator('repository.admin')
573 @auth.CSRFRequired()
569 @auth.CSRFRequired()
574 @jsonify
570 @jsonify
575 def repo_delete_svn_pattern(self, repo_name):
571 def repo_delete_svn_pattern(self, repo_name):
576 if not request.is_xhr:
572 if not request.is_xhr:
577 return False
573 return False
578
574
579 delete_pattern_id = request.POST.get('delete_svn_pattern')
575 delete_pattern_id = request.POST.get('delete_svn_pattern')
580 model = VcsSettingsModel(repo=repo_name)
576 model = VcsSettingsModel(repo=repo_name)
581 try:
577 try:
582 model.delete_repo_svn_pattern(delete_pattern_id)
578 model.delete_repo_svn_pattern(delete_pattern_id)
583 except SettingNotFound:
579 except SettingNotFound:
584 raise HTTPBadRequest()
580 raise HTTPBadRequest()
585
581
586 Session().commit()
582 Session().commit()
587 return True
583 return True
588
584
589 def _vcs_form_defaults(self, repo_name):
585 def _vcs_form_defaults(self, repo_name):
590 model = VcsSettingsModel(repo=repo_name)
586 model = VcsSettingsModel(repo=repo_name)
591 global_defaults = model.get_global_settings()
587 global_defaults = model.get_global_settings()
592
588
593 repo_defaults = {}
589 repo_defaults = {}
594 repo_defaults.update(global_defaults)
590 repo_defaults.update(global_defaults)
595 repo_defaults.update(model.get_repo_settings())
591 repo_defaults.update(model.get_repo_settings())
596
592
597 global_defaults = {
593 global_defaults = {
598 '{}_inherited'.format(k): global_defaults[k]
594 '{}_inherited'.format(k): global_defaults[k]
599 for k in global_defaults}
595 for k in global_defaults}
600
596
601 defaults = {
597 defaults = {
602 'inherit_global_settings': model.inherit_global_settings
598 'inherit_global_settings': model.inherit_global_settings
603 }
599 }
604 defaults.update(global_defaults)
600 defaults.update(global_defaults)
605 defaults.update(repo_defaults)
601 defaults.update(repo_defaults)
606 defaults.update({
602 defaults.update({
607 'new_svn_branch': '',
603 'new_svn_branch': '',
608 'new_svn_tag': '',
604 'new_svn_tag': '',
609 })
605 })
610 return defaults
606 return defaults
@@ -1,517 +1,517 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2017 RhodeCode GmbH
3 # Copyright (C) 2011-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 User Groups crud controller for pylons
22 User Groups crud controller for pylons
23 """
23 """
24
24
25 import logging
25 import logging
26 import formencode
26 import formencode
27
27
28 import peppercorn
28 import peppercorn
29 from formencode import htmlfill
29 from formencode import htmlfill
30 from pylons import request, tmpl_context as c, url, config
30 from pylons import request, tmpl_context as c, url, config
31 from pylons.controllers.util import redirect
31 from pylons.controllers.util import redirect
32 from pylons.i18n.translation import _
32 from pylons.i18n.translation import _
33
33
34 from sqlalchemy.orm import joinedload
34 from sqlalchemy.orm import joinedload
35
35
36 from rhodecode.lib import auth
36 from rhodecode.lib import auth
37 from rhodecode.lib import helpers as h
37 from rhodecode.lib import helpers as h
38 from rhodecode.lib.ext_json import json
38 from rhodecode.lib.exceptions import UserGroupAssignedException,\
39 from rhodecode.lib.exceptions import UserGroupAssignedException,\
39 RepoGroupAssignmentError
40 RepoGroupAssignmentError
40 from rhodecode.lib.utils import jsonify, action_logger
41 from rhodecode.lib.utils import jsonify, action_logger
41 from rhodecode.lib.utils2 import safe_unicode, str2bool, safe_int
42 from rhodecode.lib.utils2 import safe_unicode, str2bool, safe_int
42 from rhodecode.lib.auth import (
43 from rhodecode.lib.auth import (
43 LoginRequired, NotAnonymous, HasUserGroupPermissionAnyDecorator,
44 LoginRequired, NotAnonymous, HasUserGroupPermissionAnyDecorator,
44 HasPermissionAnyDecorator, XHRRequired)
45 HasPermissionAnyDecorator, XHRRequired)
45 from rhodecode.lib.base import BaseController, render
46 from rhodecode.lib.base import BaseController, render
46 from rhodecode.model.permission import PermissionModel
47 from rhodecode.model.permission import PermissionModel
47 from rhodecode.model.scm import UserGroupList
48 from rhodecode.model.scm import UserGroupList
48 from rhodecode.model.user_group import UserGroupModel
49 from rhodecode.model.user_group import UserGroupModel
49 from rhodecode.model.db import (
50 from rhodecode.model.db import (
50 User, UserGroup, UserGroupRepoToPerm, UserGroupRepoGroupToPerm)
51 User, UserGroup, UserGroupRepoToPerm, UserGroupRepoGroupToPerm)
51 from rhodecode.model.forms import (
52 from rhodecode.model.forms import (
52 UserGroupForm, UserGroupPermsForm, UserIndividualPermissionsForm,
53 UserGroupForm, UserGroupPermsForm, UserIndividualPermissionsForm,
53 UserPermissionsForm)
54 UserPermissionsForm)
54 from rhodecode.model.meta import Session
55 from rhodecode.model.meta import Session
55 from rhodecode.lib.utils import action_logger
56
56 from rhodecode.lib.ext_json import json
57
57
58 log = logging.getLogger(__name__)
58 log = logging.getLogger(__name__)
59
59
60
60
61 class UserGroupsController(BaseController):
61 class UserGroupsController(BaseController):
62 """REST Controller styled on the Atom Publishing Protocol"""
62 """REST Controller styled on the Atom Publishing Protocol"""
63
63
64 @LoginRequired()
64 @LoginRequired()
65 def __before__(self):
65 def __before__(self):
66 super(UserGroupsController, self).__before__()
66 super(UserGroupsController, self).__before__()
67 c.available_permissions = config['available_permissions']
67 c.available_permissions = config['available_permissions']
68 PermissionModel().set_global_permission_choices(c, gettext_translator=_)
68 PermissionModel().set_global_permission_choices(c, gettext_translator=_)
69
69
70 def __load_data(self, user_group_id):
70 def __load_data(self, user_group_id):
71 c.group_members_obj = [x.user for x in c.user_group.members]
71 c.group_members_obj = [x.user for x in c.user_group.members]
72 c.group_members_obj.sort(key=lambda u: u.username.lower())
72 c.group_members_obj.sort(key=lambda u: u.username.lower())
73 c.group_members = [(x.user_id, x.username) for x in c.group_members_obj]
73 c.group_members = [(x.user_id, x.username) for x in c.group_members_obj]
74
74
75 def __load_defaults(self, user_group_id):
75 def __load_defaults(self, user_group_id):
76 """
76 """
77 Load defaults settings for edit, and update
77 Load defaults settings for edit, and update
78
78
79 :param user_group_id:
79 :param user_group_id:
80 """
80 """
81 user_group = UserGroup.get_or_404(user_group_id)
81 user_group = UserGroup.get_or_404(user_group_id)
82 data = user_group.get_dict()
82 data = user_group.get_dict()
83 # fill owner
83 # fill owner
84 if user_group.user:
84 if user_group.user:
85 data.update({'user': user_group.user.username})
85 data.update({'user': user_group.user.username})
86 else:
86 else:
87 replacement_user = User.get_first_super_admin().username
87 replacement_user = User.get_first_super_admin().username
88 data.update({'user': replacement_user})
88 data.update({'user': replacement_user})
89 return data
89 return data
90
90
91 def _revoke_perms_on_yourself(self, form_result):
91 def _revoke_perms_on_yourself(self, form_result):
92 _updates = filter(lambda u: c.rhodecode_user.user_id == int(u[0]),
92 _updates = filter(lambda u: c.rhodecode_user.user_id == int(u[0]),
93 form_result['perm_updates'])
93 form_result['perm_updates'])
94 _additions = filter(lambda u: c.rhodecode_user.user_id == int(u[0]),
94 _additions = filter(lambda u: c.rhodecode_user.user_id == int(u[0]),
95 form_result['perm_additions'])
95 form_result['perm_additions'])
96 _deletions = filter(lambda u: c.rhodecode_user.user_id == int(u[0]),
96 _deletions = filter(lambda u: c.rhodecode_user.user_id == int(u[0]),
97 form_result['perm_deletions'])
97 form_result['perm_deletions'])
98 admin_perm = 'usergroup.admin'
98 admin_perm = 'usergroup.admin'
99 if _updates and _updates[0][1] != admin_perm or \
99 if _updates and _updates[0][1] != admin_perm or \
100 _additions and _additions[0][1] != admin_perm or \
100 _additions and _additions[0][1] != admin_perm or \
101 _deletions and _deletions[0][1] != admin_perm:
101 _deletions and _deletions[0][1] != admin_perm:
102 return True
102 return True
103 return False
103 return False
104
104
105 # permission check inside
105 # permission check inside
106 @NotAnonymous()
106 @NotAnonymous()
107 def index(self):
107 def index(self):
108 """GET /users_groups: All items in the collection"""
108 """GET /users_groups: All items in the collection"""
109 # url('users_groups')
109 # url('users_groups')
110
110
111 from rhodecode.lib.utils import PartialRenderer
111 from rhodecode.lib.utils import PartialRenderer
112 _render = PartialRenderer('data_table/_dt_elements.mako')
112 _render = PartialRenderer('data_table/_dt_elements.mako')
113
113
114 def user_group_name(user_group_id, user_group_name):
114 def user_group_name(user_group_id, user_group_name):
115 return _render("user_group_name", user_group_id, user_group_name)
115 return _render("user_group_name", user_group_id, user_group_name)
116
116
117 def user_group_actions(user_group_id, user_group_name):
117 def user_group_actions(user_group_id, user_group_name):
118 return _render("user_group_actions", user_group_id, user_group_name)
118 return _render("user_group_actions", user_group_id, user_group_name)
119
119
120 # json generate
120 # json generate
121 group_iter = UserGroupList(UserGroup.query().all(),
121 group_iter = UserGroupList(UserGroup.query().all(),
122 perm_set=['usergroup.admin'])
122 perm_set=['usergroup.admin'])
123
123
124 user_groups_data = []
124 user_groups_data = []
125 for user_gr in group_iter:
125 for user_gr in group_iter:
126 user_groups_data.append({
126 user_groups_data.append({
127 "group_name": user_group_name(
127 "group_name": user_group_name(
128 user_gr.users_group_id, h.escape(user_gr.users_group_name)),
128 user_gr.users_group_id, h.escape(user_gr.users_group_name)),
129 "group_name_raw": user_gr.users_group_name,
129 "group_name_raw": user_gr.users_group_name,
130 "desc": h.escape(user_gr.user_group_description),
130 "desc": h.escape(user_gr.user_group_description),
131 "members": len(user_gr.members),
131 "members": len(user_gr.members),
132 "sync": user_gr.group_data.get('extern_type'),
132 "sync": user_gr.group_data.get('extern_type'),
133 "active": h.bool2icon(user_gr.users_group_active),
133 "active": h.bool2icon(user_gr.users_group_active),
134 "owner": h.escape(h.link_to_user(user_gr.user.username)),
134 "owner": h.escape(h.link_to_user(user_gr.user.username)),
135 "action": user_group_actions(
135 "action": user_group_actions(
136 user_gr.users_group_id, user_gr.users_group_name)
136 user_gr.users_group_id, user_gr.users_group_name)
137 })
137 })
138
138
139 c.data = json.dumps(user_groups_data)
139 c.data = json.dumps(user_groups_data)
140 return render('admin/user_groups/user_groups.mako')
140 return render('admin/user_groups/user_groups.mako')
141
141
142 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
142 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
143 @auth.CSRFRequired()
143 @auth.CSRFRequired()
144 def create(self):
144 def create(self):
145 """POST /users_groups: Create a new item"""
145 """POST /users_groups: Create a new item"""
146 # url('users_groups')
146 # url('users_groups')
147
147
148 users_group_form = UserGroupForm()()
148 users_group_form = UserGroupForm()()
149 try:
149 try:
150 form_result = users_group_form.to_python(dict(request.POST))
150 form_result = users_group_form.to_python(dict(request.POST))
151 user_group = UserGroupModel().create(
151 user_group = UserGroupModel().create(
152 name=form_result['users_group_name'],
152 name=form_result['users_group_name'],
153 description=form_result['user_group_description'],
153 description=form_result['user_group_description'],
154 owner=c.rhodecode_user.user_id,
154 owner=c.rhodecode_user.user_id,
155 active=form_result['users_group_active'])
155 active=form_result['users_group_active'])
156 Session().flush()
156 Session().flush()
157
157
158 user_group_name = form_result['users_group_name']
158 user_group_name = form_result['users_group_name']
159 action_logger(c.rhodecode_user,
159 action_logger(c.rhodecode_user,
160 'admin_created_users_group:%s' % user_group_name,
160 'admin_created_users_group:%s' % user_group_name,
161 None, self.ip_addr, self.sa)
161 None, self.ip_addr, self.sa)
162 user_group_link = h.link_to(h.escape(user_group_name),
162 user_group_link = h.link_to(h.escape(user_group_name),
163 url('edit_users_group',
163 url('edit_users_group',
164 user_group_id=user_group.users_group_id))
164 user_group_id=user_group.users_group_id))
165 h.flash(h.literal(_('Created user group %(user_group_link)s')
165 h.flash(h.literal(_('Created user group %(user_group_link)s')
166 % {'user_group_link': user_group_link}),
166 % {'user_group_link': user_group_link}),
167 category='success')
167 category='success')
168 Session().commit()
168 Session().commit()
169 except formencode.Invalid as errors:
169 except formencode.Invalid as errors:
170 return htmlfill.render(
170 return htmlfill.render(
171 render('admin/user_groups/user_group_add.mako'),
171 render('admin/user_groups/user_group_add.mako'),
172 defaults=errors.value,
172 defaults=errors.value,
173 errors=errors.error_dict or {},
173 errors=errors.error_dict or {},
174 prefix_error=False,
174 prefix_error=False,
175 encoding="UTF-8",
175 encoding="UTF-8",
176 force_defaults=False)
176 force_defaults=False)
177 except Exception:
177 except Exception:
178 log.exception("Exception creating user group")
178 log.exception("Exception creating user group")
179 h.flash(_('Error occurred during creation of user group %s') \
179 h.flash(_('Error occurred during creation of user group %s') \
180 % request.POST.get('users_group_name'), category='error')
180 % request.POST.get('users_group_name'), category='error')
181
181
182 return redirect(
182 return redirect(
183 url('edit_users_group', user_group_id=user_group.users_group_id))
183 url('edit_users_group', user_group_id=user_group.users_group_id))
184
184
185 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
185 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
186 def new(self):
186 def new(self):
187 """GET /user_groups/new: Form to create a new item"""
187 """GET /user_groups/new: Form to create a new item"""
188 # url('new_users_group')
188 # url('new_users_group')
189 return render('admin/user_groups/user_group_add.mako')
189 return render('admin/user_groups/user_group_add.mako')
190
190
191 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
191 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
192 @auth.CSRFRequired()
192 @auth.CSRFRequired()
193 def update(self, user_group_id):
193 def update(self, user_group_id):
194 """PUT /user_groups/user_group_id: Update an existing item"""
194 """PUT /user_groups/user_group_id: Update an existing item"""
195 # Forms posted to this method should contain a hidden field:
195 # Forms posted to this method should contain a hidden field:
196 # <input type="hidden" name="_method" value="PUT" />
196 # <input type="hidden" name="_method" value="PUT" />
197 # Or using helpers:
197 # Or using helpers:
198 # h.form(url('users_group', user_group_id=ID),
198 # h.form(url('users_group', user_group_id=ID),
199 # method='put')
199 # method='put')
200 # url('users_group', user_group_id=ID)
200 # url('users_group', user_group_id=ID)
201
201
202 user_group_id = safe_int(user_group_id)
202 user_group_id = safe_int(user_group_id)
203 c.user_group = UserGroup.get_or_404(user_group_id)
203 c.user_group = UserGroup.get_or_404(user_group_id)
204 c.active = 'settings'
204 c.active = 'settings'
205 self.__load_data(user_group_id)
205 self.__load_data(user_group_id)
206
206
207 users_group_form = UserGroupForm(
207 users_group_form = UserGroupForm(
208 edit=True, old_data=c.user_group.get_dict(), allow_disabled=True)()
208 edit=True, old_data=c.user_group.get_dict(), allow_disabled=True)()
209
209
210 try:
210 try:
211 form_result = users_group_form.to_python(request.POST)
211 form_result = users_group_form.to_python(request.POST)
212 pstruct = peppercorn.parse(request.POST.items())
212 pstruct = peppercorn.parse(request.POST.items())
213 form_result['users_group_members'] = pstruct['user_group_members']
213 form_result['users_group_members'] = pstruct['user_group_members']
214
214
215 UserGroupModel().update(c.user_group, form_result)
215 UserGroupModel().update(c.user_group, form_result)
216 updated_user_group = form_result['users_group_name']
216 updated_user_group = form_result['users_group_name']
217 action_logger(c.rhodecode_user,
217 action_logger(c.rhodecode_user,
218 'admin_updated_users_group:%s' % updated_user_group,
218 'admin_updated_users_group:%s' % updated_user_group,
219 None, self.ip_addr, self.sa)
219 None, self.ip_addr, self.sa)
220 h.flash(_('Updated user group %s') % updated_user_group,
220 h.flash(_('Updated user group %s') % updated_user_group,
221 category='success')
221 category='success')
222 Session().commit()
222 Session().commit()
223 except formencode.Invalid as errors:
223 except formencode.Invalid as errors:
224 defaults = errors.value
224 defaults = errors.value
225 e = errors.error_dict or {}
225 e = errors.error_dict or {}
226
226
227 return htmlfill.render(
227 return htmlfill.render(
228 render('admin/user_groups/user_group_edit.mako'),
228 render('admin/user_groups/user_group_edit.mako'),
229 defaults=defaults,
229 defaults=defaults,
230 errors=e,
230 errors=e,
231 prefix_error=False,
231 prefix_error=False,
232 encoding="UTF-8",
232 encoding="UTF-8",
233 force_defaults=False)
233 force_defaults=False)
234 except Exception:
234 except Exception:
235 log.exception("Exception during update of user group")
235 log.exception("Exception during update of user group")
236 h.flash(_('Error occurred during update of user group %s')
236 h.flash(_('Error occurred during update of user group %s')
237 % request.POST.get('users_group_name'), category='error')
237 % request.POST.get('users_group_name'), category='error')
238
238
239 return redirect(url('edit_users_group', user_group_id=user_group_id))
239 return redirect(url('edit_users_group', user_group_id=user_group_id))
240
240
241 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
241 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
242 @auth.CSRFRequired()
242 @auth.CSRFRequired()
243 def delete(self, user_group_id):
243 def delete(self, user_group_id):
244 """DELETE /user_groups/user_group_id: Delete an existing item"""
244 """DELETE /user_groups/user_group_id: 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('users_group', user_group_id=ID),
248 # h.form(url('users_group', user_group_id=ID),
249 # method='delete')
249 # method='delete')
250 # url('users_group', user_group_id=ID)
250 # url('users_group', user_group_id=ID)
251 user_group_id = safe_int(user_group_id)
251 user_group_id = safe_int(user_group_id)
252 c.user_group = UserGroup.get_or_404(user_group_id)
252 c.user_group = UserGroup.get_or_404(user_group_id)
253 force = str2bool(request.POST.get('force'))
253 force = str2bool(request.POST.get('force'))
254
254
255 try:
255 try:
256 UserGroupModel().delete(c.user_group, force=force)
256 UserGroupModel().delete(c.user_group, force=force)
257 Session().commit()
257 Session().commit()
258 h.flash(_('Successfully deleted user group'), category='success')
258 h.flash(_('Successfully deleted user group'), category='success')
259 except UserGroupAssignedException as e:
259 except UserGroupAssignedException as e:
260 h.flash(str(e), category='error')
260 h.flash(str(e), category='error')
261 except Exception:
261 except Exception:
262 log.exception("Exception during deletion of user group")
262 log.exception("Exception during deletion of user group")
263 h.flash(_('An error occurred during deletion of user group'),
263 h.flash(_('An error occurred during deletion of user group'),
264 category='error')
264 category='error')
265 return redirect(url('users_groups'))
265 return redirect(url('users_groups'))
266
266
267 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
267 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
268 def edit(self, user_group_id):
268 def edit(self, user_group_id):
269 """GET /user_groups/user_group_id/edit: Form to edit an existing item"""
269 """GET /user_groups/user_group_id/edit: Form to edit an existing item"""
270 # url('edit_users_group', user_group_id=ID)
270 # url('edit_users_group', user_group_id=ID)
271
271
272 user_group_id = safe_int(user_group_id)
272 user_group_id = safe_int(user_group_id)
273 c.user_group = UserGroup.get_or_404(user_group_id)
273 c.user_group = UserGroup.get_or_404(user_group_id)
274 c.active = 'settings'
274 c.active = 'settings'
275 self.__load_data(user_group_id)
275 self.__load_data(user_group_id)
276
276
277 defaults = self.__load_defaults(user_group_id)
277 defaults = self.__load_defaults(user_group_id)
278
278
279 return htmlfill.render(
279 return htmlfill.render(
280 render('admin/user_groups/user_group_edit.mako'),
280 render('admin/user_groups/user_group_edit.mako'),
281 defaults=defaults,
281 defaults=defaults,
282 encoding="UTF-8",
282 encoding="UTF-8",
283 force_defaults=False
283 force_defaults=False
284 )
284 )
285
285
286 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
286 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
287 def edit_perms(self, user_group_id):
287 def edit_perms(self, user_group_id):
288 user_group_id = safe_int(user_group_id)
288 user_group_id = safe_int(user_group_id)
289 c.user_group = UserGroup.get_or_404(user_group_id)
289 c.user_group = UserGroup.get_or_404(user_group_id)
290 c.active = 'perms'
290 c.active = 'perms'
291
291
292 defaults = {}
292 defaults = {}
293 # fill user group users
293 # fill user group users
294 for p in c.user_group.user_user_group_to_perm:
294 for p in c.user_group.user_user_group_to_perm:
295 defaults.update({'u_perm_%s' % p.user.user_id:
295 defaults.update({'u_perm_%s' % p.user.user_id:
296 p.permission.permission_name})
296 p.permission.permission_name})
297
297
298 for p in c.user_group.user_group_user_group_to_perm:
298 for p in c.user_group.user_group_user_group_to_perm:
299 defaults.update({'g_perm_%s' % p.user_group.users_group_id:
299 defaults.update({'g_perm_%s' % p.user_group.users_group_id:
300 p.permission.permission_name})
300 p.permission.permission_name})
301
301
302 return htmlfill.render(
302 return htmlfill.render(
303 render('admin/user_groups/user_group_edit.mako'),
303 render('admin/user_groups/user_group_edit.mako'),
304 defaults=defaults,
304 defaults=defaults,
305 encoding="UTF-8",
305 encoding="UTF-8",
306 force_defaults=False
306 force_defaults=False
307 )
307 )
308
308
309 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
309 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
310 @auth.CSRFRequired()
310 @auth.CSRFRequired()
311 def update_perms(self, user_group_id):
311 def update_perms(self, user_group_id):
312 """
312 """
313 grant permission for given usergroup
313 grant permission for given usergroup
314
314
315 :param user_group_id:
315 :param user_group_id:
316 """
316 """
317 user_group_id = safe_int(user_group_id)
317 user_group_id = safe_int(user_group_id)
318 c.user_group = UserGroup.get_or_404(user_group_id)
318 c.user_group = UserGroup.get_or_404(user_group_id)
319 form = UserGroupPermsForm()().to_python(request.POST)
319 form = UserGroupPermsForm()().to_python(request.POST)
320
320
321 if not c.rhodecode_user.is_admin:
321 if not c.rhodecode_user.is_admin:
322 if self._revoke_perms_on_yourself(form):
322 if self._revoke_perms_on_yourself(form):
323 msg = _('Cannot change permission for yourself as admin')
323 msg = _('Cannot change permission for yourself as admin')
324 h.flash(msg, category='warning')
324 h.flash(msg, category='warning')
325 return redirect(url('edit_user_group_perms', user_group_id=user_group_id))
325 return redirect(url('edit_user_group_perms', user_group_id=user_group_id))
326
326
327 try:
327 try:
328 UserGroupModel().update_permissions(user_group_id,
328 UserGroupModel().update_permissions(user_group_id,
329 form['perm_additions'], form['perm_updates'], form['perm_deletions'])
329 form['perm_additions'], form['perm_updates'], form['perm_deletions'])
330 except RepoGroupAssignmentError:
330 except RepoGroupAssignmentError:
331 h.flash(_('Target group cannot be the same'), category='error')
331 h.flash(_('Target group cannot be the same'), category='error')
332 return redirect(url('edit_user_group_perms', user_group_id=user_group_id))
332 return redirect(url('edit_user_group_perms', user_group_id=user_group_id))
333 #TODO: implement this
333 #TODO: implement this
334 #action_logger(c.rhodecode_user, 'admin_changed_repo_permissions',
334 #action_logger(c.rhodecode_user, 'admin_changed_repo_permissions',
335 # repo_name, self.ip_addr, self.sa)
335 # repo_name, self.ip_addr, self.sa)
336 Session().commit()
336 Session().commit()
337 h.flash(_('User Group permissions updated'), category='success')
337 h.flash(_('User Group permissions updated'), category='success')
338 return redirect(url('edit_user_group_perms', user_group_id=user_group_id))
338 return redirect(url('edit_user_group_perms', user_group_id=user_group_id))
339
339
340 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
340 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
341 def edit_perms_summary(self, user_group_id):
341 def edit_perms_summary(self, user_group_id):
342 user_group_id = safe_int(user_group_id)
342 user_group_id = safe_int(user_group_id)
343 c.user_group = UserGroup.get_or_404(user_group_id)
343 c.user_group = UserGroup.get_or_404(user_group_id)
344 c.active = 'perms_summary'
344 c.active = 'perms_summary'
345 permissions = {
345 permissions = {
346 'repositories': {},
346 'repositories': {},
347 'repositories_groups': {},
347 'repositories_groups': {},
348 }
348 }
349 ugroup_repo_perms = UserGroupRepoToPerm.query()\
349 ugroup_repo_perms = UserGroupRepoToPerm.query()\
350 .options(joinedload(UserGroupRepoToPerm.permission))\
350 .options(joinedload(UserGroupRepoToPerm.permission))\
351 .options(joinedload(UserGroupRepoToPerm.repository))\
351 .options(joinedload(UserGroupRepoToPerm.repository))\
352 .filter(UserGroupRepoToPerm.users_group_id == user_group_id)\
352 .filter(UserGroupRepoToPerm.users_group_id == user_group_id)\
353 .all()
353 .all()
354
354
355 for gr in ugroup_repo_perms:
355 for gr in ugroup_repo_perms:
356 permissions['repositories'][gr.repository.repo_name] \
356 permissions['repositories'][gr.repository.repo_name] \
357 = gr.permission.permission_name
357 = gr.permission.permission_name
358
358
359 ugroup_group_perms = UserGroupRepoGroupToPerm.query()\
359 ugroup_group_perms = UserGroupRepoGroupToPerm.query()\
360 .options(joinedload(UserGroupRepoGroupToPerm.permission))\
360 .options(joinedload(UserGroupRepoGroupToPerm.permission))\
361 .options(joinedload(UserGroupRepoGroupToPerm.group))\
361 .options(joinedload(UserGroupRepoGroupToPerm.group))\
362 .filter(UserGroupRepoGroupToPerm.users_group_id == user_group_id)\
362 .filter(UserGroupRepoGroupToPerm.users_group_id == user_group_id)\
363 .all()
363 .all()
364
364
365 for gr in ugroup_group_perms:
365 for gr in ugroup_group_perms:
366 permissions['repositories_groups'][gr.group.group_name] \
366 permissions['repositories_groups'][gr.group.group_name] \
367 = gr.permission.permission_name
367 = gr.permission.permission_name
368 c.permissions = permissions
368 c.permissions = permissions
369 return render('admin/user_groups/user_group_edit.mako')
369 return render('admin/user_groups/user_group_edit.mako')
370
370
371 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
371 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
372 def edit_global_perms(self, user_group_id):
372 def edit_global_perms(self, user_group_id):
373 user_group_id = safe_int(user_group_id)
373 user_group_id = safe_int(user_group_id)
374 c.user_group = UserGroup.get_or_404(user_group_id)
374 c.user_group = UserGroup.get_or_404(user_group_id)
375 c.active = 'global_perms'
375 c.active = 'global_perms'
376
376
377 c.default_user = User.get_default_user()
377 c.default_user = User.get_default_user()
378 defaults = c.user_group.get_dict()
378 defaults = c.user_group.get_dict()
379 defaults.update(c.default_user.get_default_perms(suffix='_inherited'))
379 defaults.update(c.default_user.get_default_perms(suffix='_inherited'))
380 defaults.update(c.user_group.get_default_perms())
380 defaults.update(c.user_group.get_default_perms())
381
381
382 return htmlfill.render(
382 return htmlfill.render(
383 render('admin/user_groups/user_group_edit.mako'),
383 render('admin/user_groups/user_group_edit.mako'),
384 defaults=defaults,
384 defaults=defaults,
385 encoding="UTF-8",
385 encoding="UTF-8",
386 force_defaults=False
386 force_defaults=False
387 )
387 )
388
388
389 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
389 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
390 @auth.CSRFRequired()
390 @auth.CSRFRequired()
391 def update_global_perms(self, user_group_id):
391 def update_global_perms(self, user_group_id):
392 """PUT /users_perm/user_group_id: Update an existing item"""
392 """PUT /users_perm/user_group_id: Update an existing item"""
393 # url('users_group_perm', user_group_id=ID, method='put')
393 # url('users_group_perm', user_group_id=ID, method='put')
394 user_group_id = safe_int(user_group_id)
394 user_group_id = safe_int(user_group_id)
395 user_group = UserGroup.get_or_404(user_group_id)
395 user_group = UserGroup.get_or_404(user_group_id)
396 c.active = 'global_perms'
396 c.active = 'global_perms'
397
397
398 try:
398 try:
399 # first stage that verifies the checkbox
399 # first stage that verifies the checkbox
400 _form = UserIndividualPermissionsForm()
400 _form = UserIndividualPermissionsForm()
401 form_result = _form.to_python(dict(request.POST))
401 form_result = _form.to_python(dict(request.POST))
402 inherit_perms = form_result['inherit_default_permissions']
402 inherit_perms = form_result['inherit_default_permissions']
403 user_group.inherit_default_permissions = inherit_perms
403 user_group.inherit_default_permissions = inherit_perms
404 Session().add(user_group)
404 Session().add(user_group)
405
405
406 if not inherit_perms:
406 if not inherit_perms:
407 # only update the individual ones if we un check the flag
407 # only update the individual ones if we un check the flag
408 _form = UserPermissionsForm(
408 _form = UserPermissionsForm(
409 [x[0] for x in c.repo_create_choices],
409 [x[0] for x in c.repo_create_choices],
410 [x[0] for x in c.repo_create_on_write_choices],
410 [x[0] for x in c.repo_create_on_write_choices],
411 [x[0] for x in c.repo_group_create_choices],
411 [x[0] for x in c.repo_group_create_choices],
412 [x[0] for x in c.user_group_create_choices],
412 [x[0] for x in c.user_group_create_choices],
413 [x[0] for x in c.fork_choices],
413 [x[0] for x in c.fork_choices],
414 [x[0] for x in c.inherit_default_permission_choices])()
414 [x[0] for x in c.inherit_default_permission_choices])()
415
415
416 form_result = _form.to_python(dict(request.POST))
416 form_result = _form.to_python(dict(request.POST))
417 form_result.update({'perm_user_group_id': user_group.users_group_id})
417 form_result.update({'perm_user_group_id': user_group.users_group_id})
418
418
419 PermissionModel().update_user_group_permissions(form_result)
419 PermissionModel().update_user_group_permissions(form_result)
420
420
421 Session().commit()
421 Session().commit()
422 h.flash(_('User Group global permissions updated successfully'),
422 h.flash(_('User Group global permissions updated successfully'),
423 category='success')
423 category='success')
424
424
425 except formencode.Invalid as errors:
425 except formencode.Invalid as errors:
426 defaults = errors.value
426 defaults = errors.value
427 c.user_group = user_group
427 c.user_group = user_group
428 return htmlfill.render(
428 return htmlfill.render(
429 render('admin/user_groups/user_group_edit.mako'),
429 render('admin/user_groups/user_group_edit.mako'),
430 defaults=defaults,
430 defaults=defaults,
431 errors=errors.error_dict or {},
431 errors=errors.error_dict or {},
432 prefix_error=False,
432 prefix_error=False,
433 encoding="UTF-8",
433 encoding="UTF-8",
434 force_defaults=False)
434 force_defaults=False)
435 except Exception:
435 except Exception:
436 log.exception("Exception during permissions saving")
436 log.exception("Exception during permissions saving")
437 h.flash(_('An error occurred during permissions saving'),
437 h.flash(_('An error occurred during permissions saving'),
438 category='error')
438 category='error')
439
439
440 return redirect(url('edit_user_group_global_perms', user_group_id=user_group_id))
440 return redirect(url('edit_user_group_global_perms', user_group_id=user_group_id))
441
441
442 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
442 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
443 def edit_advanced(self, user_group_id):
443 def edit_advanced(self, user_group_id):
444 user_group_id = safe_int(user_group_id)
444 user_group_id = safe_int(user_group_id)
445 c.user_group = UserGroup.get_or_404(user_group_id)
445 c.user_group = UserGroup.get_or_404(user_group_id)
446 c.active = 'advanced'
446 c.active = 'advanced'
447 c.group_members_obj = sorted(
447 c.group_members_obj = sorted(
448 (x.user for x in c.user_group.members),
448 (x.user for x in c.user_group.members),
449 key=lambda u: u.username.lower())
449 key=lambda u: u.username.lower())
450
450
451 c.group_to_repos = sorted(
451 c.group_to_repos = sorted(
452 (x.repository for x in c.user_group.users_group_repo_to_perm),
452 (x.repository for x in c.user_group.users_group_repo_to_perm),
453 key=lambda u: u.repo_name.lower())
453 key=lambda u: u.repo_name.lower())
454
454
455 c.group_to_repo_groups = sorted(
455 c.group_to_repo_groups = sorted(
456 (x.group for x in c.user_group.users_group_repo_group_to_perm),
456 (x.group for x in c.user_group.users_group_repo_group_to_perm),
457 key=lambda u: u.group_name.lower())
457 key=lambda u: u.group_name.lower())
458
458
459 return render('admin/user_groups/user_group_edit.mako')
459 return render('admin/user_groups/user_group_edit.mako')
460
460
461 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
461 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
462 def edit_advanced_set_synchronization(self, user_group_id):
462 def edit_advanced_set_synchronization(self, user_group_id):
463 user_group_id = safe_int(user_group_id)
463 user_group_id = safe_int(user_group_id)
464 user_group = UserGroup.get_or_404(user_group_id)
464 user_group = UserGroup.get_or_404(user_group_id)
465
465
466 existing = user_group.group_data.get('extern_type')
466 existing = user_group.group_data.get('extern_type')
467
467
468 if existing:
468 if existing:
469 new_state = user_group.group_data
469 new_state = user_group.group_data
470 new_state['extern_type'] = None
470 new_state['extern_type'] = None
471 else:
471 else:
472 new_state = user_group.group_data
472 new_state = user_group.group_data
473 new_state['extern_type'] = 'manual'
473 new_state['extern_type'] = 'manual'
474 new_state['extern_type_set_by'] = c.rhodecode_user.username
474 new_state['extern_type_set_by'] = c.rhodecode_user.username
475
475
476 try:
476 try:
477 user_group.group_data = new_state
477 user_group.group_data = new_state
478 Session().add(user_group)
478 Session().add(user_group)
479 Session().commit()
479 Session().commit()
480
480
481 h.flash(_('User Group synchronization updated successfully'),
481 h.flash(_('User Group synchronization updated successfully'),
482 category='success')
482 category='success')
483 except Exception:
483 except Exception:
484 log.exception("Exception during sync settings saving")
484 log.exception("Exception during sync settings saving")
485 h.flash(_('An error occurred during synchronization update'),
485 h.flash(_('An error occurred during synchronization update'),
486 category='error')
486 category='error')
487
487
488 return redirect(
488 return redirect(
489 url('edit_user_group_advanced', user_group_id=user_group_id))
489 url('edit_user_group_advanced', user_group_id=user_group_id))
490
490
491 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
491 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
492 @XHRRequired()
492 @XHRRequired()
493 @jsonify
493 @jsonify
494 def user_group_members(self, user_group_id):
494 def user_group_members(self, user_group_id):
495 user_group_id = safe_int(user_group_id)
495 user_group_id = safe_int(user_group_id)
496 user_group = UserGroup.get_or_404(user_group_id)
496 user_group = UserGroup.get_or_404(user_group_id)
497 group_members_obj = sorted((x.user for x in user_group.members),
497 group_members_obj = sorted((x.user for x in user_group.members),
498 key=lambda u: u.username.lower())
498 key=lambda u: u.username.lower())
499
499
500 group_members = [
500 group_members = [
501 {
501 {
502 'id': user.user_id,
502 'id': user.user_id,
503 'first_name': user.name,
503 'first_name': user.name,
504 'last_name': user.lastname,
504 'last_name': user.lastname,
505 'username': user.username,
505 'username': user.username,
506 'icon_link': h.gravatar_url(user.email, 30),
506 'icon_link': h.gravatar_url(user.email, 30),
507 'value_display': h.person(user.email),
507 'value_display': h.person(user.email),
508 'value': user.username,
508 'value': user.username,
509 'value_type': 'user',
509 'value_type': 'user',
510 'active': user.active,
510 'active': user.active,
511 }
511 }
512 for user in group_members_obj
512 for user in group_members_obj
513 ]
513 ]
514
514
515 return {
515 return {
516 'members': group_members
516 'members': group_members
517 }
517 }
@@ -1,485 +1,484 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 commit controller for RhodeCode showing changes between commits
22 commit controller for RhodeCode showing changes between commits
23 """
23 """
24
24
25 import logging
25 import logging
26
26
27 from collections import defaultdict
27 from collections import defaultdict
28 from webob.exc import HTTPForbidden, HTTPBadRequest, HTTPNotFound
28 from webob.exc import HTTPForbidden, HTTPBadRequest, HTTPNotFound
29
29
30 from pylons import tmpl_context as c, request, response
30 from pylons import tmpl_context as c, request, response
31 from pylons.i18n.translation import _
31 from pylons.i18n.translation import _
32 from pylons.controllers.util import redirect
32 from pylons.controllers.util import redirect
33
33
34 from rhodecode.lib import auth
34 from rhodecode.lib import auth
35 from rhodecode.lib import diffs, codeblocks
35 from rhodecode.lib import diffs, codeblocks
36 from rhodecode.lib.auth import (
36 from rhodecode.lib.auth import (
37 LoginRequired, HasRepoPermissionAnyDecorator, NotAnonymous)
37 LoginRequired, HasRepoPermissionAnyDecorator, NotAnonymous)
38 from rhodecode.lib.base import BaseRepoController, render
38 from rhodecode.lib.base import BaseRepoController, render
39 from rhodecode.lib.compat import OrderedDict
39 from rhodecode.lib.compat import OrderedDict
40 from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError
40 from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError
41 import rhodecode.lib.helpers as h
41 import rhodecode.lib.helpers as h
42 from rhodecode.lib.utils import action_logger, jsonify
42 from rhodecode.lib.utils import jsonify
43 from rhodecode.lib.utils2 import safe_unicode
43 from rhodecode.lib.utils2 import safe_unicode
44 from rhodecode.lib.vcs.backends.base import EmptyCommit
44 from rhodecode.lib.vcs.backends.base import EmptyCommit
45 from rhodecode.lib.vcs.exceptions import (
45 from rhodecode.lib.vcs.exceptions import (
46 RepositoryError, CommitDoesNotExistError, NodeDoesNotExistError)
46 RepositoryError, CommitDoesNotExistError, NodeDoesNotExistError)
47 from rhodecode.model.db import ChangesetComment, ChangesetStatus
47 from rhodecode.model.db import ChangesetComment, ChangesetStatus
48 from rhodecode.model.changeset_status import ChangesetStatusModel
48 from rhodecode.model.changeset_status import ChangesetStatusModel
49 from rhodecode.model.comment import CommentsModel
49 from rhodecode.model.comment import CommentsModel
50 from rhodecode.model.meta import Session
50 from rhodecode.model.meta import Session
51 from rhodecode.model.repo import RepoModel
52
51
53
52
54 log = logging.getLogger(__name__)
53 log = logging.getLogger(__name__)
55
54
56
55
57 def _update_with_GET(params, GET):
56 def _update_with_GET(params, GET):
58 for k in ['diff1', 'diff2', 'diff']:
57 for k in ['diff1', 'diff2', 'diff']:
59 params[k] += GET.getall(k)
58 params[k] += GET.getall(k)
60
59
61
60
62 def get_ignore_ws(fid, GET):
61 def get_ignore_ws(fid, GET):
63 ig_ws_global = GET.get('ignorews')
62 ig_ws_global = GET.get('ignorews')
64 ig_ws = filter(lambda k: k.startswith('WS'), GET.getall(fid))
63 ig_ws = filter(lambda k: k.startswith('WS'), GET.getall(fid))
65 if ig_ws:
64 if ig_ws:
66 try:
65 try:
67 return int(ig_ws[0].split(':')[-1])
66 return int(ig_ws[0].split(':')[-1])
68 except Exception:
67 except Exception:
69 pass
68 pass
70 return ig_ws_global
69 return ig_ws_global
71
70
72
71
73 def _ignorews_url(GET, fileid=None):
72 def _ignorews_url(GET, fileid=None):
74 fileid = str(fileid) if fileid else None
73 fileid = str(fileid) if fileid else None
75 params = defaultdict(list)
74 params = defaultdict(list)
76 _update_with_GET(params, GET)
75 _update_with_GET(params, GET)
77 label = _('Show whitespace')
76 label = _('Show whitespace')
78 tooltiplbl = _('Show whitespace for all diffs')
77 tooltiplbl = _('Show whitespace for all diffs')
79 ig_ws = get_ignore_ws(fileid, GET)
78 ig_ws = get_ignore_ws(fileid, GET)
80 ln_ctx = get_line_ctx(fileid, GET)
79 ln_ctx = get_line_ctx(fileid, GET)
81
80
82 if ig_ws is None:
81 if ig_ws is None:
83 params['ignorews'] += [1]
82 params['ignorews'] += [1]
84 label = _('Ignore whitespace')
83 label = _('Ignore whitespace')
85 tooltiplbl = _('Ignore whitespace for all diffs')
84 tooltiplbl = _('Ignore whitespace for all diffs')
86 ctx_key = 'context'
85 ctx_key = 'context'
87 ctx_val = ln_ctx
86 ctx_val = ln_ctx
88
87
89 # if we have passed in ln_ctx pass it along to our params
88 # if we have passed in ln_ctx pass it along to our params
90 if ln_ctx:
89 if ln_ctx:
91 params[ctx_key] += [ctx_val]
90 params[ctx_key] += [ctx_val]
92
91
93 if fileid:
92 if fileid:
94 params['anchor'] = 'a_' + fileid
93 params['anchor'] = 'a_' + fileid
95 return h.link_to(label, h.url.current(**params), title=tooltiplbl, class_='tooltip')
94 return h.link_to(label, h.url.current(**params), title=tooltiplbl, class_='tooltip')
96
95
97
96
98 def get_line_ctx(fid, GET):
97 def get_line_ctx(fid, GET):
99 ln_ctx_global = GET.get('context')
98 ln_ctx_global = GET.get('context')
100 if fid:
99 if fid:
101 ln_ctx = filter(lambda k: k.startswith('C'), GET.getall(fid))
100 ln_ctx = filter(lambda k: k.startswith('C'), GET.getall(fid))
102 else:
101 else:
103 _ln_ctx = filter(lambda k: k.startswith('C'), GET)
102 _ln_ctx = filter(lambda k: k.startswith('C'), GET)
104 ln_ctx = GET.get(_ln_ctx[0]) if _ln_ctx else ln_ctx_global
103 ln_ctx = GET.get(_ln_ctx[0]) if _ln_ctx else ln_ctx_global
105 if ln_ctx:
104 if ln_ctx:
106 ln_ctx = [ln_ctx]
105 ln_ctx = [ln_ctx]
107
106
108 if ln_ctx:
107 if ln_ctx:
109 retval = ln_ctx[0].split(':')[-1]
108 retval = ln_ctx[0].split(':')[-1]
110 else:
109 else:
111 retval = ln_ctx_global
110 retval = ln_ctx_global
112
111
113 try:
112 try:
114 return int(retval)
113 return int(retval)
115 except Exception:
114 except Exception:
116 return 3
115 return 3
117
116
118
117
119 def _context_url(GET, fileid=None):
118 def _context_url(GET, fileid=None):
120 """
119 """
121 Generates a url for context lines.
120 Generates a url for context lines.
122
121
123 :param fileid:
122 :param fileid:
124 """
123 """
125
124
126 fileid = str(fileid) if fileid else None
125 fileid = str(fileid) if fileid else None
127 ig_ws = get_ignore_ws(fileid, GET)
126 ig_ws = get_ignore_ws(fileid, GET)
128 ln_ctx = (get_line_ctx(fileid, GET) or 3) * 2
127 ln_ctx = (get_line_ctx(fileid, GET) or 3) * 2
129
128
130 params = defaultdict(list)
129 params = defaultdict(list)
131 _update_with_GET(params, GET)
130 _update_with_GET(params, GET)
132
131
133 if ln_ctx > 0:
132 if ln_ctx > 0:
134 params['context'] += [ln_ctx]
133 params['context'] += [ln_ctx]
135
134
136 if ig_ws:
135 if ig_ws:
137 ig_ws_key = 'ignorews'
136 ig_ws_key = 'ignorews'
138 ig_ws_val = 1
137 ig_ws_val = 1
139 params[ig_ws_key] += [ig_ws_val]
138 params[ig_ws_key] += [ig_ws_val]
140
139
141 lbl = _('Increase context')
140 lbl = _('Increase context')
142 tooltiplbl = _('Increase context for all diffs')
141 tooltiplbl = _('Increase context for all diffs')
143
142
144 if fileid:
143 if fileid:
145 params['anchor'] = 'a_' + fileid
144 params['anchor'] = 'a_' + fileid
146 return h.link_to(lbl, h.url.current(**params), title=tooltiplbl, class_='tooltip')
145 return h.link_to(lbl, h.url.current(**params), title=tooltiplbl, class_='tooltip')
147
146
148
147
149 class ChangesetController(BaseRepoController):
148 class ChangesetController(BaseRepoController):
150
149
151 def __before__(self):
150 def __before__(self):
152 super(ChangesetController, self).__before__()
151 super(ChangesetController, self).__before__()
153 c.affected_files_cut_off = 60
152 c.affected_files_cut_off = 60
154
153
155 def _index(self, commit_id_range, method):
154 def _index(self, commit_id_range, method):
156 c.ignorews_url = _ignorews_url
155 c.ignorews_url = _ignorews_url
157 c.context_url = _context_url
156 c.context_url = _context_url
158 c.fulldiff = fulldiff = request.GET.get('fulldiff')
157 c.fulldiff = fulldiff = request.GET.get('fulldiff')
159
158
160 # fetch global flags of ignore ws or context lines
159 # fetch global flags of ignore ws or context lines
161 context_lcl = get_line_ctx('', request.GET)
160 context_lcl = get_line_ctx('', request.GET)
162 ign_whitespace_lcl = get_ignore_ws('', request.GET)
161 ign_whitespace_lcl = get_ignore_ws('', request.GET)
163
162
164 # diff_limit will cut off the whole diff if the limit is applied
163 # diff_limit will cut off the whole diff if the limit is applied
165 # otherwise it will just hide the big files from the front-end
164 # otherwise it will just hide the big files from the front-end
166 diff_limit = self.cut_off_limit_diff
165 diff_limit = self.cut_off_limit_diff
167 file_limit = self.cut_off_limit_file
166 file_limit = self.cut_off_limit_file
168
167
169 # get ranges of commit ids if preset
168 # get ranges of commit ids if preset
170 commit_range = commit_id_range.split('...')[:2]
169 commit_range = commit_id_range.split('...')[:2]
171
170
172 try:
171 try:
173 pre_load = ['affected_files', 'author', 'branch', 'date',
172 pre_load = ['affected_files', 'author', 'branch', 'date',
174 'message', 'parents']
173 'message', 'parents']
175
174
176 if len(commit_range) == 2:
175 if len(commit_range) == 2:
177 commits = c.rhodecode_repo.get_commits(
176 commits = c.rhodecode_repo.get_commits(
178 start_id=commit_range[0], end_id=commit_range[1],
177 start_id=commit_range[0], end_id=commit_range[1],
179 pre_load=pre_load)
178 pre_load=pre_load)
180 commits = list(commits)
179 commits = list(commits)
181 else:
180 else:
182 commits = [c.rhodecode_repo.get_commit(
181 commits = [c.rhodecode_repo.get_commit(
183 commit_id=commit_id_range, pre_load=pre_load)]
182 commit_id=commit_id_range, pre_load=pre_load)]
184
183
185 c.commit_ranges = commits
184 c.commit_ranges = commits
186 if not c.commit_ranges:
185 if not c.commit_ranges:
187 raise RepositoryError(
186 raise RepositoryError(
188 'The commit range returned an empty result')
187 'The commit range returned an empty result')
189 except CommitDoesNotExistError:
188 except CommitDoesNotExistError:
190 msg = _('No such commit exists for this repository')
189 msg = _('No such commit exists for this repository')
191 h.flash(msg, category='error')
190 h.flash(msg, category='error')
192 raise HTTPNotFound()
191 raise HTTPNotFound()
193 except Exception:
192 except Exception:
194 log.exception("General failure")
193 log.exception("General failure")
195 raise HTTPNotFound()
194 raise HTTPNotFound()
196
195
197 c.changes = OrderedDict()
196 c.changes = OrderedDict()
198 c.lines_added = 0
197 c.lines_added = 0
199 c.lines_deleted = 0
198 c.lines_deleted = 0
200
199
201 # auto collapse if we have more than limit
200 # auto collapse if we have more than limit
202 collapse_limit = diffs.DiffProcessor._collapse_commits_over
201 collapse_limit = diffs.DiffProcessor._collapse_commits_over
203 c.collapse_all_commits = len(c.commit_ranges) > collapse_limit
202 c.collapse_all_commits = len(c.commit_ranges) > collapse_limit
204
203
205 c.commit_statuses = ChangesetStatus.STATUSES
204 c.commit_statuses = ChangesetStatus.STATUSES
206 c.inline_comments = []
205 c.inline_comments = []
207 c.files = []
206 c.files = []
208
207
209 c.statuses = []
208 c.statuses = []
210 c.comments = []
209 c.comments = []
211 c.unresolved_comments = []
210 c.unresolved_comments = []
212 if len(c.commit_ranges) == 1:
211 if len(c.commit_ranges) == 1:
213 commit = c.commit_ranges[0]
212 commit = c.commit_ranges[0]
214 c.comments = CommentsModel().get_comments(
213 c.comments = CommentsModel().get_comments(
215 c.rhodecode_db_repo.repo_id,
214 c.rhodecode_db_repo.repo_id,
216 revision=commit.raw_id)
215 revision=commit.raw_id)
217 c.statuses.append(ChangesetStatusModel().get_status(
216 c.statuses.append(ChangesetStatusModel().get_status(
218 c.rhodecode_db_repo.repo_id, commit.raw_id))
217 c.rhodecode_db_repo.repo_id, commit.raw_id))
219 # comments from PR
218 # comments from PR
220 statuses = ChangesetStatusModel().get_statuses(
219 statuses = ChangesetStatusModel().get_statuses(
221 c.rhodecode_db_repo.repo_id, commit.raw_id,
220 c.rhodecode_db_repo.repo_id, commit.raw_id,
222 with_revisions=True)
221 with_revisions=True)
223 prs = set(st.pull_request for st in statuses
222 prs = set(st.pull_request for st in statuses
224 if st.pull_request is not None)
223 if st.pull_request is not None)
225 # from associated statuses, check the pull requests, and
224 # from associated statuses, check the pull requests, and
226 # show comments from them
225 # show comments from them
227 for pr in prs:
226 for pr in prs:
228 c.comments.extend(pr.comments)
227 c.comments.extend(pr.comments)
229
228
230 c.unresolved_comments = CommentsModel()\
229 c.unresolved_comments = CommentsModel()\
231 .get_commit_unresolved_todos(commit.raw_id)
230 .get_commit_unresolved_todos(commit.raw_id)
232
231
233 # Iterate over ranges (default commit view is always one commit)
232 # Iterate over ranges (default commit view is always one commit)
234 for commit in c.commit_ranges:
233 for commit in c.commit_ranges:
235 c.changes[commit.raw_id] = []
234 c.changes[commit.raw_id] = []
236
235
237 commit2 = commit
236 commit2 = commit
238 commit1 = commit.parents[0] if commit.parents else EmptyCommit()
237 commit1 = commit.parents[0] if commit.parents else EmptyCommit()
239
238
240 _diff = c.rhodecode_repo.get_diff(
239 _diff = c.rhodecode_repo.get_diff(
241 commit1, commit2,
240 commit1, commit2,
242 ignore_whitespace=ign_whitespace_lcl, context=context_lcl)
241 ignore_whitespace=ign_whitespace_lcl, context=context_lcl)
243 diff_processor = diffs.DiffProcessor(
242 diff_processor = diffs.DiffProcessor(
244 _diff, format='newdiff', diff_limit=diff_limit,
243 _diff, format='newdiff', diff_limit=diff_limit,
245 file_limit=file_limit, show_full_diff=fulldiff)
244 file_limit=file_limit, show_full_diff=fulldiff)
246
245
247 commit_changes = OrderedDict()
246 commit_changes = OrderedDict()
248 if method == 'show':
247 if method == 'show':
249 _parsed = diff_processor.prepare()
248 _parsed = diff_processor.prepare()
250 c.limited_diff = isinstance(_parsed, diffs.LimitedDiffContainer)
249 c.limited_diff = isinstance(_parsed, diffs.LimitedDiffContainer)
251
250
252 _parsed = diff_processor.prepare()
251 _parsed = diff_processor.prepare()
253
252
254 def _node_getter(commit):
253 def _node_getter(commit):
255 def get_node(fname):
254 def get_node(fname):
256 try:
255 try:
257 return commit.get_node(fname)
256 return commit.get_node(fname)
258 except NodeDoesNotExistError:
257 except NodeDoesNotExistError:
259 return None
258 return None
260 return get_node
259 return get_node
261
260
262 inline_comments = CommentsModel().get_inline_comments(
261 inline_comments = CommentsModel().get_inline_comments(
263 c.rhodecode_db_repo.repo_id, revision=commit.raw_id)
262 c.rhodecode_db_repo.repo_id, revision=commit.raw_id)
264 c.inline_cnt = CommentsModel().get_inline_comments_count(
263 c.inline_cnt = CommentsModel().get_inline_comments_count(
265 inline_comments)
264 inline_comments)
266
265
267 diffset = codeblocks.DiffSet(
266 diffset = codeblocks.DiffSet(
268 repo_name=c.repo_name,
267 repo_name=c.repo_name,
269 source_node_getter=_node_getter(commit1),
268 source_node_getter=_node_getter(commit1),
270 target_node_getter=_node_getter(commit2),
269 target_node_getter=_node_getter(commit2),
271 comments=inline_comments
270 comments=inline_comments
272 ).render_patchset(_parsed, commit1.raw_id, commit2.raw_id)
271 ).render_patchset(_parsed, commit1.raw_id, commit2.raw_id)
273 c.changes[commit.raw_id] = diffset
272 c.changes[commit.raw_id] = diffset
274 else:
273 else:
275 # downloads/raw we only need RAW diff nothing else
274 # downloads/raw we only need RAW diff nothing else
276 diff = diff_processor.as_raw()
275 diff = diff_processor.as_raw()
277 c.changes[commit.raw_id] = [None, None, None, None, diff, None, None]
276 c.changes[commit.raw_id] = [None, None, None, None, diff, None, None]
278
277
279 # sort comments by how they were generated
278 # sort comments by how they were generated
280 c.comments = sorted(c.comments, key=lambda x: x.comment_id)
279 c.comments = sorted(c.comments, key=lambda x: x.comment_id)
281
280
282 if len(c.commit_ranges) == 1:
281 if len(c.commit_ranges) == 1:
283 c.commit = c.commit_ranges[0]
282 c.commit = c.commit_ranges[0]
284 c.parent_tmpl = ''.join(
283 c.parent_tmpl = ''.join(
285 '# Parent %s\n' % x.raw_id for x in c.commit.parents)
284 '# Parent %s\n' % x.raw_id for x in c.commit.parents)
286 if method == 'download':
285 if method == 'download':
287 response.content_type = 'text/plain'
286 response.content_type = 'text/plain'
288 response.content_disposition = (
287 response.content_disposition = (
289 'attachment; filename=%s.diff' % commit_id_range[:12])
288 'attachment; filename=%s.diff' % commit_id_range[:12])
290 return diff
289 return diff
291 elif method == 'patch':
290 elif method == 'patch':
292 response.content_type = 'text/plain'
291 response.content_type = 'text/plain'
293 c.diff = safe_unicode(diff)
292 c.diff = safe_unicode(diff)
294 return render('changeset/patch_changeset.mako')
293 return render('changeset/patch_changeset.mako')
295 elif method == 'raw':
294 elif method == 'raw':
296 response.content_type = 'text/plain'
295 response.content_type = 'text/plain'
297 return diff
296 return diff
298 elif method == 'show':
297 elif method == 'show':
299 if len(c.commit_ranges) == 1:
298 if len(c.commit_ranges) == 1:
300 return render('changeset/changeset.mako')
299 return render('changeset/changeset.mako')
301 else:
300 else:
302 c.ancestor = None
301 c.ancestor = None
303 c.target_repo = c.rhodecode_db_repo
302 c.target_repo = c.rhodecode_db_repo
304 return render('changeset/changeset_range.mako')
303 return render('changeset/changeset_range.mako')
305
304
306 @LoginRequired()
305 @LoginRequired()
307 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
306 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
308 'repository.admin')
307 'repository.admin')
309 def index(self, revision, method='show'):
308 def index(self, revision, method='show'):
310 return self._index(revision, method=method)
309 return self._index(revision, method=method)
311
310
312 @LoginRequired()
311 @LoginRequired()
313 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
312 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
314 'repository.admin')
313 'repository.admin')
315 def changeset_raw(self, revision):
314 def changeset_raw(self, revision):
316 return self._index(revision, method='raw')
315 return self._index(revision, method='raw')
317
316
318 @LoginRequired()
317 @LoginRequired()
319 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
318 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
320 'repository.admin')
319 'repository.admin')
321 def changeset_patch(self, revision):
320 def changeset_patch(self, revision):
322 return self._index(revision, method='patch')
321 return self._index(revision, method='patch')
323
322
324 @LoginRequired()
323 @LoginRequired()
325 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
324 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
326 'repository.admin')
325 'repository.admin')
327 def changeset_download(self, revision):
326 def changeset_download(self, revision):
328 return self._index(revision, method='download')
327 return self._index(revision, method='download')
329
328
330 @LoginRequired()
329 @LoginRequired()
331 @NotAnonymous()
330 @NotAnonymous()
332 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
331 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
333 'repository.admin')
332 'repository.admin')
334 @auth.CSRFRequired()
333 @auth.CSRFRequired()
335 @jsonify
334 @jsonify
336 def comment(self, repo_name, revision):
335 def comment(self, repo_name, revision):
337 commit_id = revision
336 commit_id = revision
338 status = request.POST.get('changeset_status', None)
337 status = request.POST.get('changeset_status', None)
339 text = request.POST.get('text')
338 text = request.POST.get('text')
340 comment_type = request.POST.get('comment_type')
339 comment_type = request.POST.get('comment_type')
341 resolves_comment_id = request.POST.get('resolves_comment_id', None)
340 resolves_comment_id = request.POST.get('resolves_comment_id', None)
342
341
343 if status:
342 if status:
344 text = text or (_('Status change %(transition_icon)s %(status)s')
343 text = text or (_('Status change %(transition_icon)s %(status)s')
345 % {'transition_icon': '>',
344 % {'transition_icon': '>',
346 'status': ChangesetStatus.get_status_lbl(status)})
345 'status': ChangesetStatus.get_status_lbl(status)})
347
346
348 multi_commit_ids = []
347 multi_commit_ids = []
349 for _commit_id in request.POST.get('commit_ids', '').split(','):
348 for _commit_id in request.POST.get('commit_ids', '').split(','):
350 if _commit_id not in ['', None, EmptyCommit.raw_id]:
349 if _commit_id not in ['', None, EmptyCommit.raw_id]:
351 if _commit_id not in multi_commit_ids:
350 if _commit_id not in multi_commit_ids:
352 multi_commit_ids.append(_commit_id)
351 multi_commit_ids.append(_commit_id)
353
352
354 commit_ids = multi_commit_ids or [commit_id]
353 commit_ids = multi_commit_ids or [commit_id]
355
354
356 comment = None
355 comment = None
357 for current_id in filter(None, commit_ids):
356 for current_id in filter(None, commit_ids):
358 c.co = comment = CommentsModel().create(
357 c.co = comment = CommentsModel().create(
359 text=text,
358 text=text,
360 repo=c.rhodecode_db_repo.repo_id,
359 repo=c.rhodecode_db_repo.repo_id,
361 user=c.rhodecode_user.user_id,
360 user=c.rhodecode_user.user_id,
362 commit_id=current_id,
361 commit_id=current_id,
363 f_path=request.POST.get('f_path'),
362 f_path=request.POST.get('f_path'),
364 line_no=request.POST.get('line'),
363 line_no=request.POST.get('line'),
365 status_change=(ChangesetStatus.get_status_lbl(status)
364 status_change=(ChangesetStatus.get_status_lbl(status)
366 if status else None),
365 if status else None),
367 status_change_type=status,
366 status_change_type=status,
368 comment_type=comment_type,
367 comment_type=comment_type,
369 resolves_comment_id=resolves_comment_id
368 resolves_comment_id=resolves_comment_id
370 )
369 )
371
370
372 # get status if set !
371 # get status if set !
373 if status:
372 if status:
374 # if latest status was from pull request and it's closed
373 # if latest status was from pull request and it's closed
375 # disallow changing status !
374 # disallow changing status !
376 # dont_allow_on_closed_pull_request = True !
375 # dont_allow_on_closed_pull_request = True !
377
376
378 try:
377 try:
379 ChangesetStatusModel().set_status(
378 ChangesetStatusModel().set_status(
380 c.rhodecode_db_repo.repo_id,
379 c.rhodecode_db_repo.repo_id,
381 status,
380 status,
382 c.rhodecode_user.user_id,
381 c.rhodecode_user.user_id,
383 comment,
382 comment,
384 revision=current_id,
383 revision=current_id,
385 dont_allow_on_closed_pull_request=True
384 dont_allow_on_closed_pull_request=True
386 )
385 )
387 except StatusChangeOnClosedPullRequestError:
386 except StatusChangeOnClosedPullRequestError:
388 msg = _('Changing the status of a commit associated with '
387 msg = _('Changing the status of a commit associated with '
389 'a closed pull request is not allowed')
388 'a closed pull request is not allowed')
390 log.exception(msg)
389 log.exception(msg)
391 h.flash(msg, category='warning')
390 h.flash(msg, category='warning')
392 return redirect(h.url(
391 return redirect(h.url(
393 'changeset_home', repo_name=repo_name,
392 'changeset_home', repo_name=repo_name,
394 revision=current_id))
393 revision=current_id))
395
394
396 # finalize, commit and redirect
395 # finalize, commit and redirect
397 Session().commit()
396 Session().commit()
398
397
399 data = {
398 data = {
400 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
399 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
401 }
400 }
402 if comment:
401 if comment:
403 data.update(comment.get_dict())
402 data.update(comment.get_dict())
404 data.update({'rendered_text':
403 data.update({'rendered_text':
405 render('changeset/changeset_comment_block.mako')})
404 render('changeset/changeset_comment_block.mako')})
406
405
407 return data
406 return data
408
407
409 @LoginRequired()
408 @LoginRequired()
410 @NotAnonymous()
409 @NotAnonymous()
411 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
410 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
412 'repository.admin')
411 'repository.admin')
413 @auth.CSRFRequired()
412 @auth.CSRFRequired()
414 def preview_comment(self):
413 def preview_comment(self):
415 # Technically a CSRF token is not needed as no state changes with this
414 # Technically a CSRF token is not needed as no state changes with this
416 # call. However, as this is a POST is better to have it, so automated
415 # call. However, as this is a POST is better to have it, so automated
417 # tools don't flag it as potential CSRF.
416 # tools don't flag it as potential CSRF.
418 # Post is required because the payload could be bigger than the maximum
417 # Post is required because the payload could be bigger than the maximum
419 # allowed by GET.
418 # allowed by GET.
420 if not request.environ.get('HTTP_X_PARTIAL_XHR'):
419 if not request.environ.get('HTTP_X_PARTIAL_XHR'):
421 raise HTTPBadRequest()
420 raise HTTPBadRequest()
422 text = request.POST.get('text')
421 text = request.POST.get('text')
423 renderer = request.POST.get('renderer') or 'rst'
422 renderer = request.POST.get('renderer') or 'rst'
424 if text:
423 if text:
425 return h.render(text, renderer=renderer, mentions=True)
424 return h.render(text, renderer=renderer, mentions=True)
426 return ''
425 return ''
427
426
428 @LoginRequired()
427 @LoginRequired()
429 @NotAnonymous()
428 @NotAnonymous()
430 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
429 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
431 'repository.admin')
430 'repository.admin')
432 @auth.CSRFRequired()
431 @auth.CSRFRequired()
433 @jsonify
432 @jsonify
434 def delete_comment(self, repo_name, comment_id):
433 def delete_comment(self, repo_name, comment_id):
435 comment = ChangesetComment.get(comment_id)
434 comment = ChangesetComment.get(comment_id)
436 if not comment:
435 if not comment:
437 log.debug('Comment with id:%s not found, skipping', comment_id)
436 log.debug('Comment with id:%s not found, skipping', comment_id)
438 # comment already deleted in another call probably
437 # comment already deleted in another call probably
439 return True
438 return True
440
439
441 owner = (comment.author.user_id == c.rhodecode_user.user_id)
440 owner = (comment.author.user_id == c.rhodecode_user.user_id)
442 is_repo_admin = h.HasRepoPermissionAny('repository.admin')(c.repo_name)
441 is_repo_admin = h.HasRepoPermissionAny('repository.admin')(c.repo_name)
443 if h.HasPermissionAny('hg.admin')() or is_repo_admin or owner:
442 if h.HasPermissionAny('hg.admin')() or is_repo_admin or owner:
444 CommentsModel().delete(comment=comment)
443 CommentsModel().delete(comment=comment)
445 Session().commit()
444 Session().commit()
446 return True
445 return True
447 else:
446 else:
448 raise HTTPForbidden()
447 raise HTTPForbidden()
449
448
450 @LoginRequired()
449 @LoginRequired()
451 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
450 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
452 'repository.admin')
451 'repository.admin')
453 @jsonify
452 @jsonify
454 def changeset_info(self, repo_name, revision):
453 def changeset_info(self, repo_name, revision):
455 if request.is_xhr:
454 if request.is_xhr:
456 try:
455 try:
457 return c.rhodecode_repo.get_commit(commit_id=revision)
456 return c.rhodecode_repo.get_commit(commit_id=revision)
458 except CommitDoesNotExistError as e:
457 except CommitDoesNotExistError as e:
459 return EmptyCommit(message=str(e))
458 return EmptyCommit(message=str(e))
460 else:
459 else:
461 raise HTTPBadRequest()
460 raise HTTPBadRequest()
462
461
463 @LoginRequired()
462 @LoginRequired()
464 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
463 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
465 'repository.admin')
464 'repository.admin')
466 @jsonify
465 @jsonify
467 def changeset_children(self, repo_name, revision):
466 def changeset_children(self, repo_name, revision):
468 if request.is_xhr:
467 if request.is_xhr:
469 commit = c.rhodecode_repo.get_commit(commit_id=revision)
468 commit = c.rhodecode_repo.get_commit(commit_id=revision)
470 result = {"results": commit.children}
469 result = {"results": commit.children}
471 return result
470 return result
472 else:
471 else:
473 raise HTTPBadRequest()
472 raise HTTPBadRequest()
474
473
475 @LoginRequired()
474 @LoginRequired()
476 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
475 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
477 'repository.admin')
476 'repository.admin')
478 @jsonify
477 @jsonify
479 def changeset_parents(self, repo_name, revision):
478 def changeset_parents(self, repo_name, revision):
480 if request.is_xhr:
479 if request.is_xhr:
481 commit = c.rhodecode_repo.get_commit(commit_id=revision)
480 commit = c.rhodecode_repo.get_commit(commit_id=revision)
482 result = {"results": commit.parents}
481 result = {"results": commit.parents}
483 return result
482 return result
484 else:
483 else:
485 raise HTTPBadRequest()
484 raise HTTPBadRequest()
General Comments 0
You need to be logged in to leave comments. Login now