##// END OF EJS Templates
Fixed permissions for users groups, group can have create repo permission now....
marcink -
r1271:aa7e45ad beta
parent child Browse files
Show More
@@ -1,312 +1,339 b''
1 1 """
2 2 Routes configuration
3 3
4 4 The more specific and detailed routes should be defined first so they
5 5 may take precedent over the more generic routes. For more information
6 6 refer to the routes manual at http://routes.groovie.org/docs/
7 7 """
8 8 from __future__ import with_statement
9 9 from routes import Mapper
10 10 from rhodecode.lib.utils import check_repo_fast as cr
11 11
12 12
13 13 def make_map(config):
14 14 """Create, configure and return the routes Mapper"""
15 15 rmap = Mapper(directory=config['pylons.paths']['controllers'],
16 16 always_scan=config['debug'])
17 17 rmap.minimization = False
18 18 rmap.explicit = False
19 19
20 20 def check_repo(environ, match_dict):
21 21 """
22 22 check for valid repository for proper 404 handling
23 23 :param environ:
24 24 :param match_dict:
25 25 """
26 26 repo_name = match_dict.get('repo_name')
27 27 return not cr(repo_name, config['base_path'])
28 28
29 29 # The ErrorController route (handles 404/500 error pages); it should
30 30 # likely stay at the top, ensuring it can always be resolved
31 31 rmap.connect('/error/{action}', controller='error')
32 32 rmap.connect('/error/{action}/{id}', controller='error')
33 33
34 34 #==========================================================================
35 35 # CUSTOM ROUTES HERE
36 36 #==========================================================================
37 37
38 38 #MAIN PAGE
39 39 rmap.connect('home', '/', controller='home', action='index')
40 40 rmap.connect('repo_switcher', '/repos', controller='home',
41 41 action='repo_switcher')
42 42 rmap.connect('bugtracker',
43 43 "http://bitbucket.org/marcinkuzminski/rhodecode/issues",
44 44 _static=True)
45 45 rmap.connect('rhodecode_official', "http://rhodecode.org", _static=True)
46 46
47 47 #ADMIN REPOSITORY REST ROUTES
48 48 with rmap.submapper(path_prefix='/_admin', controller='admin/repos') as m:
49 49 m.connect("repos", "/repos",
50 50 action="create", conditions=dict(method=["POST"]))
51 51 m.connect("repos", "/repos",
52 52 action="index", conditions=dict(method=["GET"]))
53 53 m.connect("formatted_repos", "/repos.{format}",
54 54 action="index",
55 55 conditions=dict(method=["GET"]))
56 56 m.connect("new_repo", "/repos/new",
57 57 action="new", conditions=dict(method=["GET"]))
58 58 m.connect("formatted_new_repo", "/repos/new.{format}",
59 59 action="new", conditions=dict(method=["GET"]))
60 60 m.connect("/repos/{repo_name:.*}",
61 61 action="update", conditions=dict(method=["PUT"],
62 62 function=check_repo))
63 63 m.connect("/repos/{repo_name:.*}",
64 64 action="delete", conditions=dict(method=["DELETE"],
65 65 function=check_repo))
66 66 m.connect("edit_repo", "/repos/{repo_name:.*}/edit",
67 67 action="edit", conditions=dict(method=["GET"],
68 68 function=check_repo))
69 69 m.connect("formatted_edit_repo", "/repos/{repo_name:.*}.{format}/edit",
70 70 action="edit", conditions=dict(method=["GET"],
71 71 function=check_repo))
72 72 m.connect("repo", "/repos/{repo_name:.*}",
73 73 action="show", conditions=dict(method=["GET"],
74 74 function=check_repo))
75 75 m.connect("formatted_repo", "/repos/{repo_name:.*}.{format}",
76 76 action="show", conditions=dict(method=["GET"],
77 77 function=check_repo))
78 78 #ajax delete repo perm user
79 79 m.connect('delete_repo_user', "/repos_delete_user/{repo_name:.*}",
80 80 action="delete_perm_user", conditions=dict(method=["DELETE"],
81 81 function=check_repo))
82 82 #ajax delete repo perm users_group
83 83 m.connect('delete_repo_users_group',
84 84 "/repos_delete_users_group/{repo_name:.*}",
85 85 action="delete_perm_users_group",
86 86 conditions=dict(method=["DELETE"], function=check_repo))
87 87
88 88 #settings actions
89 89 m.connect('repo_stats', "/repos_stats/{repo_name:.*}",
90 90 action="repo_stats", conditions=dict(method=["DELETE"],
91 91 function=check_repo))
92 92 m.connect('repo_cache', "/repos_cache/{repo_name:.*}",
93 93 action="repo_cache", conditions=dict(method=["DELETE"],
94 94 function=check_repo))
95 95 m.connect('repo_public_journal',
96 96 "/repos_public_journal/{repo_name:.*}",
97 97 action="repo_public_journal", conditions=dict(method=["PUT"],
98 98 function=check_repo))
99 99 m.connect('repo_pull', "/repo_pull/{repo_name:.*}",
100 100 action="repo_pull", conditions=dict(method=["PUT"],
101 101 function=check_repo))
102 102
103 103 #ADMIN REPOS GROUP REST ROUTES
104 104 rmap.resource('repos_group', 'repos_groups',
105 105 controller='admin/repos_groups', path_prefix='/_admin')
106 106
107 107 #ADMIN USER REST ROUTES
108 108 with rmap.submapper(path_prefix='/_admin', controller='admin/users') as m:
109 109 m.connect("users", "/users",
110 110 action="create", conditions=dict(method=["POST"]))
111 111 m.connect("users", "/users",
112 112 action="index", conditions=dict(method=["GET"]))
113 113 m.connect("formatted_users", "/users.{format}",
114 114 action="index", conditions=dict(method=["GET"]))
115 115 m.connect("new_user", "/users/new",
116 116 action="new", conditions=dict(method=["GET"]))
117 117 m.connect("formatted_new_user", "/users/new.{format}",
118 118 action="new", conditions=dict(method=["GET"]))
119 119 m.connect("update_user", "/users/{id}",
120 120 action="update", conditions=dict(method=["PUT"]))
121 121 m.connect("delete_user", "/users/{id}",
122 122 action="delete", conditions=dict(method=["DELETE"]))
123 123 m.connect("edit_user", "/users/{id}/edit",
124 124 action="edit", conditions=dict(method=["GET"]))
125 125 m.connect("formatted_edit_user",
126 126 "/users/{id}.{format}/edit",
127 127 action="edit", conditions=dict(method=["GET"]))
128 128 m.connect("user", "/users/{id}",
129 129 action="show", conditions=dict(method=["GET"]))
130 130 m.connect("formatted_user", "/users/{id}.{format}",
131 131 action="show", conditions=dict(method=["GET"]))
132 132
133 133 #EXTRAS USER ROUTES
134 134 m.connect("user_perm", "/users_perm/{id}",
135 135 action="update_perm", conditions=dict(method=["PUT"]))
136 136
137 137 #ADMIN USERS REST ROUTES
138 rmap.resource('users_group', 'users_groups',
139 controller='admin/users_groups', path_prefix='/_admin')
138 with rmap.submapper(path_prefix='/_admin',
139 controller='admin/users_groups') as m:
140 m.connect("users_groups", "/users_groups",
141 action="create", conditions=dict(method=["POST"]))
142 m.connect("users_groups", "/users_groups",
143 action="index", conditions=dict(method=["GET"]))
144 m.connect("formatted_users_groups", "/users_groups.{format}",
145 action="index", conditions=dict(method=["GET"]))
146 m.connect("new_users_group", "/users_groups/new",
147 action="new", conditions=dict(method=["GET"]))
148 m.connect("formatted_new_users_group", "/users_groups/new.{format}",
149 action="new", conditions=dict(method=["GET"]))
150 m.connect("update_users_group", "/users_groups/{id}",
151 action="update", conditions=dict(method=["PUT"]))
152 m.connect("delete_users_group", "/users_groups/{id}",
153 action="delete", conditions=dict(method=["DELETE"]))
154 m.connect("edit_users_group", "/users_groups/{id}/edit",
155 action="edit", conditions=dict(method=["GET"]))
156 m.connect("formatted_edit_users_group",
157 "/users_groups/{id}.{format}/edit",
158 action="edit", conditions=dict(method=["GET"]))
159 m.connect("users_group", "/users_groups/{id}",
160 action="show", conditions=dict(method=["GET"]))
161 m.connect("formatted_users_group", "/users_groups/{id}.{format}",
162 action="show", conditions=dict(method=["GET"]))
163
164 #EXTRAS USER ROUTES
165 m.connect("users_group_perm", "/users_groups_perm/{id}",
166 action="update_perm", conditions=dict(method=["PUT"]))
140 167
141 168 #ADMIN GROUP REST ROUTES
142 169 rmap.resource('group', 'groups',
143 170 controller='admin/groups', path_prefix='/_admin')
144 171
145 172 #ADMIN PERMISSIONS REST ROUTES
146 173 rmap.resource('permission', 'permissions',
147 174 controller='admin/permissions', path_prefix='/_admin')
148 175
149 176 ##ADMIN LDAP SETTINGS
150 177 rmap.connect('ldap_settings', '/_admin/ldap',
151 178 controller='admin/ldap_settings', action='ldap_settings',
152 179 conditions=dict(method=["POST"]))
153 180
154 181 rmap.connect('ldap_home', '/_admin/ldap',
155 182 controller='admin/ldap_settings')
156 183
157 184 #ADMIN SETTINGS REST ROUTES
158 185 with rmap.submapper(path_prefix='/_admin',
159 186 controller='admin/settings') as m:
160 187 m.connect("admin_settings", "/settings",
161 188 action="create", conditions=dict(method=["POST"]))
162 189 m.connect("admin_settings", "/settings",
163 190 action="index", conditions=dict(method=["GET"]))
164 191 m.connect("formatted_admin_settings", "/settings.{format}",
165 192 action="index", conditions=dict(method=["GET"]))
166 193 m.connect("admin_new_setting", "/settings/new",
167 194 action="new", conditions=dict(method=["GET"]))
168 195 m.connect("formatted_admin_new_setting", "/settings/new.{format}",
169 196 action="new", conditions=dict(method=["GET"]))
170 197 m.connect("/settings/{setting_id}",
171 198 action="update", conditions=dict(method=["PUT"]))
172 199 m.connect("/settings/{setting_id}",
173 200 action="delete", conditions=dict(method=["DELETE"]))
174 201 m.connect("admin_edit_setting", "/settings/{setting_id}/edit",
175 202 action="edit", conditions=dict(method=["GET"]))
176 203 m.connect("formatted_admin_edit_setting",
177 204 "/settings/{setting_id}.{format}/edit",
178 205 action="edit", conditions=dict(method=["GET"]))
179 206 m.connect("admin_setting", "/settings/{setting_id}",
180 207 action="show", conditions=dict(method=["GET"]))
181 208 m.connect("formatted_admin_setting", "/settings/{setting_id}.{format}",
182 209 action="show", conditions=dict(method=["GET"]))
183 210 m.connect("admin_settings_my_account", "/my_account",
184 211 action="my_account", conditions=dict(method=["GET"]))
185 212 m.connect("admin_settings_my_account_update", "/my_account_update",
186 213 action="my_account_update", conditions=dict(method=["PUT"]))
187 214 m.connect("admin_settings_create_repository", "/create_repository",
188 215 action="create_repository", conditions=dict(method=["GET"]))
189 216
190 217 #ADMIN MAIN PAGES
191 218 with rmap.submapper(path_prefix='/_admin', controller='admin/admin') as m:
192 219 m.connect('admin_home', '', action='index')
193 220 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
194 221 action='add_repo')
195 222
196 223 #USER JOURNAL
197 224 rmap.connect('journal', '/_admin/journal', controller='journal')
198 225
199 226 rmap.connect('public_journal', '/_admin/public_journal',
200 227 controller='journal', action="public_journal")
201 228
202 229 rmap.connect('public_journal_rss', '/_admin/public_journal_rss',
203 230 controller='journal', action="public_journal_rss")
204 231
205 232 rmap.connect('public_journal_atom', '/_admin/public_journal_atom',
206 233 controller='journal', action="public_journal_atom")
207 234
208 235 rmap.connect('toggle_following', '/_admin/toggle_following',
209 236 controller='journal', action='toggle_following',
210 237 conditions=dict(method=["POST"]))
211 238
212 239 #SEARCH
213 240 rmap.connect('search', '/_admin/search', controller='search',)
214 241 rmap.connect('search_repo', '/_admin/search/{search_repo:.*}',
215 242 controller='search')
216 243
217 244 #LOGIN/LOGOUT/REGISTER/SIGN IN
218 245 rmap.connect('login_home', '/_admin/login', controller='login')
219 246 rmap.connect('logout_home', '/_admin/logout', controller='login',
220 247 action='logout')
221 248
222 249 rmap.connect('register', '/_admin/register', controller='login',
223 250 action='register')
224 251
225 252 rmap.connect('reset_password', '/_admin/password_reset',
226 253 controller='login', action='password_reset')
227 254
228 255 #FEEDS
229 256 rmap.connect('rss_feed_home', '/{repo_name:.*}/feed/rss',
230 257 controller='feed', action='rss',
231 258 conditions=dict(function=check_repo))
232 259
233 260 rmap.connect('atom_feed_home', '/{repo_name:.*}/feed/atom',
234 261 controller='feed', action='atom',
235 262 conditions=dict(function=check_repo))
236 263
237 264 #REPOSITORY ROUTES
238 265 rmap.connect('changeset_home', '/{repo_name:.*}/changeset/{revision}',
239 266 controller='changeset', revision='tip',
240 267 conditions=dict(function=check_repo))
241 268
242 269 rmap.connect('raw_changeset_home',
243 270 '/{repo_name:.*}/raw-changeset/{revision}',
244 271 controller='changeset', action='raw_changeset',
245 272 revision='tip', conditions=dict(function=check_repo))
246 273
247 274 rmap.connect('summary_home', '/{repo_name:.*}',
248 275 controller='summary', conditions=dict(function=check_repo))
249 276
250 277 rmap.connect('summary_home', '/{repo_name:.*}/summary',
251 278 controller='summary', conditions=dict(function=check_repo))
252 279
253 280 rmap.connect('shortlog_home', '/{repo_name:.*}/shortlog',
254 281 controller='shortlog', conditions=dict(function=check_repo))
255 282
256 283 rmap.connect('branches_home', '/{repo_name:.*}/branches',
257 284 controller='branches', conditions=dict(function=check_repo))
258 285
259 286 rmap.connect('tags_home', '/{repo_name:.*}/tags',
260 287 controller='tags', conditions=dict(function=check_repo))
261 288
262 289 rmap.connect('changelog_home', '/{repo_name:.*}/changelog',
263 290 controller='changelog', conditions=dict(function=check_repo))
264 291
265 292 rmap.connect('files_home', '/{repo_name:.*}/files/{revision}/{f_path:.*}',
266 293 controller='files', revision='tip', f_path='',
267 294 conditions=dict(function=check_repo))
268 295
269 296 rmap.connect('files_diff_home', '/{repo_name:.*}/diff/{f_path:.*}',
270 297 controller='files', action='diff', revision='tip', f_path='',
271 298 conditions=dict(function=check_repo))
272 299
273 300 rmap.connect('files_rawfile_home',
274 301 '/{repo_name:.*}/rawfile/{revision}/{f_path:.*}',
275 302 controller='files', action='rawfile', revision='tip',
276 303 f_path='', conditions=dict(function=check_repo))
277 304
278 305 rmap.connect('files_raw_home',
279 306 '/{repo_name:.*}/raw/{revision}/{f_path:.*}',
280 307 controller='files', action='raw', revision='tip', f_path='',
281 308 conditions=dict(function=check_repo))
282 309
283 310 rmap.connect('files_annotate_home',
284 311 '/{repo_name:.*}/annotate/{revision}/{f_path:.*}',
285 312 controller='files', action='annotate', revision='tip',
286 313 f_path='', conditions=dict(function=check_repo))
287 314
288 315 rmap.connect('files_archive_home', '/{repo_name:.*}/archive/{fname}',
289 316 controller='files', action='archivefile',
290 317 conditions=dict(function=check_repo))
291 318
292 319 rmap.connect('repo_settings_delete', '/{repo_name:.*}/settings',
293 320 controller='settings', action="delete",
294 321 conditions=dict(method=["DELETE"], function=check_repo))
295 322
296 323 rmap.connect('repo_settings_update', '/{repo_name:.*}/settings',
297 324 controller='settings', action="update",
298 325 conditions=dict(method=["PUT"], function=check_repo))
299 326
300 327 rmap.connect('repo_settings_home', '/{repo_name:.*}/settings',
301 328 controller='settings', action='index',
302 329 conditions=dict(function=check_repo))
303 330
304 331 rmap.connect('repo_fork_create_home', '/{repo_name:.*}/fork',
305 332 controller='settings', action='fork_create',
306 333 conditions=dict(function=check_repo, method=["POST"]))
307 334
308 335 rmap.connect('repo_fork_home', '/{repo_name:.*}/fork',
309 336 controller='settings', action='fork',
310 337 conditions=dict(function=check_repo))
311 338
312 339 return rmap
@@ -1,180 +1,213 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.admin.users_groups
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Users Groups crud controller for pylons
7 7
8 8 :created_on: Jan 25, 2011
9 9 :author: marcink
10 10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import logging
27 27 import traceback
28 28 import formencode
29 29
30 30 from formencode import htmlfill
31 31 from pylons import request, session, tmpl_context as c, url, config
32 32 from pylons.controllers.util import abort, redirect
33 33 from pylons.i18n.translation import _
34 34
35 35 from rhodecode.lib import helpers as h
36 36 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
37 37 from rhodecode.lib.base import BaseController, render
38 38
39 from rhodecode.model.db import User, UsersGroup
39 from rhodecode.model.db import User, UsersGroup, Permission, UsersGroupToPerm
40 40 from rhodecode.model.forms import UserForm, UsersGroupForm
41 from rhodecode.model.user import UserModel
42 41 from rhodecode.model.users_group import UsersGroupModel
43 42
44 43 log = logging.getLogger(__name__)
45 44
46 45
47 46 class UsersGroupsController(BaseController):
48 47 """REST Controller styled on the Atom Publishing Protocol"""
49 48 # To properly map this controller, ensure your config/routing.py
50 49 # file has a resource setup:
51 50 # map.resource('users_group', 'users_groups')
52 51
53 52 @LoginRequired()
54 53 @HasPermissionAllDecorator('hg.admin')
55 54 def __before__(self):
56 55 c.admin_user = session.get('admin_user')
57 56 c.admin_username = session.get('admin_username')
58 57 super(UsersGroupsController, self).__before__()
59 58 c.available_permissions = config['available_permissions']
60 59
61 60 def index(self, format='html'):
62 61 """GET /users_groups: All items in the collection"""
63 62 # url('users_groups')
64 63 c.users_groups_list = self.sa.query(UsersGroup).all()
65 64 return render('admin/users_groups/users_groups.html')
66 65
67 66 def create(self):
68 67 """POST /users_groups: Create a new item"""
69 68 # url('users_groups')
70 69 users_group_model = UsersGroupModel()
71 70 users_group_form = UsersGroupForm()()
72 71 try:
73 72 form_result = users_group_form.to_python(dict(request.POST))
74 73 users_group_model.create(form_result)
75 74 h.flash(_('created users group %s') \
76 75 % form_result['users_group_name'], category='success')
77 76 #action_logger(self.rhodecode_user, 'new_user', '', '', self.sa)
78 77 except formencode.Invalid, errors:
79 78 return htmlfill.render(
80 79 render('admin/users_groups/users_group_add.html'),
81 80 defaults=errors.value,
82 81 errors=errors.error_dict or {},
83 82 prefix_error=False,
84 83 encoding="UTF-8")
85 84 except Exception:
86 85 log.error(traceback.format_exc())
87 86 h.flash(_('error occurred during creation of users group %s') \
88 87 % request.POST.get('users_group_name'), category='error')
89 88
90 89 return redirect(url('users_groups'))
91 90
92 91 def new(self, format='html'):
93 92 """GET /users_groups/new: Form to create a new item"""
94 93 # url('new_users_group')
95 94 return render('admin/users_groups/users_group_add.html')
96 95
97 96 def update(self, id):
98 97 """PUT /users_groups/id: Update an existing item"""
99 98 # Forms posted to this method should contain a hidden field:
100 99 # <input type="hidden" name="_method" value="PUT" />
101 100 # Or using helpers:
102 101 # h.form(url('users_group', id=ID),
103 102 # method='put')
104 103 # url('users_group', id=ID)
105 104
106 105 users_group_model = UsersGroupModel()
107 106 c.users_group = users_group_model.get(id)
108 107 c.group_members = [(x.user_id, x.user.username) for x in
109 108 c.users_group.members]
110 109
111 110 c.available_members = [(x.user_id, x.username) for x in
112 111 self.sa.query(User).all()]
113 112 users_group_form = UsersGroupForm(edit=True,
114 113 old_data=c.users_group.get_dict(),
115 114 available_members=[str(x[0]) for x
116 115 in c.available_members])()
117 116
118 117 try:
119 118 form_result = users_group_form.to_python(request.POST)
120 119 users_group_model.update(id, form_result)
121 120 h.flash(_('updated users group %s') \
122 121 % form_result['users_group_name'],
123 122 category='success')
124 123 #action_logger(self.rhodecode_user, 'new_user', '', '', self.sa)
125 124 except formencode.Invalid, errors:
125 e = errors.error_dict or {}
126
127 perm = Permission.get_by_key('hg.create.repository')
128 e.update({'create_repo_perm':
129 UsersGroupToPerm.has_perm(id, perm)})
130
126 131 return htmlfill.render(
127 132 render('admin/users_groups/users_group_edit.html'),
128 133 defaults=errors.value,
129 errors=errors.error_dict or {},
134 errors=e,
130 135 prefix_error=False,
131 136 encoding="UTF-8")
132 137 except Exception:
133 138 log.error(traceback.format_exc())
134 139 h.flash(_('error occurred during update of users group %s') \
135 140 % request.POST.get('users_group_name'), category='error')
136 141
137 142 return redirect(url('users_groups'))
138 143
139 144 def delete(self, id):
140 145 """DELETE /users_groups/id: Delete an existing item"""
141 146 # Forms posted to this method should contain a hidden field:
142 147 # <input type="hidden" name="_method" value="DELETE" />
143 148 # Or using helpers:
144 149 # h.form(url('users_group', id=ID),
145 150 # method='delete')
146 151 # url('users_group', id=ID)
147 152 users_group_model = UsersGroupModel()
148 153 try:
149 154 users_group_model.delete(id)
150 155 h.flash(_('successfully deleted users group'), category='success')
151 156 except Exception:
152 157 h.flash(_('An error occurred during deletion of users group'),
153 158 category='error')
154 159 return redirect(url('users_groups'))
155 160
156 161 def show(self, id, format='html'):
157 162 """GET /users_groups/id: Show a specific item"""
158 163 # url('users_group', id=ID)
159 164
160 165 def edit(self, id, format='html'):
161 166 """GET /users_groups/id/edit: Form to edit an existing item"""
162 167 # url('edit_users_group', id=ID)
163 168
164 169 c.users_group = self.sa.query(UsersGroup).get(id)
165 170 if not c.users_group:
166 171 return redirect(url('users_groups'))
167 172
168 173 c.users_group.permissions = {}
169 174 c.group_members = [(x.user_id, x.user.username) for x in
170 175 c.users_group.members]
171 176 c.available_members = [(x.user_id, x.username) for x in
172 177 self.sa.query(User).all()]
173 178 defaults = c.users_group.get_dict()
174
179 perm = Permission.get_by_key('hg.create.repository')
180 defaults.update({'create_repo_perm':
181 UsersGroupToPerm.has_perm(id, perm)})
175 182 return htmlfill.render(
176 183 render('admin/users_groups/users_group_edit.html'),
177 184 defaults=defaults,
178 185 encoding="UTF-8",
179 186 force_defaults=False
180 187 )
188
189 def update_perm(self, id):
190 """PUT /users_perm/id: Update an existing item"""
191 # url('users_group_perm', id=ID, method='put')
192
193 grant_perm = request.POST.get('create_repo_perm', False)
194
195 if grant_perm:
196 perm = Permission.get_by_key('hg.create.none')
197 UsersGroupToPerm.revoke_perm(id, perm)
198
199 perm = Permission.get_by_key('hg.create.repository')
200 UsersGroupToPerm.grant_perm(id, perm)
201 h.flash(_("Granted 'repository create' permission to user"),
202 category='success')
203
204 else:
205 perm = Permission.get_by_key('hg.create.repository')
206 UsersGroupToPerm.revoke_perm(id, perm)
207
208 perm = Permission.get_by_key('hg.create.none')
209 UsersGroupToPerm.grant_perm(id, perm)
210 h.flash(_("Revoked 'repository create' permission to user"),
211 category='success')
212
213 return redirect(url('edit_users_group', id=id))
@@ -1,97 +1,102 b''
1 1 import logging
2 2 import datetime
3 3
4 4 from sqlalchemy import *
5 5 from sqlalchemy.exc import DatabaseError
6 6 from sqlalchemy.orm import relation, backref, class_mapper
7 7 from sqlalchemy.orm.session import Session
8 8
9 9 from rhodecode.lib.dbmigrate.migrate import *
10 10 from rhodecode.lib.dbmigrate.migrate.changeset import *
11 11
12 12 from rhodecode.model.meta import Base
13 13
14 14 log = logging.getLogger(__name__)
15 15
16 16 def upgrade(migrate_engine):
17 17 """ Upgrade operations go here.
18 18 Don't create your own engine; bind migrate_engine to your metadata
19 19 """
20 20
21 21 #==========================================================================
22 22 # Add table `groups``
23 23 #==========================================================================
24 24 from rhodecode.model.db import Group
25 25 Group().__table__.create()
26 26
27 27 #==========================================================================
28 28 # Add table `group_to_perm`
29 29 #==========================================================================
30 30 from rhodecode.model.db import GroupToPerm
31 31 GroupToPerm().__table__.create()
32 32
33 33 #==========================================================================
34 34 # Add table `users_groups`
35 35 #==========================================================================
36 36 from rhodecode.model.db import UsersGroup
37 37 UsersGroup().__table__.create()
38 38
39 39 #==========================================================================
40 40 # Add table `users_groups_members`
41 41 #==========================================================================
42 42 from rhodecode.model.db import UsersGroupMember
43 43 UsersGroupMember().__table__.create()
44 44
45 45 #==========================================================================
46 # Add table `users_group_repo_to_perm`
47 #==========================================================================
48 from rhodecode.model.db import UsersGroupRepoToPerm
49 UsersGroupRepoToPerm().__table__.create()
50
51 #==========================================================================
46 52 # Add table `users_group_to_perm`
47 53 #==========================================================================
48 54 from rhodecode.model.db import UsersGroupToPerm
49 55 UsersGroupToPerm().__table__.create()
50 56
51
52 57 #==========================================================================
53 58 # Upgrade of `users` table
54 59 #==========================================================================
55 60 from rhodecode.model.db import User
56 61
57 62 #add column
58 63 ldap_dn = Column("ldap_dn", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
59 64 ldap_dn.create(User().__table__)
60 65
61 66 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
62 67 api_key.create(User().__table__)
63 68
64 69 #remove old column
65 70 is_ldap = Column("is_ldap", Boolean(), nullable=False, unique=None, default=False)
66 71 is_ldap.drop(User().__table__)
67 72
68 73
69 74 #==========================================================================
70 75 # Upgrade of `repositories` table
71 76 #==========================================================================
72 77 from rhodecode.model.db import Repository
73 78
74 79 #ADD downloads column#
75 80 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
76 81 enable_downloads.create(Repository().__table__)
77 82
78 83 #ADD group_id column#
79 84 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'),
80 85 nullable=True, unique=False, default=None)
81 86
82 87 group_id.create(Repository().__table__)
83 88
84 89
85 90 #ADD clone_uri column#
86 91
87 92 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False,
88 93 assert_unicode=None),
89 94 nullable=True, unique=False, default=None)
90 95
91 96 clone_uri.create(Repository().__table__)
92 97 return
93 98
94 99
95 100 def downgrade(migrate_engine):
96 101 meta = MetaData()
97 102 meta.bind = migrate_engine
@@ -1,71 +1,74 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.__init__
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 The application's model objects
7 7
8 8 :created_on: Nov 25, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12
13 13
14 14 :example:
15 15
16 16 .. code-block:: python
17 17
18 18 from paste.deploy import appconfig
19 19 from pylons import config
20 20 from sqlalchemy import engine_from_config
21 21 from rhodecode.config.environment import load_environment
22 22
23 23 conf = appconfig('config:development.ini', relative_to = './../../')
24 24 load_environment(conf.global_conf, conf.local_conf)
25 25
26 26 engine = engine_from_config(config, 'sqlalchemy.')
27 27 init_model(engine)
28 28 # RUN YOUR CODE HERE
29 29
30 30 """
31 31 # This program is free software: you can redistribute it and/or modify
32 32 # it under the terms of the GNU General Public License as published by
33 33 # the Free Software Foundation, either version 3 of the License, or
34 34 # (at your option) any later version.
35 35 #
36 36 # This program is distributed in the hope that it will be useful,
37 37 # but WITHOUT ANY WARRANTY; without even the implied warranty of
38 38 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
39 39 # GNU General Public License for more details.
40 40 #
41 41 # You should have received a copy of the GNU General Public License
42 42 # along with this program. If not, see <http://www.gnu.org/licenses/>.
43 43
44 44 import logging
45 45
46 46 from rhodecode.model import meta
47 47
48 48 log = logging.getLogger(__name__)
49 49
50
50 51 def init_model(engine):
51 """Initializes db session, bind the engine with the metadata,
52 Call this before using any of the tables or classes in the model, preferably
53 once in application start
52 """
53 Initializes db session, bind the engine with the metadata,
54 Call this before using any of the tables or classes in the model,
55 preferably once in application start
54 56
55 57 :param engine: engine to bind to
56 58 """
57 59 log.info("initializing db for %s", engine)
58 60 meta.Base.metadata.bind = engine
59 61
62
60 63 class BaseModel(object):
61 64 """Base Model for all RhodeCode models, it adds sql alchemy session
62 65 into instance of model
63 66
64 67 :param sa: If passed it reuses this session instead of creating a new one
65 68 """
66 69
67 70 def __init__(self, sa=None):
68 71 if sa is not None:
69 72 self.sa = sa
70 73 else:
71 74 self.sa = meta.Session()
@@ -1,276 +1,290 b''
1 1 """caching_query.py
2 2
3 3 Represent persistence structures which allow the usage of
4 4 Beaker caching with SQLAlchemy.
5 5
6 6 The three new concepts introduced here are:
7 7
8 8 * CachingQuery - a Query subclass that caches and
9 9 retrieves results in/from Beaker.
10 10 * FromCache - a query option that establishes caching
11 11 parameters on a Query
12 12 * RelationshipCache - a variant of FromCache which is specific
13 13 to a query invoked during a lazy load.
14 14 * _params_from_query - extracts value parameters from
15 15 a Query.
16 16
17 17 The rest of what's here are standard SQLAlchemy and
18 18 Beaker constructs.
19 19
20 20 """
21 import beaker
21 22 from beaker.exceptions import BeakerException
23
22 24 from sqlalchemy.orm.interfaces import MapperOption
23 25 from sqlalchemy.orm.query import Query
24 26 from sqlalchemy.sql import visitors
25 import beaker
27
26 28
27 29 class CachingQuery(Query):
28 30 """A Query subclass which optionally loads full results from a Beaker
29 31 cache region.
30 32
31 33 The CachingQuery stores additional state that allows it to consult
32 34 a Beaker cache before accessing the database:
33 35
34 36 * A "region", which is a cache region argument passed to a
35 37 Beaker CacheManager, specifies a particular cache configuration
36 38 (including backend implementation, expiration times, etc.)
37 39 * A "namespace", which is a qualifying name that identifies a
38 40 group of keys within the cache. A query that filters on a name
39 41 might use the name "by_name", a query that filters on a date range
40 42 to a joined table might use the name "related_date_range".
41 43
42 44 When the above state is present, a Beaker cache is retrieved.
43 45
44 46 The "namespace" name is first concatenated with
45 47 a string composed of the individual entities and columns the Query
46 48 requests, i.e. such as ``Query(User.id, User.name)``.
47 49
48 50 The Beaker cache is then loaded from the cache manager based
49 51 on the region and composed namespace. The key within the cache
50 52 itself is then constructed against the bind parameters specified
51 53 by this query, which are usually literals defined in the
52 54 WHERE clause.
53 55
54 56 The FromCache and RelationshipCache mapper options below represent
55 57 the "public" method of configuring this state upon the CachingQuery.
56 58
57 59 """
58 60
59 61 def __init__(self, manager, *args, **kw):
60 62 self.cache_manager = manager
61 63 Query.__init__(self, *args, **kw)
62 64
63 65 def __iter__(self):
64 66 """override __iter__ to pull results from Beaker
65 67 if particular attributes have been configured.
66 68
67 69 Note that this approach does *not* detach the loaded objects from
68 70 the current session. If the cache backend is an in-process cache
69 71 (like "memory") and lives beyond the scope of the current session's
70 72 transaction, those objects may be expired. The method here can be
71 73 modified to first expunge() each loaded item from the current
72 74 session before returning the list of items, so that the items
73 75 in the cache are not the same ones in the current Session.
74 76
75 77 """
76 78 if hasattr(self, '_cache_parameters'):
77 return self.get_value(createfunc=lambda: list(Query.__iter__(self)))
79 return self.get_value(createfunc=lambda:
80 list(Query.__iter__(self)))
78 81 else:
79 82 return Query.__iter__(self)
80 83
81 84 def invalidate(self):
82 85 """Invalidate the value represented by this Query."""
83 86
84 87 cache, cache_key = _get_cache_parameters(self)
85 88 cache.remove(cache_key)
86 89
87 90 def get_value(self, merge=True, createfunc=None):
88 91 """Return the value from the cache for this query.
89 92
90 93 Raise KeyError if no value present and no
91 94 createfunc specified.
92 95
93 96 """
94 97 cache, cache_key = _get_cache_parameters(self)
95 98 ret = cache.get_value(cache_key, createfunc=createfunc)
96 99 if merge:
97 100 ret = self.merge_result(ret, load=False)
98 101 return ret
99 102
100 103 def set_value(self, value):
101 104 """Set the value in the cache for this query."""
102 105
103 106 cache, cache_key = _get_cache_parameters(self)
104 107 cache.put(cache_key, value)
105 108
109
106 110 def query_callable(manager):
107 111 def query(*arg, **kw):
108 112 return CachingQuery(manager, *arg, **kw)
109 113 return query
110 114
115
111 116 def get_cache_region(name, region):
112 117 if region not in beaker.cache.cache_regions:
113 118 raise BeakerException('Cache region `%s` not configured '
114 119 'Check if proper cache settings are in the .ini files' % region)
115 120 kw = beaker.cache.cache_regions[region]
116 121 return beaker.cache.Cache._get_cache(name, kw)
117 122
123
118 124 def _get_cache_parameters(query):
119 125 """For a query with cache_region and cache_namespace configured,
120 126 return the correspoinding Cache instance and cache key, based
121 127 on this query's current criterion and parameter values.
122 128
123 129 """
124 130 if not hasattr(query, '_cache_parameters'):
125 raise ValueError("This Query does not have caching parameters configured.")
131 raise ValueError("This Query does not have caching "
132 "parameters configured.")
126 133
127 134 region, namespace, cache_key = query._cache_parameters
128 135
129 136 namespace = _namespace_from_query(namespace, query)
130 137
131 138 if cache_key is None:
132 139 # cache key - the value arguments from this query's parameters.
133 140 args = _params_from_query(query)
134 141 cache_key = " ".join([str(x) for x in args])
135 142
136 143 # get cache
137 144 #cache = query.cache_manager.get_cache_region(namespace, region)
138 145 cache = get_cache_region(namespace, region)
139 146 # optional - hash the cache_key too for consistent length
140 147 # import uuid
141 148 # cache_key= str(uuid.uuid5(uuid.NAMESPACE_DNS, cache_key))
142 149
143 150 return cache, cache_key
144 151
152
145 153 def _namespace_from_query(namespace, query):
146 154 # cache namespace - the token handed in by the
147 155 # option + class we're querying against
148 156 namespace = " ".join([namespace] + [str(x) for x in query._entities])
149 157
150 158 # memcached wants this
151 159 namespace = namespace.replace(' ', '_')
152 160
153 161 return namespace
154 162
163
155 164 def _set_cache_parameters(query, region, namespace, cache_key):
156 165
157 166 if hasattr(query, '_cache_parameters'):
158 167 region, namespace, cache_key = query._cache_parameters
159 168 raise ValueError("This query is already configured "
160 169 "for region %r namespace %r" %
161 170 (region, namespace)
162 171 )
163 172 query._cache_parameters = region, namespace, cache_key
164 173
174
165 175 class FromCache(MapperOption):
166 176 """Specifies that a Query should load results from a cache."""
167 177
168 178 propagate_to_loaders = False
169 179
170 180 def __init__(self, region, namespace, cache_key=None):
171 181 """Construct a new FromCache.
172 182
173 183 :param region: the cache region. Should be a
174 184 region configured in the Beaker CacheManager.
175 185
176 186 :param namespace: the cache namespace. Should
177 187 be a name uniquely describing the target Query's
178 188 lexical structure.
179 189
180 190 :param cache_key: optional. A string cache key
181 191 that will serve as the key to the query. Use this
182 192 if your query has a huge amount of parameters (such
183 193 as when using in_()) which correspond more simply to
184 194 some other identifier.
185 195
186 196 """
187 197 self.region = region
188 198 self.namespace = namespace
189 199 self.cache_key = cache_key
190 200
191 201 def process_query(self, query):
192 202 """Process a Query during normal loading operation."""
193 203
194 _set_cache_parameters(query, self.region, self.namespace, self.cache_key)
204 _set_cache_parameters(query, self.region, self.namespace,
205 self.cache_key)
206
195 207
196 208 class RelationshipCache(MapperOption):
197 209 """Specifies that a Query as called within a "lazy load"
198 210 should load results from a cache."""
199 211
200 212 propagate_to_loaders = True
201 213
202 214 def __init__(self, region, namespace, attribute):
203 215 """Construct a new RelationshipCache.
204 216
205 217 :param region: the cache region. Should be a
206 218 region configured in the Beaker CacheManager.
207 219
208 220 :param namespace: the cache namespace. Should
209 221 be a name uniquely describing the target Query's
210 222 lexical structure.
211 223
212 224 :param attribute: A Class.attribute which
213 225 indicates a particular class relationship() whose
214 226 lazy loader should be pulled from the cache.
215 227
216 228 """
217 229 self.region = region
218 230 self.namespace = namespace
219 231 self._relationship_options = {
220 (attribute.property.parent.class_, attribute.property.key) : self
232 (attribute.property.parent.class_, attribute.property.key): self
221 233 }
222 234
223 235 def process_query_conditionally(self, query):
224 236 """Process a Query that is used within a lazy loader.
225 237
226 238 (the process_query_conditionally() method is a SQLAlchemy
227 239 hook invoked only within lazyload.)
228 240
229 241 """
230 242 if query._current_path:
231 243 mapper, key = query._current_path[-2:]
232 244
233 245 for cls in mapper.class_.__mro__:
234 246 if (cls, key) in self._relationship_options:
235 relationship_option = self._relationship_options[(cls, key)]
247 relationship_option = \
248 self._relationship_options[(cls, key)]
236 249 _set_cache_parameters(
237 250 query,
238 251 relationship_option.region,
239 252 relationship_option.namespace,
240 253 None)
241 254
242 255 def and_(self, option):
243 256 """Chain another RelationshipCache option to this one.
244 257
245 258 While many RelationshipCache objects can be specified on a single
246 259 Query separately, chaining them together allows for a more efficient
247 260 lookup during load.
248 261
249 262 """
250 263 self._relationship_options.update(option._relationship_options)
251 264 return self
252 265
253 266
254 267 def _params_from_query(query):
255 268 """Pull the bind parameter values from a query.
256 269
257 270 This takes into account any scalar attribute bindparam set up.
258 271
259 272 E.g. params_from_query(query.filter(Cls.foo==5).filter(Cls.bar==7)))
260 273 would return [5, 7].
261 274
262 275 """
263 276 v = []
277
264 278 def visit_bindparam(bind):
265 279 value = query._params.get(bind.key, bind.value)
266 280
267 281 # lazyloader may dig a callable in here, intended
268 282 # to late-evaluate params after autoflush is called.
269 283 # convert to a scalar value.
270 284 if callable(value):
271 285 value = value()
272 286
273 287 v.append(value)
274 288 if query._criterion is not None:
275 visitors.traverse(query._criterion, {}, {'bindparam':visit_bindparam})
289 visitors.traverse(query._criterion, {}, {'bindparam': visit_bindparam})
276 290 return v
@@ -1,456 +1,519 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.db
4 4 ~~~~~~~~~~~~~~~~~~
5 5
6 6 Database Models for RhodeCode
7 7
8 8 :created_on: Apr 08, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import os
27 27 import logging
28 28 import datetime
29 29 from datetime import date
30 30
31 31 from sqlalchemy import *
32 32 from sqlalchemy.exc import DatabaseError
33 33 from sqlalchemy.orm import relationship, backref
34 34 from sqlalchemy.orm.interfaces import MapperExtension
35 35
36 36 from rhodecode.lib import str2bool
37 37 from rhodecode.model.meta import Base, Session
38 38 from rhodecode.model.caching_query import FromCache
39 39
40 40 log = logging.getLogger(__name__)
41 41
42 42 #==============================================================================
43 43 # MAPPER EXTENSIONS
44 44 #==============================================================================
45 45
46 46 class RepositoryMapper(MapperExtension):
47 47 def after_update(self, mapper, connection, instance):
48 48 pass
49 49
50 50
51 51 class RhodeCodeSettings(Base):
52 52 __tablename__ = 'rhodecode_settings'
53 53 __table_args__ = (UniqueConstraint('app_settings_name'), {'useexisting':True})
54 54 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
55 55 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
56 56 app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
57 57
58 58 def __init__(self, k='', v=''):
59 59 self.app_settings_name = k
60 60 self.app_settings_value = v
61 61
62 62 def __repr__(self):
63 63 return "<%s('%s:%s')>" % (self.__class__.__name__,
64 64 self.app_settings_name, self.app_settings_value)
65 65
66 66
67 67 @classmethod
68 68 def get_app_settings(cls, cache=False):
69 69
70 70 ret = Session.query(cls)
71 71
72 72 if cache:
73 73 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
74 74
75 75 if not ret:
76 76 raise Exception('Could not get application settings !')
77 77 settings = {}
78 78 for each in ret:
79 79 settings['rhodecode_' + each.app_settings_name] = \
80 80 each.app_settings_value
81 81
82 82 return settings
83 83
84 84 @classmethod
85 85 def get_ldap_settings(cls, cache=False):
86 86 ret = Session.query(cls)\
87 87 .filter(cls.app_settings_name.startswith('ldap_'))\
88 88 .all()
89 89 fd = {}
90 90 for row in ret:
91 91 fd.update({row.app_settings_name:str2bool(row.app_settings_value)})
92 92 return fd
93 93
94 94
95 95 class RhodeCodeUi(Base):
96 96 __tablename__ = 'rhodecode_ui'
97 97 __table_args__ = {'useexisting':True}
98 98 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
99 99 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
100 100 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
101 101 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
102 102 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
103 103
104 104
105 105 class User(Base):
106 106 __tablename__ = 'users'
107 107 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'useexisting':True})
108 108 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
109 109 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
110 110 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
111 111 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
112 112 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
113 113 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
114 114 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
115 115 email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
116 116 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
117 117 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
118 118 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
119 119
120 120 user_log = relationship('UserLog', cascade='all')
121 121 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
122 122
123 123 repositories = relationship('Repository')
124 124 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
125 125
126 126 group_member = relationship('UsersGroupMember', cascade='all')
127 127
128 128 @property
129 129 def full_contact(self):
130 130 return '%s %s <%s>' % (self.name, self.lastname, self.email)
131 131
132 132 @property
133 133 def short_contact(self):
134 134 return '%s %s' % (self.name, self.lastname)
135 135
136 136
137 137 @property
138 138 def is_admin(self):
139 139 return self.admin
140 140
141 141 def __repr__(self):
142 142 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
143 143 self.user_id, self.username)
144 144
145 145 @classmethod
146 146 def by_username(cls, username):
147 147 return Session.query(cls).filter(cls.username == username).one()
148 148
149 149
150 150 def update_lastlogin(self):
151 151 """Update user lastlogin"""
152 152
153 153 try:
154 154 session = Session.object_session(self)
155 155 self.last_login = datetime.datetime.now()
156 156 session.add(self)
157 157 session.commit()
158 158 log.debug('updated user %s lastlogin', self.username)
159 159 except (DatabaseError,):
160 160 session.rollback()
161 161
162 162
163 163 class UserLog(Base):
164 164 __tablename__ = 'user_logs'
165 165 __table_args__ = {'useexisting':True}
166 166 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
167 167 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
168 168 repository_id = Column("repository_id", Integer(length=None, convert_unicode=False, assert_unicode=None), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
169 169 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
170 170 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
171 171 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
172 172 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
173 173
174 174 @property
175 175 def action_as_day(self):
176 176 return date(*self.action_date.timetuple()[:3])
177 177
178 178 user = relationship('User')
179 179 repository = relationship('Repository')
180 180
181 181
182 182 class UsersGroup(Base):
183 183 __tablename__ = 'users_groups'
184 184 __table_args__ = {'useexisting':True}
185 185
186 186 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
187 187 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
188 188 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
189 189
190 190 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
191 191
192
193 @classmethod
194 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
195 if case_insensitive:
196 gr = Session.query(cls)\
197 .filter(cls.users_group_name.ilike(group_name))
198 else:
199 gr = Session.query(UsersGroup)\
200 .filter(UsersGroup.users_group_name == group_name)
201 if cache:
202 gr = gr.options(FromCache("sql_cache_short",
203 "get_user_%s" % group_name))
204 return gr.scalar()
205
192 206 class UsersGroupMember(Base):
193 207 __tablename__ = 'users_groups_members'
194 208 __table_args__ = {'useexisting':True}
195 209
196 210 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
197 211 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
198 212 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
199 213
200 214 user = relationship('User', lazy='joined')
201 215 users_group = relationship('UsersGroup')
202 216
203 217 def __init__(self, gr_id='', u_id=''):
204 218 self.users_group_id = gr_id
205 219 self.user_id = u_id
206 220
207 221 class Repository(Base):
208 222 __tablename__ = 'repositories'
209 223 __table_args__ = (UniqueConstraint('repo_name'), {'useexisting':True},)
210 224 __mapper_args__ = {'extension':RepositoryMapper()}
211 225
212 226 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
213 227 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
214 228 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
215 229 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
216 230 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
217 231 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
218 232 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
219 233 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
220 234 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
221 235 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
222 236 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
223 237
224 238
225 239 user = relationship('User')
226 240 fork = relationship('Repository', remote_side=repo_id)
227 241 group = relationship('Group')
228 242 repo_to_perm = relationship('RepoToPerm', cascade='all', order_by='RepoToPerm.repo_to_perm_id')
229 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
243 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
230 244 stats = relationship('Statistics', cascade='all', uselist=False)
231 245
232 246 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
233 247
234 248 logs = relationship('UserLog', cascade='all')
235 249
236 250 def __repr__(self):
237 251 return "<%s('%s:%s')>" % (self.__class__.__name__,
238 252 self.repo_id, self.repo_name)
239 253
240 254 @classmethod
241 255 def by_repo_name(cls, repo_name):
242 256 return Session.query(cls).filter(cls.repo_name == repo_name).one()
243 257
244 258 @property
245 259 def just_name(self):
246 260 return self.repo_name.split(os.sep)[-1]
247 261
248 262 @property
249 263 def groups_with_parents(self):
250 264 groups = []
251 265 if self.group is None:
252 266 return groups
253 267
254 268 cur_gr = self.group
255 269 groups.insert(0, cur_gr)
256 270 while 1:
257 271 gr = getattr(cur_gr, 'parent_group', None)
258 272 cur_gr = cur_gr.parent_group
259 273 if gr is None:
260 274 break
261 275 groups.insert(0, gr)
262 276
263 277 return groups
264 278
265 279 @property
266 280 def groups_and_repo(self):
267 281 return self.groups_with_parents, self.just_name
268 282
269 283
270 284 class Group(Base):
271 285 __tablename__ = 'groups'
272 286 __table_args__ = (UniqueConstraint('group_name'), {'useexisting':True},)
273 287
274 288 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
275 289 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
276 290 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
277 291
278 292 parent_group = relationship('Group', remote_side=group_id)
279 293
280 294
281 295 def __init__(self, group_name='', parent_group=None):
282 296 self.group_name = group_name
283 297 self.parent_group = parent_group
284 298
285 299 def __repr__(self):
286 300 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
287 301 self.group_name)
288 302
289 303 @property
290 304 def parents(self):
291 305 groups = []
292 306 if self.parent_group is None:
293 307 return groups
294 308 cur_gr = self.parent_group
295 309 groups.insert(0, cur_gr)
296 310 while 1:
297 311 gr = getattr(cur_gr, 'parent_group', None)
298 312 cur_gr = cur_gr.parent_group
299 313 if gr is None:
300 314 break
301 315 groups.insert(0, gr)
302 316 return groups
303 317
304 318 @property
305 319 def repositories(self):
306 320 return Session.query(Repository).filter(Repository.group == self).all()
307 321
308 322 class Permission(Base):
309 323 __tablename__ = 'permissions'
310 324 __table_args__ = {'useexisting':True}
311 325 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
312 326 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
313 327 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
314 328
315 329 def __repr__(self):
316 330 return "<%s('%s:%s')>" % (self.__class__.__name__,
317 331 self.permission_id, self.permission_name)
318 332
319 333 @classmethod
320 334 def get_by_key(cls, key):
321 335 return Session.query(cls).filter(cls.permission_name == key).scalar()
322 336
323 337 class RepoToPerm(Base):
324 338 __tablename__ = 'repo_to_perm'
325 339 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'useexisting':True})
326 340 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
327 341 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
328 342 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
329 343 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
330 344
331 345 user = relationship('User')
332 346 permission = relationship('Permission')
333 347 repository = relationship('Repository')
334 348
335 349 class UserToPerm(Base):
336 350 __tablename__ = 'user_to_perm'
337 351 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'useexisting':True})
338 352 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
339 353 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
340 354 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
341 355
342 356 user = relationship('User')
343 357 permission = relationship('Permission')
344 358
345 359 @classmethod
346 360 def has_perm(cls, user_id, perm):
347 361 if not isinstance(perm, Permission):
348 362 raise Exception('perm needs to be an instance of Permission class')
349 363
350 364 return Session.query(cls).filter(cls.user_id == user_id)\
351 365 .filter(cls.permission == perm).scalar() is not None
352 366
353 367 @classmethod
354 368 def grant_perm(cls, user_id, perm):
355 369 if not isinstance(perm, Permission):
356 370 raise Exception('perm needs to be an instance of Permission class')
357 371
358 372 new = cls()
359 373 new.user_id = user_id
360 374 new.permission = perm
361 375 try:
362 376 Session.add(new)
363 377 Session.commit()
364 378 except:
365 379 Session.rollback()
366 380
367 381
368 382 @classmethod
369 383 def revoke_perm(cls, user_id, perm):
370 384 if not isinstance(perm, Permission):
371 385 raise Exception('perm needs to be an instance of Permission class')
372 386
373 387 try:
374 388 Session.query(cls).filter(cls.user_id == user_id)\
375 389 .filter(cls.permission == perm).delete()
376 390 Session.commit()
377 391 except:
378 392 Session.rollback()
379 393
380 class UsersGroupToPerm(Base):
381 __tablename__ = 'users_group_to_perm'
394 class UsersGroupRepoToPerm(Base):
395 __tablename__ = 'users_group_repo_to_perm'
382 396 __table_args__ = (UniqueConstraint('users_group_id', 'permission_id'), {'useexisting':True})
383 397 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
384 398 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
385 399 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
386 400 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
387 401
388 402 users_group = relationship('UsersGroup')
389 403 permission = relationship('Permission')
390 404 repository = relationship('Repository')
391 405
406
407 class UsersGroupToPerm(Base):
408 __tablename__ = 'users_group_to_perm'
409 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
410 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
411 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
412
413 users_group = relationship('UsersGroup')
414 permission = relationship('Permission')
415
416
417 @classmethod
418 def has_perm(cls, users_group_id, perm):
419 if not isinstance(perm, Permission):
420 raise Exception('perm needs to be an instance of Permission class')
421
422 return Session.query(cls).filter(cls.users_group_id ==
423 users_group_id)\
424 .filter(cls.permission == perm)\
425 .scalar() is not None
426
427 @classmethod
428 def grant_perm(cls, users_group_id, perm):
429 if not isinstance(perm, Permission):
430 raise Exception('perm needs to be an instance of Permission class')
431
432 new = cls()
433 new.users_group_id = users_group_id
434 new.permission = perm
435 try:
436 Session.add(new)
437 Session.commit()
438 except:
439 Session.rollback()
440
441
442 @classmethod
443 def revoke_perm(cls, users_group_id, perm):
444 if not isinstance(perm, Permission):
445 raise Exception('perm needs to be an instance of Permission class')
446
447 try:
448 Session.query(cls).filter(cls.users_group_id == users_group_id)\
449 .filter(cls.permission == perm).delete()
450 Session.commit()
451 except:
452 Session.rollback()
453
454
392 455 class GroupToPerm(Base):
393 456 __tablename__ = 'group_to_perm'
394 457 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'useexisting':True})
395 458
396 459 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
397 460 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
398 461 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
399 462 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
400 463
401 464 user = relationship('User')
402 465 permission = relationship('Permission')
403 466 group = relationship('Group')
404 467
405 468 class Statistics(Base):
406 469 __tablename__ = 'statistics'
407 470 __table_args__ = (UniqueConstraint('repository_id'), {'useexisting':True})
408 471 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
409 472 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
410 473 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
411 474 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
412 475 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
413 476 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
414 477
415 478 repository = relationship('Repository', single_parent=True)
416 479
417 480 class UserFollowing(Base):
418 481 __tablename__ = 'user_followings'
419 482 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
420 483 UniqueConstraint('user_id', 'follows_user_id')
421 484 , {'useexisting':True})
422 485
423 486 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
424 487 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
425 488 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
426 489 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
427 490
428 491 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
429 492
430 493 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
431 494 follows_repository = relationship('Repository', order_by='Repository.repo_name')
432 495
433 496 class CacheInvalidation(Base):
434 497 __tablename__ = 'cache_invalidation'
435 498 __table_args__ = (UniqueConstraint('cache_key'), {'useexisting':True})
436 499 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
437 500 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
438 501 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
439 502 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
440 503
441 504
442 505 def __init__(self, cache_key, cache_args=''):
443 506 self.cache_key = cache_key
444 507 self.cache_args = cache_args
445 508 self.cache_active = False
446 509
447 510 def __repr__(self):
448 511 return "<%s('%s:%s')>" % (self.__class__.__name__,
449 512 self.cache_id, self.cache_key)
450 513
451 514 class DbMigrateVersion(Base):
452 515 __tablename__ = 'db_migrate_version'
453 516 __table_args__ = {'useexisting':True}
454 517 repository_id = Column('repository_id', String(250), primary_key=True)
455 518 repository_path = Column('repository_path', Text)
456 519 version = Column('version', Integer)
@@ -1,583 +1,579 b''
1 1 """ this is forms validation classes
2 2 http://formencode.org/module-formencode.validators.html
3 3 for list off all availible validators
4 4
5 5 we can create our own validators
6 6
7 7 The table below outlines the options which can be used in a schema in addition to the validators themselves
8 8 pre_validators [] These validators will be applied before the schema
9 9 chained_validators [] These validators will be applied after the schema
10 10 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
11 11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
12 12 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
13 13 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
14 14
15 15
16 16 <name> = formencode.validators.<name of validator>
17 17 <name> must equal form name
18 18 list=[1,2,3,4,5]
19 19 for SELECT use formencode.All(OneOf(list), Int())
20 20
21 21 """
22 22 import os
23 23 import re
24 24 import logging
25 25
26 26 import formencode
27 27 from formencode import All
28 28 from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \
29 29 Email, Bool, StringBoolean, Set
30 30
31 31 from pylons.i18n.translation import _
32 32 from webhelpers.pylonslib.secure_form import authentication_token
33 33
34 34 from rhodecode.lib.utils import repo_name_slug
35 35 from rhodecode.lib.auth import authenticate, get_crypt_password
36 36 from rhodecode.lib.exceptions import LdapImportError
37 37 from rhodecode.model import meta
38 38 from rhodecode.model.user import UserModel
39 39 from rhodecode.model.repo import RepoModel
40 from rhodecode.model.users_group import UsersGroupModel
41 40 from rhodecode.model.db import User, UsersGroup
42 41 from rhodecode import BACKENDS
43 42
44 43 log = logging.getLogger(__name__)
45 44
46 45 #this is needed to translate the messages using _() in validators
47 46 class State_obj(object):
48 47 _ = staticmethod(_)
49 48
50 #===============================================================================
49 #==============================================================================
51 50 # VALIDATORS
52 #===============================================================================
51 #==============================================================================
53 52 class ValidAuthToken(formencode.validators.FancyValidator):
54 53 messages = {'invalid_token':_('Token mismatch')}
55 54
56 55 def validate_python(self, value, state):
57 56
58 57 if value != authentication_token():
59 58 raise formencode.Invalid(self.message('invalid_token', state,
60 59 search_number=value), value, state)
61 60
62 61 def ValidUsername(edit, old_data):
63 62 class _ValidUsername(formencode.validators.FancyValidator):
64 63
65 64 def validate_python(self, value, state):
66 65 if value in ['default', 'new_user']:
67 66 raise formencode.Invalid(_('Invalid username'), value, state)
68 67 #check if user is unique
69 68 old_un = None
70 69 if edit:
71 70 old_un = UserModel().get(old_data.get('user_id')).username
72 71
73 72 if old_un != value or not edit:
74 73 if UserModel().get_by_username(value, cache=False,
75 74 case_insensitive=True):
76 raise formencode.Invalid(_('This username already exists') ,
77 value, state)
78
75 raise formencode.Invalid(_('This username already '
76 'exists') , value, state)
79 77
80 78 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
81 79 raise formencode.Invalid(_('Username may only contain '
82 'alphanumeric characters underscores, '
83 'periods or dashes and must begin with '
84 'alphanumeric character'),
85 value, state)
86
87
80 'alphanumeric characters '
81 'underscores, periods or dashes '
82 'and must begin with alphanumeric '
83 'character'), value, state)
88 84
89 85 return _ValidUsername
90 86
91 87
92
93 88 def ValidUsersGroup(edit, old_data):
94 89
95 90 class _ValidUsersGroup(formencode.validators.FancyValidator):
96 91
97 92 def validate_python(self, value, state):
98 93 if value in ['default']:
99 94 raise formencode.Invalid(_('Invalid group name'), value, state)
100 95 #check if group is unique
101 96 old_ugname = None
102 97 if edit:
103 old_ugname = UsersGroupModel()\
104 .get(old_data.get('users_group_id')).users_group_name
98 old_ugname = UsersGroup.get(
99 old_data.get('users_group_id')).users_group_name
105 100
106 101 if old_ugname != value or not edit:
107 if UsersGroupModel().get_by_groupname(value, cache=False,
102 if UsersGroup.get_by_group_name(value, cache=False,
108 103 case_insensitive=True):
109 raise formencode.Invalid(_('This users group already exists') ,
110 value, state)
104 raise formencode.Invalid(_('This users group '
105 'already exists') , value,
106 state)
111 107
112 108
113 109 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
114 110 raise formencode.Invalid(_('Group name may only contain '
115 'alphanumeric characters underscores, '
116 'periods or dashes and must begin with '
117 'alphanumeric character'),
118 value, state)
111 'alphanumeric characters '
112 'underscores, periods or dashes '
113 'and must begin with alphanumeric '
114 'character'), value, state)
119 115
120 116 return _ValidUsersGroup
121 117
122 118
123 119
124 120 class ValidPassword(formencode.validators.FancyValidator):
125 121
126 122 def to_python(self, value, state):
127 123
128 124 if value:
129 125
130 126 if value.get('password'):
131 127 try:
132 128 value['password'] = get_crypt_password(value['password'])
133 129 except UnicodeEncodeError:
134 130 e_dict = {'password':_('Invalid characters in password')}
135 131 raise formencode.Invalid('', value, state, error_dict=e_dict)
136 132
137 133 if value.get('password_confirmation'):
138 134 try:
139 135 value['password_confirmation'] = \
140 136 get_crypt_password(value['password_confirmation'])
141 137 except UnicodeEncodeError:
142 138 e_dict = {'password_confirmation':_('Invalid characters in password')}
143 139 raise formencode.Invalid('', value, state, error_dict=e_dict)
144 140
145 141 if value.get('new_password'):
146 142 try:
147 143 value['new_password'] = \
148 144 get_crypt_password(value['new_password'])
149 145 except UnicodeEncodeError:
150 146 e_dict = {'new_password':_('Invalid characters in password')}
151 147 raise formencode.Invalid('', value, state, error_dict=e_dict)
152 148
153 149 return value
154 150
155 151 class ValidPasswordsMatch(formencode.validators.FancyValidator):
156 152
157 153 def validate_python(self, value, state):
158 154
159 155 if value['password'] != value['password_confirmation']:
160 156 e_dict = {'password_confirmation':
161 157 _('Password do not match')}
162 158 raise formencode.Invalid('', value, state, error_dict=e_dict)
163 159
164 160 class ValidAuth(formencode.validators.FancyValidator):
165 161 messages = {
166 162 'invalid_password':_('invalid password'),
167 163 'invalid_login':_('invalid user name'),
168 164 'disabled_account':_('Your account is disabled')
169 165
170 166 }
171 167 #error mapping
172 168 e_dict = {'username':messages['invalid_login'],
173 169 'password':messages['invalid_password']}
174 170 e_dict_disable = {'username':messages['disabled_account']}
175 171
176 172 def validate_python(self, value, state):
177 173 password = value['password']
178 174 username = value['username']
179 175 user = UserModel().get_by_username(username)
180 176
181 177 if authenticate(username, password):
182 178 return value
183 179 else:
184 180 if user and user.active is False:
185 181 log.warning('user %s is disabled', username)
186 182 raise formencode.Invalid(self.message('disabled_account',
187 183 state=State_obj),
188 184 value, state,
189 185 error_dict=self.e_dict_disable)
190 186 else:
191 187 log.warning('user %s not authenticated', username)
192 188 raise formencode.Invalid(self.message('invalid_password',
193 189 state=State_obj), value, state,
194 190 error_dict=self.e_dict)
195 191
196 192 class ValidRepoUser(formencode.validators.FancyValidator):
197 193
198 194 def to_python(self, value, state):
199 195 sa = meta.Session()
200 196 try:
201 197 self.user_db = sa.query(User)\
202 198 .filter(User.active == True)\
203 199 .filter(User.username == value).one()
204 200 except Exception:
205 201 raise formencode.Invalid(_('This username is not valid'),
206 202 value, state)
207 203 finally:
208 204 meta.Session.remove()
209 205
210 206 return self.user_db.user_id
211 207
212 208 def ValidRepoName(edit, old_data):
213 209 class _ValidRepoName(formencode.validators.FancyValidator):
214 210
215 211 def to_python(self, value, state):
216 212 slug = repo_name_slug(value)
217 213 if slug in ['_admin']:
218 214 raise formencode.Invalid(_('This repository name is disallowed'),
219 215 value, state)
220 216 if old_data.get('repo_name') != value or not edit:
221 217 if RepoModel().get_by_repo_name(slug, cache=False):
222 218 raise formencode.Invalid(_('This repository already exists') ,
223 219 value, state)
224 220 return slug
225 221
226 222
227 223 return _ValidRepoName
228 224
229 225 def ValidCloneUri():
230 226 from mercurial.httprepo import httprepository, httpsrepository
231 227 from rhodecode.lib.utils import make_ui
232 228
233 229 class _ValidCloneUri(formencode.validators.FancyValidator):
234 230 def to_python(self, value, state):
235 231 if not value:
236 232 pass
237 233 elif value.startswith('https'):
238 234 try:
239 235 httpsrepository(make_ui('db'), value).capabilities()
240 236 except:
241 237 raise formencode.Invalid(_('invalid clone url'), value,
242 238 state)
243 239 elif value.startswith('http'):
244 240 try:
245 241 httprepository(make_ui('db'), value).capabilities()
246 242 except:
247 243 raise formencode.Invalid(_('invalid clone url'), value,
248 244 state)
249 245 else:
250 246 raise formencode.Invalid(_('Invalid clone url, provide a '
251 247 'valid clone http\s url'), value,
252 248 state)
253 249
254 250 return _ValidCloneUri
255 251
256 252 def ValidForkType(old_data):
257 253 class _ValidForkType(formencode.validators.FancyValidator):
258 254
259 255 def to_python(self, value, state):
260 256 if old_data['repo_type'] != value:
261 257 raise formencode.Invalid(_('Fork have to be the same '
262 258 'type as original'), value, state)
263 259 return value
264 260 return _ValidForkType
265 261
266 262 class ValidPerms(formencode.validators.FancyValidator):
267 263 messages = {'perm_new_member_name':_('This username or users group name'
268 264 ' is not valid')}
269 265
270 266 def to_python(self, value, state):
271 267 perms_update = []
272 268 perms_new = []
273 269 #build a list of permission to update and new permission to create
274 270 for k, v in value.items():
275 271 #means new added member to permissions
276 272 if k.startswith('perm_new_member'):
277 273 new_perm = value.get('perm_new_member', False)
278 274 new_member = value.get('perm_new_member_name', False)
279 275 new_type = value.get('perm_new_member_type')
280 276
281 277 if new_member and new_perm:
282 278 if (new_member, new_perm, new_type) not in perms_new:
283 279 perms_new.append((new_member, new_perm, new_type))
284 280 elif k.startswith('u_perm_') or k.startswith('g_perm_'):
285 281 member = k[7:]
286 282 t = {'u':'user',
287 283 'g':'users_group'}[k[0]]
288 284 if member == 'default':
289 285 if value['private']:
290 286 #set none for default when updating to private repo
291 287 v = 'repository.none'
292 288 perms_update.append((member, v, t))
293 289
294 290 value['perms_updates'] = perms_update
295 291 value['perms_new'] = perms_new
296 292
297 293 #update permissions
298 294 sa = meta.Session
299 295 for k, v, t in perms_new:
300 296 try:
301 297 if t is 'user':
302 298 self.user_db = sa.query(User)\
303 299 .filter(User.active == True)\
304 300 .filter(User.username == k).one()
305 301 if t is 'users_group':
306 302 self.user_db = sa.query(UsersGroup)\
307 303 .filter(UsersGroup.users_group_active == True)\
308 304 .filter(UsersGroup.users_group_name == k).one()
309 305
310 306 except Exception:
311 307 msg = self.message('perm_new_member_name',
312 308 state=State_obj)
313 309 raise formencode.Invalid(msg, value, state,
314 310 error_dict={'perm_new_member_name':msg})
315 311 return value
316 312
317 313 class ValidSettings(formencode.validators.FancyValidator):
318 314
319 315 def to_python(self, value, state):
320 316 #settings form can't edit user
321 317 if value.has_key('user'):
322 318 del['value']['user']
323 319
324 320 return value
325 321
326 322 class ValidPath(formencode.validators.FancyValidator):
327 323 def to_python(self, value, state):
328 324
329 325 if not os.path.isdir(value):
330 326 msg = _('This is not a valid path')
331 327 raise formencode.Invalid(msg, value, state,
332 328 error_dict={'paths_root_path':msg})
333 329 return value
334 330
335 331 def UniqSystemEmail(old_data):
336 332 class _UniqSystemEmail(formencode.validators.FancyValidator):
337 333 def to_python(self, value, state):
338 334 value = value.lower()
339 335 if old_data.get('email') != value:
340 336 sa = meta.Session()
341 337 try:
342 338 user = sa.query(User).filter(User.email == value).scalar()
343 339 if user:
344 340 raise formencode.Invalid(_("This e-mail address is already taken") ,
345 341 value, state)
346 342 finally:
347 343 meta.Session.remove()
348 344
349 345 return value
350 346
351 347 return _UniqSystemEmail
352 348
353 349 class ValidSystemEmail(formencode.validators.FancyValidator):
354 350 def to_python(self, value, state):
355 351 value = value.lower()
356 352 sa = meta.Session
357 353 try:
358 354 user = sa.query(User).filter(User.email == value).scalar()
359 355 if user is None:
360 356 raise formencode.Invalid(_("This e-mail address doesn't exist.") ,
361 357 value, state)
362 358 finally:
363 359 meta.Session.remove()
364 360
365 361 return value
366 362
367 363 class LdapLibValidator(formencode.validators.FancyValidator):
368 364
369 365 def to_python(self, value, state):
370 366
371 367 try:
372 368 import ldap
373 369 except ImportError:
374 370 raise LdapImportError
375 371 return value
376 372
377 373 class AttrLoginValidator(formencode.validators.FancyValidator):
378 374
379 375 def to_python(self, value, state):
380 376
381 377 if not value or not isinstance(value, (str, unicode)):
382 378 raise formencode.Invalid(_("The LDAP Login attribute of the CN "
383 379 "must be specified - this is the name "
384 380 "of the attribute that is equivalent "
385 381 "to 'username'"),
386 382 value, state)
387 383
388 384 return value
389 385
390 386 #===============================================================================
391 387 # FORMS
392 388 #===============================================================================
393 389 class LoginForm(formencode.Schema):
394 390 allow_extra_fields = True
395 391 filter_extra_fields = True
396 392 username = UnicodeString(
397 393 strip=True,
398 394 min=1,
399 395 not_empty=True,
400 396 messages={
401 397 'empty':_('Please enter a login'),
402 398 'tooShort':_('Enter a value %(min)i characters long or more')}
403 399 )
404 400
405 401 password = UnicodeString(
406 402 strip=True,
407 403 min=6,
408 404 not_empty=True,
409 405 messages={
410 406 'empty':_('Please enter a password'),
411 407 'tooShort':_('Enter %(min)i characters or more')}
412 408 )
413 409
414 410
415 411 #chained validators have access to all data
416 412 chained_validators = [ValidAuth]
417 413
418 414 def UserForm(edit=False, old_data={}):
419 415 class _UserForm(formencode.Schema):
420 416 allow_extra_fields = True
421 417 filter_extra_fields = True
422 418 username = All(UnicodeString(strip=True, min=1, not_empty=True),
423 419 ValidUsername(edit, old_data))
424 420 if edit:
425 421 new_password = All(UnicodeString(strip=True, min=6, not_empty=False))
426 422 admin = StringBoolean(if_missing=False)
427 423 else:
428 424 password = All(UnicodeString(strip=True, min=6, not_empty=True))
429 425 active = StringBoolean(if_missing=False)
430 426 name = UnicodeString(strip=True, min=1, not_empty=True)
431 427 lastname = UnicodeString(strip=True, min=1, not_empty=True)
432 428 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
433 429
434 430 chained_validators = [ValidPassword]
435 431
436 432 return _UserForm
437 433
438 434
439 435 def UsersGroupForm(edit=False, old_data={}, available_members=[]):
440 436 class _UsersGroupForm(formencode.Schema):
441 437 allow_extra_fields = True
442 438 filter_extra_fields = True
443 439
444 440 users_group_name = All(UnicodeString(strip=True, min=1, not_empty=True),
445 441 ValidUsersGroup(edit, old_data))
446 442
447 443 users_group_active = StringBoolean(if_missing=False)
448 444
449 445 if edit:
450 446 users_group_members = OneOf(available_members, hideList=False,
451 447 testValueList=True,
452 448 if_missing=None, not_empty=False)
453 449
454 450 return _UsersGroupForm
455 451
456 452 def RegisterForm(edit=False, old_data={}):
457 453 class _RegisterForm(formencode.Schema):
458 454 allow_extra_fields = True
459 455 filter_extra_fields = True
460 456 username = All(ValidUsername(edit, old_data),
461 457 UnicodeString(strip=True, min=1, not_empty=True))
462 458 password = All(UnicodeString(strip=True, min=6, not_empty=True))
463 459 password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=True))
464 460 active = StringBoolean(if_missing=False)
465 461 name = UnicodeString(strip=True, min=1, not_empty=True)
466 462 lastname = UnicodeString(strip=True, min=1, not_empty=True)
467 463 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
468 464
469 465 chained_validators = [ValidPasswordsMatch, ValidPassword]
470 466
471 467 return _RegisterForm
472 468
473 469 def PasswordResetForm():
474 470 class _PasswordResetForm(formencode.Schema):
475 471 allow_extra_fields = True
476 472 filter_extra_fields = True
477 473 email = All(ValidSystemEmail(), Email(not_empty=True))
478 474 return _PasswordResetForm
479 475
480 476 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
481 477 repo_groups=[]):
482 478 class _RepoForm(formencode.Schema):
483 479 allow_extra_fields = True
484 480 filter_extra_fields = False
485 481 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
486 482 ValidRepoName(edit, old_data))
487 483 clone_uri = All(UnicodeString(strip=True, min=1, not_empty=False),
488 484 ValidCloneUri()())
489 485 repo_group = OneOf(repo_groups, hideList=True)
490 486 repo_type = OneOf(supported_backends)
491 487 description = UnicodeString(strip=True, min=1, not_empty=True)
492 488 private = StringBoolean(if_missing=False)
493 489 enable_statistics = StringBoolean(if_missing=False)
494 490 enable_downloads = StringBoolean(if_missing=False)
495 491
496 492 if edit:
497 493 #this is repo owner
498 494 user = All(Int(not_empty=True), ValidRepoUser)
499 495
500 496 chained_validators = [ValidPerms]
501 497 return _RepoForm
502 498
503 499 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
504 500 class _RepoForkForm(formencode.Schema):
505 501 allow_extra_fields = True
506 502 filter_extra_fields = False
507 503 fork_name = All(UnicodeString(strip=True, min=1, not_empty=True),
508 504 ValidRepoName(edit, old_data))
509 505 description = UnicodeString(strip=True, min=1, not_empty=True)
510 506 private = StringBoolean(if_missing=False)
511 507 repo_type = All(ValidForkType(old_data), OneOf(supported_backends))
512 508 return _RepoForkForm
513 509
514 510 def RepoSettingsForm(edit=False, old_data={}):
515 511 class _RepoForm(formencode.Schema):
516 512 allow_extra_fields = True
517 513 filter_extra_fields = False
518 514 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
519 515 ValidRepoName(edit, old_data))
520 516 description = UnicodeString(strip=True, min=1, not_empty=True)
521 517 private = StringBoolean(if_missing=False)
522 518
523 519 chained_validators = [ValidPerms, ValidSettings]
524 520 return _RepoForm
525 521
526 522
527 523 def ApplicationSettingsForm():
528 524 class _ApplicationSettingsForm(formencode.Schema):
529 525 allow_extra_fields = True
530 526 filter_extra_fields = False
531 527 rhodecode_title = UnicodeString(strip=True, min=1, not_empty=True)
532 528 rhodecode_realm = UnicodeString(strip=True, min=1, not_empty=True)
533 529 rhodecode_ga_code = UnicodeString(strip=True, min=1, not_empty=False)
534 530
535 531 return _ApplicationSettingsForm
536 532
537 533 def ApplicationUiSettingsForm():
538 534 class _ApplicationUiSettingsForm(formencode.Schema):
539 535 allow_extra_fields = True
540 536 filter_extra_fields = False
541 537 web_push_ssl = OneOf(['true', 'false'], if_missing='false')
542 538 paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=1, not_empty=True))
543 539 hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
544 540 hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
545 541 hooks_pretxnchangegroup_push_logger = OneOf(['True', 'False'], if_missing=False)
546 542 hooks_preoutgoing_pull_logger = OneOf(['True', 'False'], if_missing=False)
547 543
548 544 return _ApplicationUiSettingsForm
549 545
550 546 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
551 547 class _DefaultPermissionsForm(formencode.Schema):
552 548 allow_extra_fields = True
553 549 filter_extra_fields = True
554 550 overwrite_default = StringBoolean(if_missing=False)
555 551 anonymous = OneOf(['True', 'False'], if_missing=False)
556 552 default_perm = OneOf(perms_choices)
557 553 default_register = OneOf(register_choices)
558 554 default_create = OneOf(create_choices)
559 555
560 556 return _DefaultPermissionsForm
561 557
562 558
563 559 def LdapSettingsForm(tls_reqcert_choices, search_scope_choices):
564 560 class _LdapSettingsForm(formencode.Schema):
565 561 allow_extra_fields = True
566 562 filter_extra_fields = True
567 563 pre_validators = [LdapLibValidator]
568 564 ldap_active = StringBoolean(if_missing=False)
569 565 ldap_host = UnicodeString(strip=True,)
570 566 ldap_port = Number(strip=True,)
571 567 ldap_ldaps = StringBoolean(if_missing=False)
572 568 ldap_tls_reqcert = OneOf(tls_reqcert_choices)
573 569 ldap_dn_user = UnicodeString(strip=True,)
574 570 ldap_dn_pass = UnicodeString(strip=True,)
575 571 ldap_base_dn = UnicodeString(strip=True,)
576 572 ldap_filter = UnicodeString(strip=True,)
577 573 ldap_search_scope = OneOf(search_scope_choices)
578 574 ldap_attr_login = All(AttrLoginValidator, UnicodeString(strip=True,))
579 575 ldap_attr_firstname = UnicodeString(strip=True,)
580 576 ldap_attr_lastname = UnicodeString(strip=True,)
581 577 ldap_attr_email = UnicodeString(strip=True,)
582 578
583 579 return _LdapSettingsForm
@@ -1,70 +1,71 b''
1 1 """SQLAlchemy Metadata and Session object"""
2 2 from sqlalchemy.ext.declarative import declarative_base
3 3 from sqlalchemy.orm import scoped_session, sessionmaker, class_mapper
4 4 from beaker import cache
5 5
6 6 from rhodecode.model import caching_query
7 7
8 8
9 9 # Beaker CacheManager. A home base for cache configurations.
10 10 cache_manager = cache.CacheManager()
11 11
12 12 __all__ = ['Base', 'Session']
13 13 #
14 14 # SQLAlchemy session manager. Updated by model.init_model()
15 15 #
16 16 Session = scoped_session(
17 17 sessionmaker(
18 18 query_cls=caching_query.query_callable(cache_manager)
19 19 )
20 20 )
21 21
22
22 23 class BaseModel(object):
23 24 """Base Model for all classess
24 25
25 26 """
26 27
27 28 @classmethod
28 29 def _get_keys(cls):
29 30 """return column names for this model """
30 31 return class_mapper(cls).c.keys()
31 32
32 33 def get_dict(self):
33 34 """return dict with keys and values corresponding
34 35 to this model data """
35 36
36 37 d = {}
37 38 for k in self._get_keys():
38 39 d[k] = getattr(self, k)
39 40 return d
40 41
41 42 def get_appstruct(self):
42 43 """return list with keys and values tupples corresponding
43 44 to this model data """
44 45
45 46 l = []
46 47 for k in self._get_keys():
47 48 l.append((k, getattr(self, k),))
48 49 return l
49 50
50 51 def populate_obj(self, populate_dict):
51 52 """populate model with data from given populate_dict"""
52 53
53 54 for k in self._get_keys():
54 55 if k in populate_dict:
55 56 setattr(self, k, populate_dict[k])
56 57
57 58 @classmethod
58 59 def query(cls):
59 60 return Session.query(cls)
60 61
61 62 @classmethod
62 63 def get(cls, id_):
63 64 return Session.query(cls).get(id_)
64 65
65 66
66 67 # The declarative Base
67 68 Base = declarative_base(cls=BaseModel)
68 69
69 70 #to use cache use this in query
70 71 #.options(FromCache("sqlalchemy_cache_type", "cachekey"))
@@ -1,112 +1,113 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.permission
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 permissions model for RhodeCode
7 7
8 8 :created_on: Aug 20, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import logging
27 27 import traceback
28 28
29 29 from sqlalchemy.exc import DatabaseError
30 30
31 31 from rhodecode.model import BaseModel
32 32 from rhodecode.model.db import User, Permission, UserToPerm, RepoToPerm
33 33 from rhodecode.model.caching_query import FromCache
34 34
35 35 log = logging.getLogger(__name__)
36 36
37 37
38 38 class PermissionModel(BaseModel):
39 39 """Permissions model for RhodeCode
40 40 """
41 41
42 42 def get_permission(self, permission_id, cache=False):
43 43 """Get's permissions by id
44 44
45 45 :param permission_id: id of permission to get from database
46 46 :param cache: use Cache for this query
47 47 """
48 48 perm = self.sa.query(Permission)
49 49 if cache:
50 50 perm = perm.options(FromCache("sql_cache_short",
51 51 "get_permission_%s" % permission_id))
52 52 return perm.get(permission_id)
53 53
54 54 def get_permission_by_name(self, name, cache=False):
55 55 """Get's permissions by given name
56 56
57 57 :param name: name to fetch
58 58 :param cache: Use cache for this query
59 59 """
60 60 perm = self.sa.query(Permission)\
61 61 .filter(Permission.permission_name == name)
62 62 if cache:
63 63 perm = perm.options(FromCache("sql_cache_short",
64 64 "get_permission_%s" % name))
65 65 return perm.scalar()
66 66
67 67 def update(self, form_result):
68 68 perm_user = self.sa.query(User)\
69 .filter(User.username == form_result['perm_user_name']).scalar()
70 u2p = self.sa.query(UserToPerm).filter(UserToPerm.user == perm_user).all()
69 .filter(User.username ==
70 form_result['perm_user_name']).scalar()
71 u2p = self.sa.query(UserToPerm).filter(UserToPerm.user ==
72 perm_user).all()
71 73 if len(u2p) != 3:
72 74 raise Exception('Defined: %s should be 3 permissions for default'
73 75 ' user. This should not happen please verify'
74 76 ' your database' % len(u2p))
75 77
76 78 try:
77 79 #stage 1 change defaults
78 80 for p in u2p:
79 81 if p.permission.permission_name.startswith('repository.'):
80 82 p.permission = self.get_permission_by_name(
81 83 form_result['default_perm'])
82 84 self.sa.add(p)
83 85
84 86 if p.permission.permission_name.startswith('hg.register.'):
85 87 p.permission = self.get_permission_by_name(
86 88 form_result['default_register'])
87 89 self.sa.add(p)
88 90
89 91 if p.permission.permission_name.startswith('hg.create.'):
90 92 p.permission = self.get_permission_by_name(
91 93 form_result['default_create'])
92 94 self.sa.add(p)
93 95
94 96 #stage 2 update all default permissions for repos if checked
95 97 if form_result['overwrite_default'] == True:
96 98 for r2p in self.sa.query(RepoToPerm)\
97 99 .filter(RepoToPerm.user == perm_user).all():
98 100 r2p.permission = self.get_permission_by_name(
99 101 form_result['default_perm'])
100 102 self.sa.add(r2p)
101 103
102 104 #stage 3 set anonymous access
103 105 if perm_user.username == 'default':
104 106 perm_user.active = bool(form_result['anonymous'])
105 107 self.sa.add(perm_user)
106 108
107
108 109 self.sa.commit()
109 110 except (DatabaseError,):
110 111 log.error(traceback.format_exc())
111 112 self.sa.rollback()
112 113 raise
@@ -1,352 +1,349 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.repo
4 4 ~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Repository model for rhodecode
7 7
8 8 :created_on: Jun 5, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25 import os
26 26 import shutil
27 27 import logging
28 28 import traceback
29 29 from datetime import datetime
30 30
31 31 from sqlalchemy.orm import joinedload, make_transient
32 32
33 33 from vcs.utils.lazy import LazyProperty
34 34 from vcs.backends import get_backend
35 35
36 36 from rhodecode.model import BaseModel
37 37 from rhodecode.model.caching_query import FromCache
38 38 from rhodecode.model.db import Repository, RepoToPerm, User, Permission, \
39 Statistics, UsersGroup, UsersGroupToPerm, RhodeCodeUi
39 Statistics, UsersGroup, UsersGroupRepoToPerm, RhodeCodeUi
40 40 from rhodecode.model.user import UserModel
41 from rhodecode.model.users_group import UsersGroupMember, UsersGroupModel
42
43 41
44 42 log = logging.getLogger(__name__)
45 43
44
46 45 class RepoModel(BaseModel):
47 46
48 47 @LazyProperty
49 48 def repos_path(self):
50 49 """Get's the repositories root path from database
51 50 """
52 51
53 52 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
54 53 return q.ui_value
55 54
56 55 def get(self, repo_id, cache=False):
57 56 repo = self.sa.query(Repository)\
58 57 .filter(Repository.repo_id == repo_id)
59 58
60 59 if cache:
61 60 repo = repo.options(FromCache("sql_cache_short",
62 61 "get_repo_%s" % repo_id))
63 62 return repo.scalar()
64 63
65
66 64 def get_by_repo_name(self, repo_name, cache=False):
67 65 repo = self.sa.query(Repository)\
68 66 .filter(Repository.repo_name == repo_name)
69 67
70 68 if cache:
71 69 repo = repo.options(FromCache("sql_cache_short",
72 70 "get_repo_%s" % repo_name))
73 71 return repo.scalar()
74 72
75
76 73 def get_full(self, repo_name, cache=False, invalidate=False):
77 74 repo = self.sa.query(Repository)\
78 75 .options(joinedload(Repository.fork))\
79 76 .options(joinedload(Repository.user))\
80 77 .filter(Repository.repo_name == repo_name)\
81 78
82 79 if cache:
83 80 repo = repo.options(FromCache("sql_cache_long",
84 81 "get_repo_full_%s" % repo_name))
85 82 if invalidate and cache:
86 83 repo.invalidate()
87 84
88 85 ret = repo.scalar()
89 86
90 87 #make transient for sake of errors
91 88 make_transient(ret)
92 89 for k in ['fork', 'user']:
93 90 attr = getattr(ret, k, False)
94 91 if attr:
95 92 make_transient(attr)
96 93 return ret
97 94
98
99 95 def get_users_js(self):
100 96
101 97 users = self.sa.query(User).filter(User.active == True).all()
102 98 u_tmpl = '''{id:%s, fname:"%s", lname:"%s", nname:"%s"},'''
103 99 users_array = '[%s]' % '\n'.join([u_tmpl % (u.user_id, u.name,
104 100 u.lastname, u.username)
105 101 for u in users])
106 102 return users_array
107 103
108
109 104 def get_users_groups_js(self):
110 105 users_groups = self.sa.query(UsersGroup)\
111 106 .filter(UsersGroup.users_group_active == True).all()
112 107
113 108 g_tmpl = '''{id:%s, grname:"%s",grmembers:"%s"},'''
114 109
115 110 users_groups_array = '[%s]' % '\n'.join([g_tmpl % \
116 111 (gr.users_group_id, gr.users_group_name,
117 112 len(gr.members))
118 113 for gr in users_groups])
119 114 return users_groups_array
120 115
121 116 def update(self, repo_name, form_data):
122 117 try:
123 118 cur_repo = self.get_by_repo_name(repo_name, cache=False)
124 119 user_model = UserModel(self.sa)
125 users_group_model = UsersGroupModel(self.sa)
126 120
127 121 #update permissions
128 122 for member, perm, member_type in form_data['perms_updates']:
129 123 if member_type == 'user':
130 124 r2p = self.sa.query(RepoToPerm)\
131 .filter(RepoToPerm.user == user_model.get_by_username(member))\
125 .filter(RepoToPerm.user == user_model.
126 get_by_username(member))\
132 127 .filter(RepoToPerm.repository == cur_repo)\
133 128 .one()
134 129
135 130 r2p.permission = self.sa.query(Permission)\
136 .filter(Permission.permission_name == perm)\
137 .scalar()
131 .filter(Permission.permission_name ==
132 perm).scalar()
138 133 self.sa.add(r2p)
139 134 else:
140 g2p = self.sa.query(UsersGroupToPerm)\
141 .filter(UsersGroupToPerm.users_group == users_group_model.get_by_groupname(member))\
142 .filter(UsersGroupToPerm.repository == cur_repo)\
143 .one()
135 g2p = self.sa.query(UsersGroupRepoToPerm)\
136 .filter(UsersGroupRepoToPerm.users_group ==
137 UsersGroup.get_by_group_name(member))\
138 .filter(UsersGroupRepoToPerm.repository ==
139 cur_repo).one()
144 140
145 141 g2p.permission = self.sa.query(Permission)\
146 .filter(Permission.permission_name == perm)\
147 .scalar()
142 .filter(Permission.permission_name ==
143 perm).scalar()
148 144 self.sa.add(g2p)
149 145
150 146 #set new permissions
151 147 for member, perm, member_type in form_data['perms_new']:
152 148 if member_type == 'user':
153 149 r2p = RepoToPerm()
154 150 r2p.repository = cur_repo
155 151 r2p.user = user_model.get_by_username(member)
156 152
157 153 r2p.permission = self.sa.query(Permission)\
158 .filter(Permission.permission_name == perm)\
159 .scalar()
154 .filter(Permission.
155 permission_name == perm)\
156 .scalar()
160 157 self.sa.add(r2p)
161 158 else:
162 g2p = UsersGroupToPerm()
159 g2p = UsersGroupRepoToPerm()
163 160 g2p.repository = cur_repo
164 g2p.users_group = users_group_model.get_by_groupname(member)
161 g2p.users_group = UsersGroup.get_by_group_name(member)
165 162
166 163 g2p.permission = self.sa.query(Permission)\
167 .filter(Permission.permission_name == perm)\
168 .scalar()
164 .filter(Permission.
165 permission_name == perm)\
166 .scalar()
169 167 self.sa.add(g2p)
170 168
171 169 #update current repo
172 170 for k, v in form_data.items():
173 171 if k == 'user':
174 172 cur_repo.user = user_model.get(v)
175 173 else:
176 174 setattr(cur_repo, k, v)
177 175
178 176 self.sa.add(cur_repo)
179 177
180 178 if repo_name != form_data['repo_name']:
181 179 #rename our data
182 180 self.__rename_repo(repo_name, form_data['repo_name'])
183 181
184 182 self.sa.commit()
185 183 except:
186 184 log.error(traceback.format_exc())
187 185 self.sa.rollback()
188 186 raise
189 187
190 188 def create(self, form_data, cur_user, just_db=False, fork=False):
191 189 try:
192 190 if fork:
193 191 #force str since hg doesn't go with unicode
194 192 repo_name = str(form_data['fork_name'])
195 193 org_name = str(form_data['repo_name'])
196 194
197 195 else:
198 196 org_name = repo_name = str(form_data['repo_name'])
199 197 new_repo = Repository()
200 198 new_repo.enable_statistics = False
201 199 for k, v in form_data.items():
202 200 if k == 'repo_name':
203 201 v = repo_name
204 202 setattr(new_repo, k, v)
205 203
206 204 if fork:
207 205 parent_repo = self.sa.query(Repository)\
208 206 .filter(Repository.repo_name == org_name).scalar()
209 207 new_repo.fork = parent_repo
210 208
211 209 new_repo.user_id = cur_user.user_id
212 210 self.sa.add(new_repo)
213 211
214 212 #create default permission
215 213 repo_to_perm = RepoToPerm()
216 214 default = 'repository.read'
217 215 for p in UserModel(self.sa).get_by_username('default',
218 216 cache=False).user_perms:
219 217 if p.permission.permission_name.startswith('repository.'):
220 218 default = p.permission.permission_name
221 219 break
222 220
223 221 default_perm = 'repository.none' if form_data['private'] else default
224 222
225 223 repo_to_perm.permission_id = self.sa.query(Permission)\
226 224 .filter(Permission.permission_name == default_perm)\
227 225 .one().permission_id
228 226
229 227 repo_to_perm.repository = new_repo
230 228 repo_to_perm.user_id = UserModel(self.sa)\
231 229 .get_by_username('default', cache=False).user_id
232 230
233 231 self.sa.add(repo_to_perm)
234 232
235 233 if not just_db:
236 234 self.__create_repo(repo_name, form_data['repo_type'],
237 235 form_data['clone_uri'])
238 236
239 237 self.sa.commit()
240 238
241 239 #now automatically start following this repository as owner
242 240 from rhodecode.model.scm import ScmModel
243 241 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
244 242 cur_user.user_id)
245 243
246 244 except:
247 245 log.error(traceback.format_exc())
248 246 self.sa.rollback()
249 247 raise
250 248
251 249 def create_fork(self, form_data, cur_user):
252 250 from rhodecode.lib.celerylib import tasks, run_task
253 251 run_task(tasks.create_repo_fork, form_data, cur_user)
254 252
255 253 def delete(self, repo):
256 254 try:
257 255 self.sa.delete(repo)
258 256 self.__delete_repo(repo)
259 257 self.sa.commit()
260 258 except:
261 259 log.error(traceback.format_exc())
262 260 self.sa.rollback()
263 261 raise
264 262
265 263 def delete_perm_user(self, form_data, repo_name):
266 264 try:
267 265 self.sa.query(RepoToPerm)\
268 266 .filter(RepoToPerm.repository \
269 267 == self.get_by_repo_name(repo_name))\
270 268 .filter(RepoToPerm.user_id == form_data['user_id']).delete()
271 269 self.sa.commit()
272 270 except:
273 271 log.error(traceback.format_exc())
274 272 self.sa.rollback()
275 273 raise
276 274
277 275 def delete_perm_users_group(self, form_data, repo_name):
278 276 try:
279 self.sa.query(UsersGroupToPerm)\
280 .filter(UsersGroupToPerm.repository \
277 self.sa.query(UsersGroupRepoToPerm)\
278 .filter(UsersGroupRepoToPerm.repository \
281 279 == self.get_by_repo_name(repo_name))\
282 .filter(UsersGroupToPerm.users_group_id \
280 .filter(UsersGroupRepoToPerm.users_group_id \
283 281 == form_data['users_group_id']).delete()
284 282 self.sa.commit()
285 283 except:
286 284 log.error(traceback.format_exc())
287 285 self.sa.rollback()
288 286 raise
289 287
290 288 def delete_stats(self, repo_name):
291 289 try:
292 290 self.sa.query(Statistics)\
293 291 .filter(Statistics.repository == \
294 292 self.get_by_repo_name(repo_name)).delete()
295 293 self.sa.commit()
296 294 except:
297 295 log.error(traceback.format_exc())
298 296 self.sa.rollback()
299 297 raise
300 298
301
302 299 def __create_repo(self, repo_name, alias, clone_uri=False):
303 300 """
304 301 makes repository on filesystem
305 302
306 303 :param repo_name:
307 304 :param alias:
308 305 """
309 306 from rhodecode.lib.utils import check_repo
310 307 repo_path = os.path.join(self.repos_path, repo_name)
311 308 if check_repo(repo_name, self.repos_path):
312 309 log.info('creating repo %s in %s @ %s', repo_name, repo_path,
313 310 clone_uri)
314 311 backend = get_backend(alias)
315 312 backend(repo_path, create=True, src_url=clone_uri)
316 313
317 314 def __rename_repo(self, old, new):
318 315 """
319 316 renames repository on filesystem
320 317
321 318 :param old: old name
322 319 :param new: new name
323 320 """
324 321 log.info('renaming repo from %s to %s', old, new)
325 322
326 323 old_path = os.path.join(self.repos_path, old)
327 324 new_path = os.path.join(self.repos_path, new)
328 325 if os.path.isdir(new_path):
329 326 raise Exception('Was trying to rename to already existing dir %s',
330 327 new_path)
331 328 shutil.move(old_path, new_path)
332 329
333 330 def __delete_repo(self, repo):
334 331 """
335 332 removes repo from filesystem, the removal is acctually made by
336 333 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
337 334 repository is no longer valid for rhodecode, can be undeleted later on
338 335 by reverting the renames on this repository
339 336
340 337 :param repo: repo object
341 338 """
342 339 rm_path = os.path.join(self.repos_path, repo.repo_name)
343 340 log.info("Removing %s", rm_path)
344 341 #disable hg/git
345 342 alias = repo.repo_type
346 343 shutil.move(os.path.join(rm_path, '.%s' % alias),
347 344 os.path.join(rm_path, 'rm__.%s' % alias))
348 345 #disable repo
349 346 shutil.move(rm_path, os.path.join(self.repos_path, 'rm__%s__%s' \
350 347 % (datetime.today()\
351 348 .strftime('%Y%m%d_%H%M%S_%f'),
352 349 repo.repo_name)))
@@ -1,109 +1,93 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.user_group
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 users groups model for RhodeCode
7 7
8 8 :created_on: Jan 25, 2011
9 9 :author: marcink
10 10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import logging
27 27 import traceback
28 28
29 29 from pylons.i18n.translation import _
30 30
31 31 from rhodecode.model import BaseModel
32 32 from rhodecode.model.caching_query import FromCache
33 33 from rhodecode.model.db import UsersGroup, UsersGroupMember
34 34
35 from sqlalchemy.exc import DatabaseError
36
37 35 log = logging.getLogger(__name__)
38 36
39 37
40 38 class UsersGroupModel(BaseModel):
41 39
42 40 def get(self, users_group_id, cache=False):
43 41 users_group = self.sa.query(UsersGroup)
44 42 if cache:
45 43 users_group = users_group.options(FromCache("sql_cache_short",
46 "get_users_group_%s" % users_group_id))
44 "get_users_group_%s" % users_group_id))
47 45 return users_group.get(users_group_id)
48 46
49
50 def get_by_groupname(self, users_group_name, cache=False,
51 case_insensitive=False):
52
53 if case_insensitive:
54 user = self.sa.query(UsersGroup)\
55 .filter(UsersGroup.users_group_name.ilike(users_group_name))
56 else:
57 user = self.sa.query(UsersGroup)\
58 .filter(UsersGroup.users_group_name == users_group_name)
59 if cache:
60 user = user.options(FromCache("sql_cache_short",
61 "get_user_%s" % users_group_name))
62 return user.scalar()
63
64 47 def create(self, form_data):
65 48 try:
66 49 new_users_group = UsersGroup()
67 50 for k, v in form_data.items():
68 51 setattr(new_users_group, k, v)
69 52
70 53 self.sa.add(new_users_group)
71 54 self.sa.commit()
72 55 except:
73 56 log.error(traceback.format_exc())
74 57 self.sa.rollback()
75 58 raise
76 59
77 60 def update(self, users_group_id, form_data):
78 61
79 62 try:
80 63 users_group = self.get(users_group_id, cache=False)
81 64
82 65 for k, v in form_data.items():
83 66 if k == 'users_group_members':
84 67 users_group.members = []
85 68 self.sa.flush()
86 69 members_list = []
87 70 if v:
88 71 for u_id in set(v):
89 members_list.append(UsersGroupMember(users_group_id,
90 u_id))
72 members_list.append(UsersGroupMember(
73 users_group_id,
74 u_id))
91 75 setattr(users_group, 'members', members_list)
92 76 setattr(users_group, k, v)
93 77
94 78 self.sa.add(users_group)
95 79 self.sa.commit()
96 80 except:
97 81 log.error(traceback.format_exc())
98 82 self.sa.rollback()
99 83 raise
100 84
101 85 def delete(self, users_group_id):
102 86 try:
103 87 users_group = self.get(users_group_id, cache=False)
104 88 self.sa.delete(users_group)
105 89 self.sa.commit()
106 90 except:
107 91 log.error(traceback.format_exc())
108 92 self.sa.rollback()
109 93 raise
@@ -1,270 +1,270 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.html"/>
3 3
4 4 <%def name="title()">
5 5 ${_('Edit users group')} ${c.users_group.users_group_name} - ${c.rhodecode_name}
6 6 </%def>
7 7
8 8 <%def name="breadcrumbs_links()">
9 9 ${h.link_to(_('Admin'),h.url('admin_home'))}
10 10 &raquo;
11 11 ${h.link_to(_('UsersGroups'),h.url('users_groups'))}
12 12 &raquo;
13 13 ${_('edit')} "${c.users_group.users_group_name}"
14 14 </%def>
15 15
16 16 <%def name="page_nav()">
17 17 ${self.menu('admin')}
18 18 </%def>
19 19
20 20 <%def name="main()">
21 21 <div class="box box-left">
22 22 <!-- box / title -->
23 23 <div class="title">
24 24 ${self.breadcrumbs()}
25 25 </div>
26 26 <!-- end box / title -->
27 27 ${h.form(url('users_group', id=c.users_group.users_group_id),method='put', id='edit_users_group')}
28 28 <div class="form">
29 29 <!-- fields -->
30 30 <div class="fields">
31 31 <div class="field">
32 32 <div class="label">
33 33 <label for="users_group_name">${_('Group name')}:</label>
34 34 </div>
35 35 <div class="input">
36 36 ${h.text('users_group_name',class_='small')}
37 37 </div>
38 38 </div>
39 39
40 40 <div class="field">
41 41 <div class="label label-checkbox">
42 42 <label for="users_group_active">${_('Active')}:</label>
43 43 </div>
44 44 <div class="checkboxes">
45 45 ${h.checkbox('users_group_active',value=True)}
46 46 </div>
47 47 </div>
48 48 <div class="field">
49 49 <div class="label">
50 50 <label for="users_group_active">${_('Members')}:</label>
51 51 </div>
52 52 <div class="select">
53 53 <table>
54 54 <tr>
55 55 <td>
56 56 <div>
57 57 <div style="float:left">
58 58 <div class="text" style="padding: 0px 0px 6px;">${_('Choosen group members')}</div>
59 59 ${h.select('users_group_members',[x[0] for x in c.group_members],c.group_members,multiple=True,size=8,style="min-width:210px")}
60 60 <div id="remove_all_elements" style="cursor:pointer;text-align:center">
61 61 ${_('Remove all elements')}
62 62 <img alt="remove" style="vertical-align:text-bottom" src="${h.url("/images/icons/arrow_right.png")}"/>
63 63 </div>
64 64 </div>
65 65 <div style="float:left;width:20px;padding-top:50px">
66 66 <img alt="add" id="add_element"
67 67 style="padding:2px;cursor:pointer"
68 68 src="${h.url("/images/icons/arrow_left.png")}"/>
69 69 <br />
70 70 <img alt="remove" id="remove_element"
71 71 style="padding:2px;cursor:pointer"
72 72 src="${h.url("/images/icons/arrow_right.png")}"/>
73 73 </div>
74 74 <div style="float:left">
75 75 <div class="text" style="padding: 0px 0px 6px;">${_('Available members')}</div>
76 76 ${h.select('available_members',[],c.available_members,multiple=True,size=8,style="min-width:210px")}
77 77 <div id="add_all_elements" style="cursor:pointer;text-align:center">
78 78 <img alt="add" style="vertical-align:text-bottom" src="${h.url("/images/icons/arrow_left.png")}"/>
79 79 ${_('Add all elements')}
80 80 </div>
81 81 </div>
82 82 </div>
83 83 </td>
84 84 </tr>
85 85 </table>
86 86 </div>
87 87
88 88 </div>
89 89 <div class="buttons">
90 90 ${h.submit('save','save',class_="ui-button")}
91 91 </div>
92 92 </div>
93 93 </div>
94 94 ${h.end_form()}
95 95 </div>
96 96
97 97 <script type="text/javascript">
98 98 YAHOO.util.Event.onDOMReady(function(){
99 99 var D = YAHOO.util.Dom;
100 100 var E = YAHOO.util.Event;
101 101
102 102 //definition of containers ID's
103 103 var available_container = 'available_members';
104 104 var selected_container = 'users_group_members';
105 105
106 106 //form containing containers id
107 107 var form_id = 'edit_users_group';
108 108
109 109 //temp container for selected storage.
110 110 var cache = new Array();
111 111 var av_cache = new Array();
112 112 var c = D.get(selected_container);
113 113 var ac = D.get(available_container);
114 114
115 115 //get only selected options for further fullfilment
116 116 for(var i = 0;node =c.options[i];i++){
117 117 if(node.selected){
118 118 //push selected to my temp storage left overs :)
119 119 cache.push(node);
120 120 }
121 121 }
122 122
123 123 //clear 'selected' select
124 124 //c.options.length = 0;
125 125
126 126 //fill it with remembered options
127 127 //for(var i = 0;node = cache[i];i++){
128 128 // c.options[i]=new Option(node.text, node.value, false, false);
129 129 //}
130 130
131 131
132 132 //get all available options to cache
133 133 for(var i = 0;node =ac.options[i];i++){
134 134 //push selected to my temp storage left overs :)
135 135 av_cache.push(node);
136 136 }
137 137
138 138 //fill available only with those not in choosen
139 139 ac.options.length=0;
140 140 tmp_cache = new Array();
141 141
142 142 for(var i = 0;node = av_cache[i];i++){
143 143 var add = true;
144 144 for(var i2 = 0;node_2 = cache[i2];i2++){
145 145 if(node.value == node_2.value){
146 146 add=false;
147 147 break;
148 148 }
149 149 }
150 150 if(add){
151 151 tmp_cache.push(new Option(node.text, node.value, false, false));
152 152 }
153 153 }
154 154
155 155 for(var i = 0;node = tmp_cache[i];i++){
156 156 ac.options[i] = node;
157 157 }
158 158
159 159 function prompts_action_callback(e){
160 160
161 161 var choosen = D.get(selected_container);
162 162 var available = D.get(available_container);
163 163
164 164 //get checked and unchecked options from field
165 165 function get_checked(from_field){
166 166 //temp container for storage.
167 167 var sel_cache = new Array();
168 168 var oth_cache = new Array();
169 169
170 170 for(var i = 0;node = from_field.options[i];i++){
171 171 if(node.selected){
172 172 //push selected fields :)
173 173 sel_cache.push(node);
174 174 }
175 175 else{
176 176 oth_cache.push(node)
177 177 }
178 178 }
179 179
180 180 return [sel_cache,oth_cache]
181 181 }
182 182
183 183 //fill the field with given options
184 184 function fill_with(field,options){
185 185 //clear firtst
186 186 field.options.length=0;
187 187 for(var i = 0;node = options[i];i++){
188 188 field.options[i]=new Option(node.text, node.value,
189 189 false, false);
190 190 }
191 191
192 192 }
193 193 //adds to current field
194 194 function add_to(field,options){
195 195 for(var i = 0;node = options[i];i++){
196 196 field.appendChild(new Option(node.text, node.value,
197 197 false, false));
198 198 }
199 199 }
200 200
201 201 // add action
202 202 if (this.id=='add_element'){
203 203 var c = get_checked(available);
204 204 add_to(choosen,c[0]);
205 205 fill_with(available,c[1]);
206 206 }
207 207 // remove action
208 208 if (this.id=='remove_element'){
209 209 var c = get_checked(choosen);
210 210 add_to(available,c[0]);
211 211 fill_with(choosen,c[1]);
212 212 }
213 213 // add all elements
214 214 if(this.id=='add_all_elements'){
215 215 for(var i=0; node = available.options[i];i++){
216 216 choosen.appendChild(new Option(node.text,
217 217 node.value, false, false));
218 218 }
219 219 available.options.length = 0;
220 220 }
221 221 //remove all elements
222 222 if(this.id=='remove_all_elements'){
223 223 for(var i=0; node = choosen.options[i];i++){
224 224 available.appendChild(new Option(node.text,
225 225 node.value, false, false));
226 226 }
227 227 choosen.options.length = 0;
228 228 }
229 229
230 230 }
231 231
232 232
233 233 E.addListener(['add_element','remove_element',
234 234 'add_all_elements','remove_all_elements'],'click',
235 235 prompts_action_callback)
236 236
237 237 E.addListener(form_id,'submit',function(){
238 238 var choosen = D.get(selected_container);
239 239 for (var i = 0; i < choosen.options.length; i++) {
240 240 choosen.options[i].selected = 'selected';
241 241 }
242 242 })
243 243 });
244 244 </script>
245 245 <div class="box box-right">
246 246 <!-- box / title -->
247 247 <div class="title">
248 248 <h5>${_('Permissions')}</h5>
249 249 </div>
250 ${h.form(url('xxx', id=''),method='put')}
250 ${h.form(url('users_group_perm', id=c.users_group.users_group_id), method='put')}
251 251 <div class="form">
252 252 <!-- fields -->
253 253 <div class="fields">
254 254 <div class="field">
255 255 <div class="label label-checkbox">
256 256 <label for="">${_('Create repositories')}:</label>
257 257 </div>
258 258 <div class="checkboxes">
259 ${h.checkbox('create',value=True)}
259 ${h.checkbox('create_repo_perm',value=True)}
260 260 </div>
261 261 </div>
262 262 <div class="buttons">
263 263 ${h.submit('save','Save',class_="ui-button")}
264 264 ${h.reset('reset','Reset',class_="ui-button")}
265 265 </div>
266 266 </div>
267 267 </div>
268 268 ${h.end_form()}
269 269 </div>
270 270 </%def> No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now