##// END OF EJS Templates
Cleanup leftover print statements
marcink -
r3024:1361ddff beta
parent child Browse files
Show More
@@ -1,812 +1,811 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.api
3 rhodecode.controllers.api
4 ~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 API controller for RhodeCode
6 API controller for RhodeCode
7
7
8 :created_on: Aug 20, 2011
8 :created_on: Aug 20, 2011
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27
27
28 import traceback
28 import traceback
29 import logging
29 import logging
30
30
31 from rhodecode.controllers.api import JSONRPCController, JSONRPCError
31 from rhodecode.controllers.api import JSONRPCController, JSONRPCError
32 from rhodecode.lib.auth import HasPermissionAllDecorator, \
32 from rhodecode.lib.auth import HasPermissionAllDecorator, \
33 HasPermissionAnyDecorator, PasswordGenerator, AuthUser
33 HasPermissionAnyDecorator, PasswordGenerator, AuthUser
34 from rhodecode.lib.utils import map_groups, repo2db_mapper
34 from rhodecode.lib.utils import map_groups, repo2db_mapper
35 from rhodecode.model.meta import Session
35 from rhodecode.model.meta import Session
36 from rhodecode.model.scm import ScmModel
36 from rhodecode.model.scm import ScmModel
37 from rhodecode.model.repo import RepoModel
37 from rhodecode.model.repo import RepoModel
38 from rhodecode.model.user import UserModel
38 from rhodecode.model.user import UserModel
39 from rhodecode.model.users_group import UsersGroupModel
39 from rhodecode.model.users_group import UsersGroupModel
40 from rhodecode.model.permission import PermissionModel
40 from rhodecode.model.permission import PermissionModel
41 from rhodecode.model.db import Repository
41 from rhodecode.model.db import Repository
42
42
43 log = logging.getLogger(__name__)
43 log = logging.getLogger(__name__)
44
44
45
45
46 class Optional(object):
46 class Optional(object):
47 """
47 """
48 Defines an optional parameter::
48 Defines an optional parameter::
49
49
50 param = param.getval() if isinstance(param, Optional) else param
50 param = param.getval() if isinstance(param, Optional) else param
51 param = param() if isinstance(param, Optional) else param
51 param = param() if isinstance(param, Optional) else param
52
52
53 is equivalent of::
53 is equivalent of::
54
54
55 param = Optional.extract(param)
55 param = Optional.extract(param)
56
56
57 """
57 """
58 def __init__(self, type_):
58 def __init__(self, type_):
59 self.type_ = type_
59 self.type_ = type_
60
60
61 def __repr__(self):
61 def __repr__(self):
62 return '<Optional:%s>' % self.type_.__repr__()
62 return '<Optional:%s>' % self.type_.__repr__()
63
63
64 def __call__(self):
64 def __call__(self):
65 return self.getval()
65 return self.getval()
66
66
67 def getval(self):
67 def getval(self):
68 """
68 """
69 returns value from this Optional instance
69 returns value from this Optional instance
70 """
70 """
71 return self.type_
71 return self.type_
72
72
73 @classmethod
73 @classmethod
74 def extract(cls, val):
74 def extract(cls, val):
75 if isinstance(val, cls):
75 if isinstance(val, cls):
76 return val.getval()
76 return val.getval()
77 return val
77 return val
78
78
79
79
80 def get_user_or_error(userid):
80 def get_user_or_error(userid):
81 """
81 """
82 Get user by id or name or return JsonRPCError if not found
82 Get user by id or name or return JsonRPCError if not found
83
83
84 :param userid:
84 :param userid:
85 """
85 """
86 user = UserModel().get_user(userid)
86 user = UserModel().get_user(userid)
87 if user is None:
87 if user is None:
88 raise JSONRPCError("user `%s` does not exist" % userid)
88 raise JSONRPCError("user `%s` does not exist" % userid)
89 return user
89 return user
90
90
91
91
92 def get_repo_or_error(repoid):
92 def get_repo_or_error(repoid):
93 """
93 """
94 Get repo by id or name or return JsonRPCError if not found
94 Get repo by id or name or return JsonRPCError if not found
95
95
96 :param userid:
96 :param userid:
97 """
97 """
98 repo = RepoModel().get_repo(repoid)
98 repo = RepoModel().get_repo(repoid)
99 if repo is None:
99 if repo is None:
100 raise JSONRPCError('repository `%s` does not exist' % (repoid))
100 raise JSONRPCError('repository `%s` does not exist' % (repoid))
101 return repo
101 return repo
102
102
103
103
104 def get_users_group_or_error(usersgroupid):
104 def get_users_group_or_error(usersgroupid):
105 """
105 """
106 Get users group by id or name or return JsonRPCError if not found
106 Get users group by id or name or return JsonRPCError if not found
107
107
108 :param userid:
108 :param userid:
109 """
109 """
110 users_group = UsersGroupModel().get_group(usersgroupid)
110 users_group = UsersGroupModel().get_group(usersgroupid)
111 if users_group is None:
111 if users_group is None:
112 raise JSONRPCError('users group `%s` does not exist' % usersgroupid)
112 raise JSONRPCError('users group `%s` does not exist' % usersgroupid)
113 return users_group
113 return users_group
114
114
115
115
116 def get_perm_or_error(permid):
116 def get_perm_or_error(permid):
117 """
117 """
118 Get permission by id or name or return JsonRPCError if not found
118 Get permission by id or name or return JsonRPCError if not found
119
119
120 :param userid:
120 :param userid:
121 """
121 """
122 perm = PermissionModel().get_permission_by_name(permid)
122 perm = PermissionModel().get_permission_by_name(permid)
123 if perm is None:
123 if perm is None:
124 raise JSONRPCError('permission `%s` does not exist' % (permid))
124 raise JSONRPCError('permission `%s` does not exist' % (permid))
125 return perm
125 return perm
126
126
127
127
128 class ApiController(JSONRPCController):
128 class ApiController(JSONRPCController):
129 """
129 """
130 API Controller
130 API Controller
131
131
132
132
133 Each method needs to have USER as argument this is then based on given
133 Each method needs to have USER as argument this is then based on given
134 API_KEY propagated as instance of user object
134 API_KEY propagated as instance of user object
135
135
136 Preferably this should be first argument also
136 Preferably this should be first argument also
137
137
138
138
139 Each function should also **raise** JSONRPCError for any
139 Each function should also **raise** JSONRPCError for any
140 errors that happens
140 errors that happens
141
141
142 """
142 """
143
143
144 @HasPermissionAllDecorator('hg.admin')
144 @HasPermissionAllDecorator('hg.admin')
145 def pull(self, apiuser, repoid):
145 def pull(self, apiuser, repoid):
146 """
146 """
147 Dispatch pull action on given repo
147 Dispatch pull action on given repo
148
148
149 :param apiuser:
149 :param apiuser:
150 :param repoid:
150 :param repoid:
151 """
151 """
152
152
153 repo = get_repo_or_error(repoid)
153 repo = get_repo_or_error(repoid)
154
154
155 try:
155 try:
156 ScmModel().pull_changes(repo.repo_name,
156 ScmModel().pull_changes(repo.repo_name,
157 self.rhodecode_user.username)
157 self.rhodecode_user.username)
158 return 'Pulled from `%s`' % repo.repo_name
158 return 'Pulled from `%s`' % repo.repo_name
159 except Exception:
159 except Exception:
160 log.error(traceback.format_exc())
160 log.error(traceback.format_exc())
161 raise JSONRPCError(
161 raise JSONRPCError(
162 'Unable to pull changes from `%s`' % repo.repo_name
162 'Unable to pull changes from `%s`' % repo.repo_name
163 )
163 )
164
164
165 @HasPermissionAllDecorator('hg.admin')
165 @HasPermissionAllDecorator('hg.admin')
166 def rescan_repos(self, apiuser, remove_obsolete=Optional(False)):
166 def rescan_repos(self, apiuser, remove_obsolete=Optional(False)):
167 """
167 """
168 Dispatch rescan repositories action. If remove_obsolete is set
168 Dispatch rescan repositories action. If remove_obsolete is set
169 than also delete repos that are in database but not in the filesystem.
169 than also delete repos that are in database but not in the filesystem.
170 aka "clean zombies"
170 aka "clean zombies"
171
171
172 :param apiuser:
172 :param apiuser:
173 :param remove_obsolete:
173 :param remove_obsolete:
174 """
174 """
175
175
176 try:
176 try:
177 rm_obsolete = Optional.extract(remove_obsolete)
177 rm_obsolete = Optional.extract(remove_obsolete)
178 added, removed = repo2db_mapper(ScmModel().repo_scan(),
178 added, removed = repo2db_mapper(ScmModel().repo_scan(),
179 remove_obsolete=rm_obsolete)
179 remove_obsolete=rm_obsolete)
180 return {'added': added, 'removed': removed}
180 return {'added': added, 'removed': removed}
181 except Exception:
181 except Exception:
182 log.error(traceback.format_exc())
182 log.error(traceback.format_exc())
183 raise JSONRPCError(
183 raise JSONRPCError(
184 'Error occurred during rescan repositories action'
184 'Error occurred during rescan repositories action'
185 )
185 )
186
186
187 @HasPermissionAllDecorator('hg.admin')
187 @HasPermissionAllDecorator('hg.admin')
188 def lock(self, apiuser, repoid, userid, locked):
188 def lock(self, apiuser, repoid, userid, locked):
189 """
189 """
190 Set locking state on particular repository by given user
190 Set locking state on particular repository by given user
191
191
192 :param apiuser:
192 :param apiuser:
193 :param repoid:
193 :param repoid:
194 :param userid:
194 :param userid:
195 :param locked:
195 :param locked:
196 """
196 """
197 repo = get_repo_or_error(repoid)
197 repo = get_repo_or_error(repoid)
198 user = get_user_or_error(userid)
198 user = get_user_or_error(userid)
199 locked = bool(locked)
199 locked = bool(locked)
200 try:
200 try:
201 if locked:
201 if locked:
202 Repository.lock(repo, user.user_id)
202 Repository.lock(repo, user.user_id)
203 else:
203 else:
204 Repository.unlock(repo)
204 Repository.unlock(repo)
205
205
206 return ('User `%s` set lock state for repo `%s` to `%s`'
206 return ('User `%s` set lock state for repo `%s` to `%s`'
207 % (user.username, repo.repo_name, locked))
207 % (user.username, repo.repo_name, locked))
208 except Exception:
208 except Exception:
209 log.error(traceback.format_exc())
209 log.error(traceback.format_exc())
210 raise JSONRPCError(
210 raise JSONRPCError(
211 'Error occurred locking repository `%s`' % repo.repo_name
211 'Error occurred locking repository `%s`' % repo.repo_name
212 )
212 )
213
213
214 @HasPermissionAllDecorator('hg.admin')
214 @HasPermissionAllDecorator('hg.admin')
215 def get_user(self, apiuser, userid):
215 def get_user(self, apiuser, userid):
216 """"
216 """"
217 Get a user by username
217 Get a user by username
218
218
219 :param apiuser:
219 :param apiuser:
220 :param userid:
220 :param userid:
221 """
221 """
222
222
223 user = get_user_or_error(userid)
223 user = get_user_or_error(userid)
224 data = user.get_api_data()
224 data = user.get_api_data()
225 data['permissions'] = AuthUser(user_id=user.user_id).permissions
225 data['permissions'] = AuthUser(user_id=user.user_id).permissions
226 return data
226 return data
227
227
228 @HasPermissionAllDecorator('hg.admin')
228 @HasPermissionAllDecorator('hg.admin')
229 def get_users(self, apiuser):
229 def get_users(self, apiuser):
230 """"
230 """"
231 Get all users
231 Get all users
232
232
233 :param apiuser:
233 :param apiuser:
234 """
234 """
235
235
236 result = []
236 result = []
237 for user in UserModel().get_all():
237 for user in UserModel().get_all():
238 result.append(user.get_api_data())
238 result.append(user.get_api_data())
239 return result
239 return result
240
240
241 @HasPermissionAllDecorator('hg.admin')
241 @HasPermissionAllDecorator('hg.admin')
242 def create_user(self, apiuser, username, email, password,
242 def create_user(self, apiuser, username, email, password,
243 firstname=Optional(None), lastname=Optional(None),
243 firstname=Optional(None), lastname=Optional(None),
244 active=Optional(True), admin=Optional(False),
244 active=Optional(True), admin=Optional(False),
245 ldap_dn=Optional(None)):
245 ldap_dn=Optional(None)):
246 """
246 """
247 Create new user
247 Create new user
248
248
249 :param apiuser:
249 :param apiuser:
250 :param username:
250 :param username:
251 :param email:
251 :param email:
252 :param password:
252 :param password:
253 :param firstname:
253 :param firstname:
254 :param lastname:
254 :param lastname:
255 :param active:
255 :param active:
256 :param admin:
256 :param admin:
257 :param ldap_dn:
257 :param ldap_dn:
258 """
258 """
259
259
260 if UserModel().get_by_username(username):
260 if UserModel().get_by_username(username):
261 raise JSONRPCError("user `%s` already exist" % username)
261 raise JSONRPCError("user `%s` already exist" % username)
262
262
263 if UserModel().get_by_email(email, case_insensitive=True):
263 if UserModel().get_by_email(email, case_insensitive=True):
264 raise JSONRPCError("email `%s` already exist" % email)
264 raise JSONRPCError("email `%s` already exist" % email)
265
265
266 if Optional.extract(ldap_dn):
266 if Optional.extract(ldap_dn):
267 # generate temporary password if ldap_dn
267 # generate temporary password if ldap_dn
268 password = PasswordGenerator().gen_password(length=8)
268 password = PasswordGenerator().gen_password(length=8)
269
269
270 try:
270 try:
271 user = UserModel().create_or_update(
271 user = UserModel().create_or_update(
272 username=Optional.extract(username),
272 username=Optional.extract(username),
273 password=Optional.extract(password),
273 password=Optional.extract(password),
274 email=Optional.extract(email),
274 email=Optional.extract(email),
275 firstname=Optional.extract(firstname),
275 firstname=Optional.extract(firstname),
276 lastname=Optional.extract(lastname),
276 lastname=Optional.extract(lastname),
277 active=Optional.extract(active),
277 active=Optional.extract(active),
278 admin=Optional.extract(admin),
278 admin=Optional.extract(admin),
279 ldap_dn=Optional.extract(ldap_dn)
279 ldap_dn=Optional.extract(ldap_dn)
280 )
280 )
281 Session().commit()
281 Session().commit()
282 return dict(
282 return dict(
283 msg='created new user `%s`' % username,
283 msg='created new user `%s`' % username,
284 user=user.get_api_data()
284 user=user.get_api_data()
285 )
285 )
286 except Exception:
286 except Exception:
287 log.error(traceback.format_exc())
287 log.error(traceback.format_exc())
288 raise JSONRPCError('failed to create user `%s`' % username)
288 raise JSONRPCError('failed to create user `%s`' % username)
289
289
290 @HasPermissionAllDecorator('hg.admin')
290 @HasPermissionAllDecorator('hg.admin')
291 def update_user(self, apiuser, userid, username=Optional(None),
291 def update_user(self, apiuser, userid, username=Optional(None),
292 email=Optional(None), firstname=Optional(None),
292 email=Optional(None), firstname=Optional(None),
293 lastname=Optional(None), active=Optional(None),
293 lastname=Optional(None), active=Optional(None),
294 admin=Optional(None), ldap_dn=Optional(None),
294 admin=Optional(None), ldap_dn=Optional(None),
295 password=Optional(None)):
295 password=Optional(None)):
296 """
296 """
297 Updates given user
297 Updates given user
298
298
299 :param apiuser:
299 :param apiuser:
300 :param userid:
300 :param userid:
301 :param username:
301 :param username:
302 :param email:
302 :param email:
303 :param firstname:
303 :param firstname:
304 :param lastname:
304 :param lastname:
305 :param active:
305 :param active:
306 :param admin:
306 :param admin:
307 :param ldap_dn:
307 :param ldap_dn:
308 :param password:
308 :param password:
309 """
309 """
310
310
311 user = get_user_or_error(userid)
311 user = get_user_or_error(userid)
312
312
313 # call function and store only updated arguments
313 # call function and store only updated arguments
314 updates = {}
314 updates = {}
315
315
316 def store_update(attr, name):
316 def store_update(attr, name):
317 if not isinstance(attr, Optional):
317 if not isinstance(attr, Optional):
318 updates[name] = attr
318 updates[name] = attr
319
319
320 try:
320 try:
321
321
322 store_update(username, 'username')
322 store_update(username, 'username')
323 store_update(password, 'password')
323 store_update(password, 'password')
324 store_update(email, 'email')
324 store_update(email, 'email')
325 store_update(firstname, 'name')
325 store_update(firstname, 'name')
326 store_update(lastname, 'lastname')
326 store_update(lastname, 'lastname')
327 store_update(active, 'active')
327 store_update(active, 'active')
328 store_update(admin, 'admin')
328 store_update(admin, 'admin')
329 store_update(ldap_dn, 'ldap_dn')
329 store_update(ldap_dn, 'ldap_dn')
330
330
331 user = UserModel().update_user(user, **updates)
331 user = UserModel().update_user(user, **updates)
332 Session().commit()
332 Session().commit()
333 return dict(
333 return dict(
334 msg='updated user ID:%s %s' % (user.user_id, user.username),
334 msg='updated user ID:%s %s' % (user.user_id, user.username),
335 user=user.get_api_data()
335 user=user.get_api_data()
336 )
336 )
337 except Exception:
337 except Exception:
338 log.error(traceback.format_exc())
338 log.error(traceback.format_exc())
339 raise JSONRPCError('failed to update user `%s`' % userid)
339 raise JSONRPCError('failed to update user `%s`' % userid)
340
340
341 @HasPermissionAllDecorator('hg.admin')
341 @HasPermissionAllDecorator('hg.admin')
342 def delete_user(self, apiuser, userid):
342 def delete_user(self, apiuser, userid):
343 """"
343 """"
344 Deletes an user
344 Deletes an user
345
345
346 :param apiuser:
346 :param apiuser:
347 :param userid:
347 :param userid:
348 """
348 """
349 user = get_user_or_error(userid)
349 user = get_user_or_error(userid)
350
350
351 try:
351 try:
352 UserModel().delete(userid)
352 UserModel().delete(userid)
353 Session().commit()
353 Session().commit()
354 return dict(
354 return dict(
355 msg='deleted user ID:%s %s' % (user.user_id, user.username),
355 msg='deleted user ID:%s %s' % (user.user_id, user.username),
356 user=None
356 user=None
357 )
357 )
358 except Exception:
358 except Exception:
359 log.error(traceback.format_exc())
359 log.error(traceback.format_exc())
360 raise JSONRPCError('failed to delete ID:%s %s' % (user.user_id,
360 raise JSONRPCError('failed to delete ID:%s %s' % (user.user_id,
361 user.username))
361 user.username))
362
362
363 @HasPermissionAllDecorator('hg.admin')
363 @HasPermissionAllDecorator('hg.admin')
364 def get_users_group(self, apiuser, usersgroupid):
364 def get_users_group(self, apiuser, usersgroupid):
365 """"
365 """"
366 Get users group by name or id
366 Get users group by name or id
367
367
368 :param apiuser:
368 :param apiuser:
369 :param usersgroupid:
369 :param usersgroupid:
370 """
370 """
371 users_group = get_users_group_or_error(usersgroupid)
371 users_group = get_users_group_or_error(usersgroupid)
372
372
373 data = users_group.get_api_data()
373 data = users_group.get_api_data()
374
374
375 members = []
375 members = []
376 for user in users_group.members:
376 for user in users_group.members:
377 user = user.user
377 user = user.user
378 members.append(user.get_api_data())
378 members.append(user.get_api_data())
379 data['members'] = members
379 data['members'] = members
380 return data
380 return data
381
381
382 @HasPermissionAllDecorator('hg.admin')
382 @HasPermissionAllDecorator('hg.admin')
383 def get_users_groups(self, apiuser):
383 def get_users_groups(self, apiuser):
384 """"
384 """"
385 Get all users groups
385 Get all users groups
386
386
387 :param apiuser:
387 :param apiuser:
388 """
388 """
389
389
390 result = []
390 result = []
391 for users_group in UsersGroupModel().get_all():
391 for users_group in UsersGroupModel().get_all():
392 result.append(users_group.get_api_data())
392 result.append(users_group.get_api_data())
393 return result
393 return result
394
394
395 @HasPermissionAllDecorator('hg.admin')
395 @HasPermissionAllDecorator('hg.admin')
396 def create_users_group(self, apiuser, group_name, active=Optional(True)):
396 def create_users_group(self, apiuser, group_name, active=Optional(True)):
397 """
397 """
398 Creates an new usergroup
398 Creates an new usergroup
399
399
400 :param apiuser:
400 :param apiuser:
401 :param group_name:
401 :param group_name:
402 :param active:
402 :param active:
403 """
403 """
404
404
405 if UsersGroupModel().get_by_name(group_name):
405 if UsersGroupModel().get_by_name(group_name):
406 raise JSONRPCError("users group `%s` already exist" % group_name)
406 raise JSONRPCError("users group `%s` already exist" % group_name)
407
407
408 try:
408 try:
409 active = Optional.extract(active)
409 active = Optional.extract(active)
410 ug = UsersGroupModel().create(name=group_name, active=active)
410 ug = UsersGroupModel().create(name=group_name, active=active)
411 Session().commit()
411 Session().commit()
412 return dict(
412 return dict(
413 msg='created new users group `%s`' % group_name,
413 msg='created new users group `%s`' % group_name,
414 users_group=ug.get_api_data()
414 users_group=ug.get_api_data()
415 )
415 )
416 except Exception:
416 except Exception:
417 log.error(traceback.format_exc())
417 log.error(traceback.format_exc())
418 raise JSONRPCError('failed to create group `%s`' % group_name)
418 raise JSONRPCError('failed to create group `%s`' % group_name)
419
419
420 @HasPermissionAllDecorator('hg.admin')
420 @HasPermissionAllDecorator('hg.admin')
421 def add_user_to_users_group(self, apiuser, usersgroupid, userid):
421 def add_user_to_users_group(self, apiuser, usersgroupid, userid):
422 """"
422 """"
423 Add a user to a users group
423 Add a user to a users group
424
424
425 :param apiuser:
425 :param apiuser:
426 :param usersgroupid:
426 :param usersgroupid:
427 :param userid:
427 :param userid:
428 """
428 """
429 user = get_user_or_error(userid)
429 user = get_user_or_error(userid)
430 users_group = get_users_group_or_error(usersgroupid)
430 users_group = get_users_group_or_error(usersgroupid)
431
431
432 try:
432 try:
433 ugm = UsersGroupModel().add_user_to_group(users_group, user)
433 ugm = UsersGroupModel().add_user_to_group(users_group, user)
434 success = True if ugm != True else False
434 success = True if ugm != True else False
435 msg = 'added member `%s` to users group `%s`' % (
435 msg = 'added member `%s` to users group `%s`' % (
436 user.username, users_group.users_group_name
436 user.username, users_group.users_group_name
437 )
437 )
438 msg = msg if success else 'User is already in that group'
438 msg = msg if success else 'User is already in that group'
439 Session().commit()
439 Session().commit()
440
440
441 return dict(
441 return dict(
442 success=success,
442 success=success,
443 msg=msg
443 msg=msg
444 )
444 )
445 except Exception:
445 except Exception:
446 log.error(traceback.format_exc())
446 log.error(traceback.format_exc())
447 raise JSONRPCError(
447 raise JSONRPCError(
448 'failed to add member to users group `%s`' % (
448 'failed to add member to users group `%s`' % (
449 users_group.users_group_name
449 users_group.users_group_name
450 )
450 )
451 )
451 )
452
452
453 @HasPermissionAllDecorator('hg.admin')
453 @HasPermissionAllDecorator('hg.admin')
454 def remove_user_from_users_group(self, apiuser, usersgroupid, userid):
454 def remove_user_from_users_group(self, apiuser, usersgroupid, userid):
455 """
455 """
456 Remove user from a group
456 Remove user from a group
457
457
458 :param apiuser:
458 :param apiuser:
459 :param usersgroupid:
459 :param usersgroupid:
460 :param userid:
460 :param userid:
461 """
461 """
462 user = get_user_or_error(userid)
462 user = get_user_or_error(userid)
463 users_group = get_users_group_or_error(usersgroupid)
463 users_group = get_users_group_or_error(usersgroupid)
464
464
465 try:
465 try:
466 success = UsersGroupModel().remove_user_from_group(users_group,
466 success = UsersGroupModel().remove_user_from_group(users_group,
467 user)
467 user)
468 msg = 'removed member `%s` from users group `%s`' % (
468 msg = 'removed member `%s` from users group `%s`' % (
469 user.username, users_group.users_group_name
469 user.username, users_group.users_group_name
470 )
470 )
471 msg = msg if success else "User wasn't in group"
471 msg = msg if success else "User wasn't in group"
472 Session().commit()
472 Session().commit()
473 return dict(success=success, msg=msg)
473 return dict(success=success, msg=msg)
474 except Exception:
474 except Exception:
475 log.error(traceback.format_exc())
475 log.error(traceback.format_exc())
476 raise JSONRPCError(
476 raise JSONRPCError(
477 'failed to remove member from users group `%s`' % (
477 'failed to remove member from users group `%s`' % (
478 users_group.users_group_name
478 users_group.users_group_name
479 )
479 )
480 )
480 )
481
481
482 @HasPermissionAnyDecorator('hg.admin')
482 @HasPermissionAnyDecorator('hg.admin')
483 def get_repo(self, apiuser, repoid):
483 def get_repo(self, apiuser, repoid):
484 """"
484 """"
485 Get repository by name
485 Get repository by name
486
486
487 :param apiuser:
487 :param apiuser:
488 :param repoid:
488 :param repoid:
489 """
489 """
490 repo = get_repo_or_error(repoid)
490 repo = get_repo_or_error(repoid)
491
491
492 members = []
492 members = []
493 for user in repo.repo_to_perm:
493 for user in repo.repo_to_perm:
494 perm = user.permission.permission_name
494 perm = user.permission.permission_name
495 user = user.user
495 user = user.user
496 user_data = user.get_api_data()
496 user_data = user.get_api_data()
497 user_data['type'] = "user"
497 user_data['type'] = "user"
498 user_data['permission'] = perm
498 user_data['permission'] = perm
499 members.append(user_data)
499 members.append(user_data)
500
500
501 for users_group in repo.users_group_to_perm:
501 for users_group in repo.users_group_to_perm:
502 perm = users_group.permission.permission_name
502 perm = users_group.permission.permission_name
503 users_group = users_group.users_group
503 users_group = users_group.users_group
504 users_group_data = users_group.get_api_data()
504 users_group_data = users_group.get_api_data()
505 users_group_data['type'] = "users_group"
505 users_group_data['type'] = "users_group"
506 users_group_data['permission'] = perm
506 users_group_data['permission'] = perm
507 members.append(users_group_data)
507 members.append(users_group_data)
508
508
509 data = repo.get_api_data()
509 data = repo.get_api_data()
510 data['members'] = members
510 data['members'] = members
511 return data
511 return data
512
512
513 @HasPermissionAnyDecorator('hg.admin')
513 @HasPermissionAnyDecorator('hg.admin')
514 def get_repos(self, apiuser):
514 def get_repos(self, apiuser):
515 """"
515 """"
516 Get all repositories
516 Get all repositories
517
517
518 :param apiuser:
518 :param apiuser:
519 """
519 """
520
520
521 result = []
521 result = []
522 for repo in RepoModel().get_all():
522 for repo in RepoModel().get_all():
523 result.append(repo.get_api_data())
523 result.append(repo.get_api_data())
524 return result
524 return result
525
525
526 @HasPermissionAnyDecorator('hg.admin')
526 @HasPermissionAnyDecorator('hg.admin')
527 def get_repo_nodes(self, apiuser, repoid, revision, root_path,
527 def get_repo_nodes(self, apiuser, repoid, revision, root_path,
528 ret_type='all'):
528 ret_type='all'):
529 """
529 """
530 returns a list of nodes and it's children
530 returns a list of nodes and it's children
531 for a given path at given revision. It's possible to specify ret_type
531 for a given path at given revision. It's possible to specify ret_type
532 to show only files or dirs
532 to show only files or dirs
533
533
534 :param apiuser:
534 :param apiuser:
535 :param repoid: name or id of repository
535 :param repoid: name or id of repository
536 :param revision: revision for which listing should be done
536 :param revision: revision for which listing should be done
537 :param root_path: path from which start displaying
537 :param root_path: path from which start displaying
538 :param ret_type: return type 'all|files|dirs' nodes
538 :param ret_type: return type 'all|files|dirs' nodes
539 """
539 """
540 repo = get_repo_or_error(repoid)
540 repo = get_repo_or_error(repoid)
541 try:
541 try:
542 _d, _f = ScmModel().get_nodes(repo, revision, root_path,
542 _d, _f = ScmModel().get_nodes(repo, revision, root_path,
543 flat=False)
543 flat=False)
544 _map = {
544 _map = {
545 'all': _d + _f,
545 'all': _d + _f,
546 'files': _f,
546 'files': _f,
547 'dirs': _d,
547 'dirs': _d,
548 }
548 }
549 return _map[ret_type]
549 return _map[ret_type]
550 except KeyError:
550 except KeyError:
551 raise JSONRPCError('ret_type must be one of %s' % _map.keys())
551 raise JSONRPCError('ret_type must be one of %s' % _map.keys())
552 except Exception:
552 except Exception:
553 log.error(traceback.format_exc())
553 log.error(traceback.format_exc())
554 raise JSONRPCError(
554 raise JSONRPCError(
555 'failed to get repo: `%s` nodes' % repo.repo_name
555 'failed to get repo: `%s` nodes' % repo.repo_name
556 )
556 )
557
557
558 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
558 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
559 def create_repo(self, apiuser, repo_name, owner, repo_type,
559 def create_repo(self, apiuser, repo_name, owner, repo_type,
560 description=Optional(''), private=Optional(False),
560 description=Optional(''), private=Optional(False),
561 clone_uri=Optional(None), landing_rev=Optional('tip')):
561 clone_uri=Optional(None), landing_rev=Optional('tip')):
562 """
562 """
563 Create repository, if clone_url is given it makes a remote clone
563 Create repository, if clone_url is given it makes a remote clone
564 if repo_name is withina group name the groups will be created
564 if repo_name is withina group name the groups will be created
565 automatically if they aren't present
565 automatically if they aren't present
566
566
567 :param apiuser:
567 :param apiuser:
568 :param repo_name:
568 :param repo_name:
569 :param onwer:
569 :param onwer:
570 :param repo_type:
570 :param repo_type:
571 :param description:
571 :param description:
572 :param private:
572 :param private:
573 :param clone_uri:
573 :param clone_uri:
574 :param landing_rev:
574 :param landing_rev:
575 """
575 """
576 owner = get_user_or_error(owner)
576 owner = get_user_or_error(owner)
577
577
578 if RepoModel().get_by_repo_name(repo_name):
578 if RepoModel().get_by_repo_name(repo_name):
579 raise JSONRPCError("repo `%s` already exist" % repo_name)
579 raise JSONRPCError("repo `%s` already exist" % repo_name)
580
580
581 private = Optional.extract(private)
581 private = Optional.extract(private)
582 clone_uri = Optional.extract(clone_uri)
582 clone_uri = Optional.extract(clone_uri)
583 description = Optional.extract(description)
583 description = Optional.extract(description)
584 landing_rev = Optional.extract(landing_rev)
584 landing_rev = Optional.extract(landing_rev)
585
585
586 try:
586 try:
587 # create structure of groups and return the last group
587 # create structure of groups and return the last group
588 group = map_groups(repo_name)
588 group = map_groups(repo_name)
589
589
590 repo = RepoModel().create_repo(
590 repo = RepoModel().create_repo(
591 repo_name=repo_name,
591 repo_name=repo_name,
592 repo_type=repo_type,
592 repo_type=repo_type,
593 description=description,
593 description=description,
594 owner=owner,
594 owner=owner,
595 private=private,
595 private=private,
596 clone_uri=clone_uri,
596 clone_uri=clone_uri,
597 repos_group=group,
597 repos_group=group,
598 landing_rev=landing_rev,
598 landing_rev=landing_rev,
599 )
599 )
600
600
601 Session().commit()
601 Session().commit()
602
602
603 return dict(
603 return dict(
604 msg="Created new repository `%s`" % (repo.repo_name),
604 msg="Created new repository `%s`" % (repo.repo_name),
605 repo=repo.get_api_data()
605 repo=repo.get_api_data()
606 )
606 )
607
607
608 except Exception:
608 except Exception:
609 log.error(traceback.format_exc())
609 log.error(traceback.format_exc())
610 raise JSONRPCError('failed to create repository `%s`' % repo_name)
610 raise JSONRPCError('failed to create repository `%s`' % repo_name)
611
611
612 @HasPermissionAnyDecorator('hg.admin')
612 @HasPermissionAnyDecorator('hg.admin')
613 def fork_repo(self, apiuser, repoid, fork_name, owner,
613 def fork_repo(self, apiuser, repoid, fork_name, owner,
614 description=Optional(''), copy_permissions=Optional(False),
614 description=Optional(''), copy_permissions=Optional(False),
615 private=Optional(False), landing_rev=Optional('tip')):
615 private=Optional(False), landing_rev=Optional('tip')):
616 repo = get_repo_or_error(repoid)
616 repo = get_repo_or_error(repoid)
617 repo_name = repo.repo_name
617 repo_name = repo.repo_name
618 owner = get_user_or_error(owner)
618 owner = get_user_or_error(owner)
619
619
620 _repo = RepoModel().get_by_repo_name(fork_name)
620 _repo = RepoModel().get_by_repo_name(fork_name)
621 if _repo:
621 if _repo:
622 type_ = 'fork' if _repo.fork else 'repo'
622 type_ = 'fork' if _repo.fork else 'repo'
623 raise JSONRPCError("%s `%s` already exist" % (type_, fork_name))
623 raise JSONRPCError("%s `%s` already exist" % (type_, fork_name))
624
624
625 try:
625 try:
626 # create structure of groups and return the last group
626 # create structure of groups and return the last group
627 group = map_groups(fork_name)
627 group = map_groups(fork_name)
628
628
629 form_data = dict(
629 form_data = dict(
630 repo_name=fork_name,
630 repo_name=fork_name,
631 repo_name_full=fork_name,
631 repo_name_full=fork_name,
632 repo_group=group,
632 repo_group=group,
633 repo_type=repo.repo_type,
633 repo_type=repo.repo_type,
634 description=Optional.extract(description),
634 description=Optional.extract(description),
635 private=Optional.extract(private),
635 private=Optional.extract(private),
636 copy_permissions=Optional.extract(copy_permissions),
636 copy_permissions=Optional.extract(copy_permissions),
637 landing_rev=Optional.extract(landing_rev),
637 landing_rev=Optional.extract(landing_rev),
638 update_after_clone=False,
638 update_after_clone=False,
639 fork_parent_id=repo.repo_id,
639 fork_parent_id=repo.repo_id,
640 )
640 )
641 RepoModel().create_fork(form_data, cur_user=owner)
641 RepoModel().create_fork(form_data, cur_user=owner)
642 return dict(
642 return dict(
643 msg='Created fork of `%s` as `%s`' % (repo.repo_name,
643 msg='Created fork of `%s` as `%s`' % (repo.repo_name,
644 fork_name),
644 fork_name),
645 success=True # cannot return the repo data here since fork
645 success=True # cannot return the repo data here since fork
646 # cann be done async
646 # cann be done async
647 )
647 )
648 except Exception:
648 except Exception:
649 log.error(traceback.format_exc())
649 log.error(traceback.format_exc())
650 raise JSONRPCError(
650 raise JSONRPCError(
651 'failed to fork repository `%s` as `%s`' % (repo_name,
651 'failed to fork repository `%s` as `%s`' % (repo_name,
652 fork_name)
652 fork_name)
653 )
653 )
654
654
655 @HasPermissionAnyDecorator('hg.admin')
655 @HasPermissionAnyDecorator('hg.admin')
656 def delete_repo(self, apiuser, repoid):
656 def delete_repo(self, apiuser, repoid):
657 """
657 """
658 Deletes a given repository
658 Deletes a given repository
659
659
660 :param apiuser:
660 :param apiuser:
661 :param repoid:
661 :param repoid:
662 """
662 """
663 repo = get_repo_or_error(repoid)
663 repo = get_repo_or_error(repoid)
664
664
665 try:
665 try:
666 RepoModel().delete(repo)
666 RepoModel().delete(repo)
667 Session().commit()
667 Session().commit()
668 return dict(
668 return dict(
669 msg='Deleted repository `%s`' % repo.repo_name,
669 msg='Deleted repository `%s`' % repo.repo_name,
670 success=True
670 success=True
671 )
671 )
672 except Exception:
672 except Exception:
673 log.error(traceback.format_exc())
673 log.error(traceback.format_exc())
674 raise JSONRPCError(
674 raise JSONRPCError(
675 'failed to delete repository `%s`' % repo.repo_name
675 'failed to delete repository `%s`' % repo.repo_name
676 )
676 )
677
677
678 @HasPermissionAnyDecorator('hg.admin')
678 @HasPermissionAnyDecorator('hg.admin')
679 def grant_user_permission(self, apiuser, repoid, userid, perm):
679 def grant_user_permission(self, apiuser, repoid, userid, perm):
680 """
680 """
681 Grant permission for user on given repository, or update existing one
681 Grant permission for user on given repository, or update existing one
682 if found
682 if found
683
683
684 :param repoid:
684 :param repoid:
685 :param userid:
685 :param userid:
686 :param perm:
686 :param perm:
687 """
687 """
688 repo = get_repo_or_error(repoid)
688 repo = get_repo_or_error(repoid)
689 user = get_user_or_error(userid)
689 user = get_user_or_error(userid)
690 perm = get_perm_or_error(perm)
690 perm = get_perm_or_error(perm)
691
691
692 try:
692 try:
693
693
694 RepoModel().grant_user_permission(repo=repo, user=user, perm=perm)
694 RepoModel().grant_user_permission(repo=repo, user=user, perm=perm)
695
695
696 Session().commit()
696 Session().commit()
697 return dict(
697 return dict(
698 msg='Granted perm: `%s` for user: `%s` in repo: `%s`' % (
698 msg='Granted perm: `%s` for user: `%s` in repo: `%s`' % (
699 perm.permission_name, user.username, repo.repo_name
699 perm.permission_name, user.username, repo.repo_name
700 ),
700 ),
701 success=True
701 success=True
702 )
702 )
703 except Exception:
703 except Exception:
704 log.error(traceback.format_exc())
704 log.error(traceback.format_exc())
705 raise JSONRPCError(
705 raise JSONRPCError(
706 'failed to edit permission for user: `%s` in repo: `%s`' % (
706 'failed to edit permission for user: `%s` in repo: `%s`' % (
707 userid, repoid
707 userid, repoid
708 )
708 )
709 )
709 )
710
710
711 @HasPermissionAnyDecorator('hg.admin')
711 @HasPermissionAnyDecorator('hg.admin')
712 def revoke_user_permission(self, apiuser, repoid, userid):
712 def revoke_user_permission(self, apiuser, repoid, userid):
713 """
713 """
714 Revoke permission for user on given repository
714 Revoke permission for user on given repository
715
715
716 :param apiuser:
716 :param apiuser:
717 :param repoid:
717 :param repoid:
718 :param userid:
718 :param userid:
719 """
719 """
720
720
721 repo = get_repo_or_error(repoid)
721 repo = get_repo_or_error(repoid)
722 user = get_user_or_error(userid)
722 user = get_user_or_error(userid)
723 try:
723 try:
724
724
725 RepoModel().revoke_user_permission(repo=repo, user=user)
725 RepoModel().revoke_user_permission(repo=repo, user=user)
726
726
727 Session().commit()
727 Session().commit()
728 return dict(
728 return dict(
729 msg='Revoked perm for user: `%s` in repo: `%s`' % (
729 msg='Revoked perm for user: `%s` in repo: `%s`' % (
730 user.username, repo.repo_name
730 user.username, repo.repo_name
731 ),
731 ),
732 success=True
732 success=True
733 )
733 )
734 except Exception:
734 except Exception:
735 log.error(traceback.format_exc())
735 log.error(traceback.format_exc())
736 raise JSONRPCError(
736 raise JSONRPCError(
737 'failed to edit permission for user: `%s` in repo: `%s`' % (
737 'failed to edit permission for user: `%s` in repo: `%s`' % (
738 userid, repoid
738 userid, repoid
739 )
739 )
740 )
740 )
741
741
742 @HasPermissionAnyDecorator('hg.admin')
742 @HasPermissionAnyDecorator('hg.admin')
743 def grant_users_group_permission(self, apiuser, repoid, usersgroupid,
743 def grant_users_group_permission(self, apiuser, repoid, usersgroupid,
744 perm):
744 perm):
745 """
745 """
746 Grant permission for users group on given repository, or update
746 Grant permission for users group on given repository, or update
747 existing one if found
747 existing one if found
748
748
749 :param apiuser:
749 :param apiuser:
750 :param repoid:
750 :param repoid:
751 :param usersgroupid:
751 :param usersgroupid:
752 :param perm:
752 :param perm:
753 """
753 """
754 repo = get_repo_or_error(repoid)
754 repo = get_repo_or_error(repoid)
755 perm = get_perm_or_error(perm)
755 perm = get_perm_or_error(perm)
756 users_group = get_users_group_or_error(usersgroupid)
756 users_group = get_users_group_or_error(usersgroupid)
757
757
758 try:
758 try:
759 RepoModel().grant_users_group_permission(repo=repo,
759 RepoModel().grant_users_group_permission(repo=repo,
760 group_name=users_group,
760 group_name=users_group,
761 perm=perm)
761 perm=perm)
762
762
763 Session().commit()
763 Session().commit()
764 return dict(
764 return dict(
765 msg='Granted perm: `%s` for users group: `%s` in '
765 msg='Granted perm: `%s` for users group: `%s` in '
766 'repo: `%s`' % (
766 'repo: `%s`' % (
767 perm.permission_name, users_group.users_group_name,
767 perm.permission_name, users_group.users_group_name,
768 repo.repo_name
768 repo.repo_name
769 ),
769 ),
770 success=True
770 success=True
771 )
771 )
772 except Exception:
772 except Exception:
773 print traceback.format_exc()
774 log.error(traceback.format_exc())
773 log.error(traceback.format_exc())
775 raise JSONRPCError(
774 raise JSONRPCError(
776 'failed to edit permission for users group: `%s` in '
775 'failed to edit permission for users group: `%s` in '
777 'repo: `%s`' % (
776 'repo: `%s`' % (
778 usersgroupid, repo.repo_name
777 usersgroupid, repo.repo_name
779 )
778 )
780 )
779 )
781
780
782 @HasPermissionAnyDecorator('hg.admin')
781 @HasPermissionAnyDecorator('hg.admin')
783 def revoke_users_group_permission(self, apiuser, repoid, usersgroupid):
782 def revoke_users_group_permission(self, apiuser, repoid, usersgroupid):
784 """
783 """
785 Revoke permission for users group on given repository
784 Revoke permission for users group on given repository
786
785
787 :param apiuser:
786 :param apiuser:
788 :param repoid:
787 :param repoid:
789 :param usersgroupid:
788 :param usersgroupid:
790 """
789 """
791 repo = get_repo_or_error(repoid)
790 repo = get_repo_or_error(repoid)
792 users_group = get_users_group_or_error(usersgroupid)
791 users_group = get_users_group_or_error(usersgroupid)
793
792
794 try:
793 try:
795 RepoModel().revoke_users_group_permission(repo=repo,
794 RepoModel().revoke_users_group_permission(repo=repo,
796 group_name=users_group)
795 group_name=users_group)
797
796
798 Session().commit()
797 Session().commit()
799 return dict(
798 return dict(
800 msg='Revoked perm for users group: `%s` in repo: `%s`' % (
799 msg='Revoked perm for users group: `%s` in repo: `%s`' % (
801 users_group.users_group_name, repo.repo_name
800 users_group.users_group_name, repo.repo_name
802 ),
801 ),
803 success=True
802 success=True
804 )
803 )
805 except Exception:
804 except Exception:
806 log.error(traceback.format_exc())
805 log.error(traceback.format_exc())
807 raise JSONRPCError(
806 raise JSONRPCError(
808 'failed to edit permission for users group: `%s` in '
807 'failed to edit permission for users group: `%s` in '
809 'repo: `%s`' % (
808 'repo: `%s`' % (
810 users_group.users_group_name, repo.repo_name
809 users_group.users_group_name, repo.repo_name
811 )
810 )
812 )
811 )
@@ -1,1812 +1,1811 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.db
3 rhodecode.model.db
4 ~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~
5
5
6 Database Models for RhodeCode
6 Database Models for RhodeCode
7
7
8 :created_on: Apr 08, 2010
8 :created_on: Apr 08, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import os
26 import os
27 import logging
27 import logging
28 import datetime
28 import datetime
29 import traceback
29 import traceback
30 import hashlib
30 import hashlib
31 import time
31 import time
32 from collections import defaultdict
32 from collections import defaultdict
33
33
34 from sqlalchemy import *
34 from sqlalchemy import *
35 from sqlalchemy.ext.hybrid import hybrid_property
35 from sqlalchemy.ext.hybrid import hybrid_property
36 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
36 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
37 from sqlalchemy.exc import DatabaseError
37 from sqlalchemy.exc import DatabaseError
38 from beaker.cache import cache_region, region_invalidate
38 from beaker.cache import cache_region, region_invalidate
39 from webob.exc import HTTPNotFound
39 from webob.exc import HTTPNotFound
40
40
41 from pylons.i18n.translation import lazy_ugettext as _
41 from pylons.i18n.translation import lazy_ugettext as _
42
42
43 from rhodecode.lib.vcs import get_backend
43 from rhodecode.lib.vcs import get_backend
44 from rhodecode.lib.vcs.utils.helpers import get_scm
44 from rhodecode.lib.vcs.utils.helpers import get_scm
45 from rhodecode.lib.vcs.exceptions import VCSError
45 from rhodecode.lib.vcs.exceptions import VCSError
46 from rhodecode.lib.vcs.utils.lazy import LazyProperty
46 from rhodecode.lib.vcs.utils.lazy import LazyProperty
47
47
48 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
48 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
49 safe_unicode, remove_suffix
49 safe_unicode, remove_suffix
50 from rhodecode.lib.compat import json
50 from rhodecode.lib.compat import json
51 from rhodecode.lib.caching_query import FromCache
51 from rhodecode.lib.caching_query import FromCache
52
52
53 from rhodecode.model.meta import Base, Session
53 from rhodecode.model.meta import Base, Session
54
54
55 URL_SEP = '/'
55 URL_SEP = '/'
56 log = logging.getLogger(__name__)
56 log = logging.getLogger(__name__)
57
57
58 #==============================================================================
58 #==============================================================================
59 # BASE CLASSES
59 # BASE CLASSES
60 #==============================================================================
60 #==============================================================================
61
61
62 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
62 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
63
63
64
64
65 class BaseModel(object):
65 class BaseModel(object):
66 """
66 """
67 Base Model for all classess
67 Base Model for all classess
68 """
68 """
69
69
70 @classmethod
70 @classmethod
71 def _get_keys(cls):
71 def _get_keys(cls):
72 """return column names for this model """
72 """return column names for this model """
73 return class_mapper(cls).c.keys()
73 return class_mapper(cls).c.keys()
74
74
75 def get_dict(self):
75 def get_dict(self):
76 """
76 """
77 return dict with keys and values corresponding
77 return dict with keys and values corresponding
78 to this model data """
78 to this model data """
79
79
80 d = {}
80 d = {}
81 for k in self._get_keys():
81 for k in self._get_keys():
82 d[k] = getattr(self, k)
82 d[k] = getattr(self, k)
83
83
84 # also use __json__() if present to get additional fields
84 # also use __json__() if present to get additional fields
85 _json_attr = getattr(self, '__json__', None)
85 _json_attr = getattr(self, '__json__', None)
86 if _json_attr:
86 if _json_attr:
87 # update with attributes from __json__
87 # update with attributes from __json__
88 if callable(_json_attr):
88 if callable(_json_attr):
89 _json_attr = _json_attr()
89 _json_attr = _json_attr()
90 for k, val in _json_attr.iteritems():
90 for k, val in _json_attr.iteritems():
91 d[k] = val
91 d[k] = val
92 return d
92 return d
93
93
94 def get_appstruct(self):
94 def get_appstruct(self):
95 """return list with keys and values tupples corresponding
95 """return list with keys and values tupples corresponding
96 to this model data """
96 to this model data """
97
97
98 l = []
98 l = []
99 for k in self._get_keys():
99 for k in self._get_keys():
100 l.append((k, getattr(self, k),))
100 l.append((k, getattr(self, k),))
101 return l
101 return l
102
102
103 def populate_obj(self, populate_dict):
103 def populate_obj(self, populate_dict):
104 """populate model with data from given populate_dict"""
104 """populate model with data from given populate_dict"""
105
105
106 for k in self._get_keys():
106 for k in self._get_keys():
107 if k in populate_dict:
107 if k in populate_dict:
108 setattr(self, k, populate_dict[k])
108 setattr(self, k, populate_dict[k])
109
109
110 @classmethod
110 @classmethod
111 def query(cls):
111 def query(cls):
112 return Session().query(cls)
112 return Session().query(cls)
113
113
114 @classmethod
114 @classmethod
115 def get(cls, id_):
115 def get(cls, id_):
116 if id_:
116 if id_:
117 return cls.query().get(id_)
117 return cls.query().get(id_)
118
118
119 @classmethod
119 @classmethod
120 def get_or_404(cls, id_):
120 def get_or_404(cls, id_):
121 try:
121 try:
122 id_ = int(id_)
122 id_ = int(id_)
123 except (TypeError, ValueError):
123 except (TypeError, ValueError):
124 raise HTTPNotFound
124 raise HTTPNotFound
125
125
126 res = cls.query().get(id_)
126 res = cls.query().get(id_)
127 if not res:
127 if not res:
128 raise HTTPNotFound
128 raise HTTPNotFound
129 return res
129 return res
130
130
131 @classmethod
131 @classmethod
132 def getAll(cls):
132 def getAll(cls):
133 return cls.query().all()
133 return cls.query().all()
134
134
135 @classmethod
135 @classmethod
136 def delete(cls, id_):
136 def delete(cls, id_):
137 obj = cls.query().get(id_)
137 obj = cls.query().get(id_)
138 Session().delete(obj)
138 Session().delete(obj)
139
139
140 def __repr__(self):
140 def __repr__(self):
141 if hasattr(self, '__unicode__'):
141 if hasattr(self, '__unicode__'):
142 # python repr needs to return str
142 # python repr needs to return str
143 return safe_str(self.__unicode__())
143 return safe_str(self.__unicode__())
144 return '<DB:%s>' % (self.__class__.__name__)
144 return '<DB:%s>' % (self.__class__.__name__)
145
145
146
146
147 class RhodeCodeSetting(Base, BaseModel):
147 class RhodeCodeSetting(Base, BaseModel):
148 __tablename__ = 'rhodecode_settings'
148 __tablename__ = 'rhodecode_settings'
149 __table_args__ = (
149 __table_args__ = (
150 UniqueConstraint('app_settings_name'),
150 UniqueConstraint('app_settings_name'),
151 {'extend_existing': True, 'mysql_engine': 'InnoDB',
151 {'extend_existing': True, 'mysql_engine': 'InnoDB',
152 'mysql_charset': 'utf8'}
152 'mysql_charset': 'utf8'}
153 )
153 )
154 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
154 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
155 app_settings_name = Column("app_settings_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
155 app_settings_name = Column("app_settings_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
156 _app_settings_value = Column("app_settings_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
156 _app_settings_value = Column("app_settings_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
157
157
158 def __init__(self, k='', v=''):
158 def __init__(self, k='', v=''):
159 self.app_settings_name = k
159 self.app_settings_name = k
160 self.app_settings_value = v
160 self.app_settings_value = v
161
161
162 @validates('_app_settings_value')
162 @validates('_app_settings_value')
163 def validate_settings_value(self, key, val):
163 def validate_settings_value(self, key, val):
164 assert type(val) == unicode
164 assert type(val) == unicode
165 return val
165 return val
166
166
167 @hybrid_property
167 @hybrid_property
168 def app_settings_value(self):
168 def app_settings_value(self):
169 v = self._app_settings_value
169 v = self._app_settings_value
170 if self.app_settings_name == 'ldap_active':
170 if self.app_settings_name == 'ldap_active':
171 v = str2bool(v)
171 v = str2bool(v)
172 return v
172 return v
173
173
174 @app_settings_value.setter
174 @app_settings_value.setter
175 def app_settings_value(self, val):
175 def app_settings_value(self, val):
176 """
176 """
177 Setter that will always make sure we use unicode in app_settings_value
177 Setter that will always make sure we use unicode in app_settings_value
178
178
179 :param val:
179 :param val:
180 """
180 """
181 self._app_settings_value = safe_unicode(val)
181 self._app_settings_value = safe_unicode(val)
182
182
183 def __unicode__(self):
183 def __unicode__(self):
184 return u"<%s('%s:%s')>" % (
184 return u"<%s('%s:%s')>" % (
185 self.__class__.__name__,
185 self.__class__.__name__,
186 self.app_settings_name, self.app_settings_value
186 self.app_settings_name, self.app_settings_value
187 )
187 )
188
188
189 @classmethod
189 @classmethod
190 def get_by_name(cls, key):
190 def get_by_name(cls, key):
191 return cls.query()\
191 return cls.query()\
192 .filter(cls.app_settings_name == key).scalar()
192 .filter(cls.app_settings_name == key).scalar()
193
193
194 @classmethod
194 @classmethod
195 def get_by_name_or_create(cls, key):
195 def get_by_name_or_create(cls, key):
196 res = cls.get_by_name(key)
196 res = cls.get_by_name(key)
197 if not res:
197 if not res:
198 res = cls(key)
198 res = cls(key)
199 return res
199 return res
200
200
201 @classmethod
201 @classmethod
202 def get_app_settings(cls, cache=False):
202 def get_app_settings(cls, cache=False):
203
203
204 ret = cls.query()
204 ret = cls.query()
205
205
206 if cache:
206 if cache:
207 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
207 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
208
208
209 if not ret:
209 if not ret:
210 raise Exception('Could not get application settings !')
210 raise Exception('Could not get application settings !')
211 settings = {}
211 settings = {}
212 for each in ret:
212 for each in ret:
213 settings['rhodecode_' + each.app_settings_name] = \
213 settings['rhodecode_' + each.app_settings_name] = \
214 each.app_settings_value
214 each.app_settings_value
215
215
216 return settings
216 return settings
217
217
218 @classmethod
218 @classmethod
219 def get_ldap_settings(cls, cache=False):
219 def get_ldap_settings(cls, cache=False):
220 ret = cls.query()\
220 ret = cls.query()\
221 .filter(cls.app_settings_name.startswith('ldap_')).all()
221 .filter(cls.app_settings_name.startswith('ldap_')).all()
222 fd = {}
222 fd = {}
223 for row in ret:
223 for row in ret:
224 fd.update({row.app_settings_name: row.app_settings_value})
224 fd.update({row.app_settings_name: row.app_settings_value})
225
225
226 return fd
226 return fd
227
227
228
228
229 class RhodeCodeUi(Base, BaseModel):
229 class RhodeCodeUi(Base, BaseModel):
230 __tablename__ = 'rhodecode_ui'
230 __tablename__ = 'rhodecode_ui'
231 __table_args__ = (
231 __table_args__ = (
232 UniqueConstraint('ui_key'),
232 UniqueConstraint('ui_key'),
233 {'extend_existing': True, 'mysql_engine': 'InnoDB',
233 {'extend_existing': True, 'mysql_engine': 'InnoDB',
234 'mysql_charset': 'utf8'}
234 'mysql_charset': 'utf8'}
235 )
235 )
236
236
237 HOOK_UPDATE = 'changegroup.update'
237 HOOK_UPDATE = 'changegroup.update'
238 HOOK_REPO_SIZE = 'changegroup.repo_size'
238 HOOK_REPO_SIZE = 'changegroup.repo_size'
239 HOOK_PUSH = 'changegroup.push_logger'
239 HOOK_PUSH = 'changegroup.push_logger'
240 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
240 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
241 HOOK_PULL = 'outgoing.pull_logger'
241 HOOK_PULL = 'outgoing.pull_logger'
242 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
242 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
243
243
244 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
244 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
245 ui_section = Column("ui_section", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
245 ui_section = Column("ui_section", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
246 ui_key = Column("ui_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
246 ui_key = Column("ui_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
247 ui_value = Column("ui_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
247 ui_value = Column("ui_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
248 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
248 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
249
249
250 @classmethod
250 @classmethod
251 def get_by_key(cls, key):
251 def get_by_key(cls, key):
252 return cls.query().filter(cls.ui_key == key).scalar()
252 return cls.query().filter(cls.ui_key == key).scalar()
253
253
254 @classmethod
254 @classmethod
255 def get_builtin_hooks(cls):
255 def get_builtin_hooks(cls):
256 q = cls.query()
256 q = cls.query()
257 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
257 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
258 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
258 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
259 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
259 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
260 return q.all()
260 return q.all()
261
261
262 @classmethod
262 @classmethod
263 def get_custom_hooks(cls):
263 def get_custom_hooks(cls):
264 q = cls.query()
264 q = cls.query()
265 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
265 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
266 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
266 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
267 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
267 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
268 q = q.filter(cls.ui_section == 'hooks')
268 q = q.filter(cls.ui_section == 'hooks')
269 return q.all()
269 return q.all()
270
270
271 @classmethod
271 @classmethod
272 def get_repos_location(cls):
272 def get_repos_location(cls):
273 return cls.get_by_key('/').ui_value
273 return cls.get_by_key('/').ui_value
274
274
275 @classmethod
275 @classmethod
276 def create_or_update_hook(cls, key, val):
276 def create_or_update_hook(cls, key, val):
277 new_ui = cls.get_by_key(key) or cls()
277 new_ui = cls.get_by_key(key) or cls()
278 new_ui.ui_section = 'hooks'
278 new_ui.ui_section = 'hooks'
279 new_ui.ui_active = True
279 new_ui.ui_active = True
280 new_ui.ui_key = key
280 new_ui.ui_key = key
281 new_ui.ui_value = val
281 new_ui.ui_value = val
282
282
283 Session().add(new_ui)
283 Session().add(new_ui)
284
284
285 def __repr__(self):
285 def __repr__(self):
286 return '<DB:%s[%s:%s]>' % (self.__class__.__name__, self.ui_key,
286 return '<DB:%s[%s:%s]>' % (self.__class__.__name__, self.ui_key,
287 self.ui_value)
287 self.ui_value)
288
288
289
289
290 class User(Base, BaseModel):
290 class User(Base, BaseModel):
291 __tablename__ = 'users'
291 __tablename__ = 'users'
292 __table_args__ = (
292 __table_args__ = (
293 UniqueConstraint('username'), UniqueConstraint('email'),
293 UniqueConstraint('username'), UniqueConstraint('email'),
294 Index('u_username_idx', 'username'),
294 Index('u_username_idx', 'username'),
295 Index('u_email_idx', 'email'),
295 Index('u_email_idx', 'email'),
296 {'extend_existing': True, 'mysql_engine': 'InnoDB',
296 {'extend_existing': True, 'mysql_engine': 'InnoDB',
297 'mysql_charset': 'utf8'}
297 'mysql_charset': 'utf8'}
298 )
298 )
299 DEFAULT_USER = 'default'
299 DEFAULT_USER = 'default'
300 DEFAULT_PERMISSIONS = [
300 DEFAULT_PERMISSIONS = [
301 'hg.register.manual_activate', 'hg.create.repository',
301 'hg.register.manual_activate', 'hg.create.repository',
302 'hg.fork.repository', 'repository.read'
302 'hg.fork.repository', 'repository.read'
303 ]
303 ]
304 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
304 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
305 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
305 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
306 password = Column("password", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
306 password = Column("password", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
307 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
307 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
308 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
308 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
309 name = Column("firstname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
309 name = Column("firstname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
310 lastname = Column("lastname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
310 lastname = Column("lastname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
311 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
311 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
312 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
312 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
313 ldap_dn = Column("ldap_dn", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
313 ldap_dn = Column("ldap_dn", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
314 api_key = Column("api_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
314 api_key = Column("api_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
315 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
315 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
316
316
317 user_log = relationship('UserLog', cascade='all')
317 user_log = relationship('UserLog', cascade='all')
318 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
318 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
319
319
320 repositories = relationship('Repository')
320 repositories = relationship('Repository')
321 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
321 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
322 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
322 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
323 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
323 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
324
324
325 group_member = relationship('UsersGroupMember', cascade='all')
325 group_member = relationship('UsersGroupMember', cascade='all')
326
326
327 notifications = relationship('UserNotification', cascade='all')
327 notifications = relationship('UserNotification', cascade='all')
328 # notifications assigned to this user
328 # notifications assigned to this user
329 user_created_notifications = relationship('Notification', cascade='all')
329 user_created_notifications = relationship('Notification', cascade='all')
330 # comments created by this user
330 # comments created by this user
331 user_comments = relationship('ChangesetComment', cascade='all')
331 user_comments = relationship('ChangesetComment', cascade='all')
332 #extra emails for this user
332 #extra emails for this user
333 user_emails = relationship('UserEmailMap', cascade='all')
333 user_emails = relationship('UserEmailMap', cascade='all')
334
334
335 @hybrid_property
335 @hybrid_property
336 def email(self):
336 def email(self):
337 return self._email
337 return self._email
338
338
339 @email.setter
339 @email.setter
340 def email(self, val):
340 def email(self, val):
341 self._email = val.lower() if val else None
341 self._email = val.lower() if val else None
342
342
343 @property
343 @property
344 def firstname(self):
344 def firstname(self):
345 # alias for future
345 # alias for future
346 return self.name
346 return self.name
347
347
348 @property
348 @property
349 def emails(self):
349 def emails(self):
350 other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
350 other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
351 return [self.email] + [x.email for x in other]
351 return [self.email] + [x.email for x in other]
352
352
353 @property
353 @property
354 def username_and_name(self):
354 def username_and_name(self):
355 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
355 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
356
356
357 @property
357 @property
358 def full_name(self):
358 def full_name(self):
359 return '%s %s' % (self.firstname, self.lastname)
359 return '%s %s' % (self.firstname, self.lastname)
360
360
361 @property
361 @property
362 def full_name_or_username(self):
362 def full_name_or_username(self):
363 return ('%s %s' % (self.firstname, self.lastname)
363 return ('%s %s' % (self.firstname, self.lastname)
364 if (self.firstname and self.lastname) else self.username)
364 if (self.firstname and self.lastname) else self.username)
365
365
366 @property
366 @property
367 def full_contact(self):
367 def full_contact(self):
368 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
368 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
369
369
370 @property
370 @property
371 def short_contact(self):
371 def short_contact(self):
372 return '%s %s' % (self.firstname, self.lastname)
372 return '%s %s' % (self.firstname, self.lastname)
373
373
374 @property
374 @property
375 def is_admin(self):
375 def is_admin(self):
376 return self.admin
376 return self.admin
377
377
378 def __unicode__(self):
378 def __unicode__(self):
379 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
379 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
380 self.user_id, self.username)
380 self.user_id, self.username)
381
381
382 @classmethod
382 @classmethod
383 def get_by_username(cls, username, case_insensitive=False, cache=False):
383 def get_by_username(cls, username, case_insensitive=False, cache=False):
384 if case_insensitive:
384 if case_insensitive:
385 q = cls.query().filter(cls.username.ilike(username))
385 q = cls.query().filter(cls.username.ilike(username))
386 else:
386 else:
387 q = cls.query().filter(cls.username == username)
387 q = cls.query().filter(cls.username == username)
388
388
389 if cache:
389 if cache:
390 q = q.options(FromCache(
390 q = q.options(FromCache(
391 "sql_cache_short",
391 "sql_cache_short",
392 "get_user_%s" % _hash_key(username)
392 "get_user_%s" % _hash_key(username)
393 )
393 )
394 )
394 )
395 return q.scalar()
395 return q.scalar()
396
396
397 @classmethod
397 @classmethod
398 def get_by_api_key(cls, api_key, cache=False):
398 def get_by_api_key(cls, api_key, cache=False):
399 q = cls.query().filter(cls.api_key == api_key)
399 q = cls.query().filter(cls.api_key == api_key)
400
400
401 if cache:
401 if cache:
402 q = q.options(FromCache("sql_cache_short",
402 q = q.options(FromCache("sql_cache_short",
403 "get_api_key_%s" % api_key))
403 "get_api_key_%s" % api_key))
404 return q.scalar()
404 return q.scalar()
405
405
406 @classmethod
406 @classmethod
407 def get_by_email(cls, email, case_insensitive=False, cache=False):
407 def get_by_email(cls, email, case_insensitive=False, cache=False):
408 if case_insensitive:
408 if case_insensitive:
409 q = cls.query().filter(cls.email.ilike(email))
409 q = cls.query().filter(cls.email.ilike(email))
410 else:
410 else:
411 q = cls.query().filter(cls.email == email)
411 q = cls.query().filter(cls.email == email)
412
412
413 if cache:
413 if cache:
414 q = q.options(FromCache("sql_cache_short",
414 q = q.options(FromCache("sql_cache_short",
415 "get_email_key_%s" % email))
415 "get_email_key_%s" % email))
416
416
417 ret = q.scalar()
417 ret = q.scalar()
418 if ret is None:
418 if ret is None:
419 q = UserEmailMap.query()
419 q = UserEmailMap.query()
420 # try fetching in alternate email map
420 # try fetching in alternate email map
421 if case_insensitive:
421 if case_insensitive:
422 q = q.filter(UserEmailMap.email.ilike(email))
422 q = q.filter(UserEmailMap.email.ilike(email))
423 else:
423 else:
424 q = q.filter(UserEmailMap.email == email)
424 q = q.filter(UserEmailMap.email == email)
425 q = q.options(joinedload(UserEmailMap.user))
425 q = q.options(joinedload(UserEmailMap.user))
426 if cache:
426 if cache:
427 q = q.options(FromCache("sql_cache_short",
427 q = q.options(FromCache("sql_cache_short",
428 "get_email_map_key_%s" % email))
428 "get_email_map_key_%s" % email))
429 ret = getattr(q.scalar(), 'user', None)
429 ret = getattr(q.scalar(), 'user', None)
430
430
431 return ret
431 return ret
432
432
433 def update_lastlogin(self):
433 def update_lastlogin(self):
434 """Update user lastlogin"""
434 """Update user lastlogin"""
435 self.last_login = datetime.datetime.now()
435 self.last_login = datetime.datetime.now()
436 Session().add(self)
436 Session().add(self)
437 log.debug('updated user %s lastlogin' % self.username)
437 log.debug('updated user %s lastlogin' % self.username)
438
438
439 def get_api_data(self):
439 def get_api_data(self):
440 """
440 """
441 Common function for generating user related data for API
441 Common function for generating user related data for API
442 """
442 """
443 user = self
443 user = self
444 data = dict(
444 data = dict(
445 user_id=user.user_id,
445 user_id=user.user_id,
446 username=user.username,
446 username=user.username,
447 firstname=user.name,
447 firstname=user.name,
448 lastname=user.lastname,
448 lastname=user.lastname,
449 email=user.email,
449 email=user.email,
450 emails=user.emails,
450 emails=user.emails,
451 api_key=user.api_key,
451 api_key=user.api_key,
452 active=user.active,
452 active=user.active,
453 admin=user.admin,
453 admin=user.admin,
454 ldap_dn=user.ldap_dn,
454 ldap_dn=user.ldap_dn,
455 last_login=user.last_login,
455 last_login=user.last_login,
456 )
456 )
457 return data
457 return data
458
458
459 def __json__(self):
459 def __json__(self):
460 data = dict(
460 data = dict(
461 full_name=self.full_name,
461 full_name=self.full_name,
462 full_name_or_username=self.full_name_or_username,
462 full_name_or_username=self.full_name_or_username,
463 short_contact=self.short_contact,
463 short_contact=self.short_contact,
464 full_contact=self.full_contact
464 full_contact=self.full_contact
465 )
465 )
466 data.update(self.get_api_data())
466 data.update(self.get_api_data())
467 return data
467 return data
468
468
469
469
470 class UserEmailMap(Base, BaseModel):
470 class UserEmailMap(Base, BaseModel):
471 __tablename__ = 'user_email_map'
471 __tablename__ = 'user_email_map'
472 __table_args__ = (
472 __table_args__ = (
473 Index('uem_email_idx', 'email'),
473 Index('uem_email_idx', 'email'),
474 UniqueConstraint('email'),
474 UniqueConstraint('email'),
475 {'extend_existing': True, 'mysql_engine': 'InnoDB',
475 {'extend_existing': True, 'mysql_engine': 'InnoDB',
476 'mysql_charset': 'utf8'}
476 'mysql_charset': 'utf8'}
477 )
477 )
478 __mapper_args__ = {}
478 __mapper_args__ = {}
479
479
480 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
480 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
481 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
481 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
482 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
482 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
483 user = relationship('User', lazy='joined')
483 user = relationship('User', lazy='joined')
484
484
485 @validates('_email')
485 @validates('_email')
486 def validate_email(self, key, email):
486 def validate_email(self, key, email):
487 # check if this email is not main one
487 # check if this email is not main one
488 main_email = Session().query(User).filter(User.email == email).scalar()
488 main_email = Session().query(User).filter(User.email == email).scalar()
489 if main_email is not None:
489 if main_email is not None:
490 raise AttributeError('email %s is present is user table' % email)
490 raise AttributeError('email %s is present is user table' % email)
491 return email
491 return email
492
492
493 @hybrid_property
493 @hybrid_property
494 def email(self):
494 def email(self):
495 return self._email
495 return self._email
496
496
497 @email.setter
497 @email.setter
498 def email(self, val):
498 def email(self, val):
499 self._email = val.lower() if val else None
499 self._email = val.lower() if val else None
500
500
501
501
502 class UserLog(Base, BaseModel):
502 class UserLog(Base, BaseModel):
503 __tablename__ = 'user_logs'
503 __tablename__ = 'user_logs'
504 __table_args__ = (
504 __table_args__ = (
505 {'extend_existing': True, 'mysql_engine': 'InnoDB',
505 {'extend_existing': True, 'mysql_engine': 'InnoDB',
506 'mysql_charset': 'utf8'},
506 'mysql_charset': 'utf8'},
507 )
507 )
508 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
508 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
509 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
509 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
510 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
510 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
511 repository_name = Column("repository_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
511 repository_name = Column("repository_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
512 user_ip = Column("user_ip", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
512 user_ip = Column("user_ip", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
513 action = Column("action", UnicodeText(1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
513 action = Column("action", UnicodeText(1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
514 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
514 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
515
515
516 @property
516 @property
517 def action_as_day(self):
517 def action_as_day(self):
518 return datetime.date(*self.action_date.timetuple()[:3])
518 return datetime.date(*self.action_date.timetuple()[:3])
519
519
520 user = relationship('User')
520 user = relationship('User')
521 repository = relationship('Repository', cascade='')
521 repository = relationship('Repository', cascade='')
522
522
523
523
524 class UsersGroup(Base, BaseModel):
524 class UsersGroup(Base, BaseModel):
525 __tablename__ = 'users_groups'
525 __tablename__ = 'users_groups'
526 __table_args__ = (
526 __table_args__ = (
527 {'extend_existing': True, 'mysql_engine': 'InnoDB',
527 {'extend_existing': True, 'mysql_engine': 'InnoDB',
528 'mysql_charset': 'utf8'},
528 'mysql_charset': 'utf8'},
529 )
529 )
530
530
531 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
531 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
532 users_group_name = Column("users_group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
532 users_group_name = Column("users_group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
533 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
533 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
534 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
534 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
535
535
536 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
536 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
537 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
537 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
538 users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
538 users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
539
539
540 def __unicode__(self):
540 def __unicode__(self):
541 return u'<userGroup(%s)>' % (self.users_group_name)
541 return u'<userGroup(%s)>' % (self.users_group_name)
542
542
543 @classmethod
543 @classmethod
544 def get_by_group_name(cls, group_name, cache=False,
544 def get_by_group_name(cls, group_name, cache=False,
545 case_insensitive=False):
545 case_insensitive=False):
546 if case_insensitive:
546 if case_insensitive:
547 q = cls.query().filter(cls.users_group_name.ilike(group_name))
547 q = cls.query().filter(cls.users_group_name.ilike(group_name))
548 else:
548 else:
549 q = cls.query().filter(cls.users_group_name == group_name)
549 q = cls.query().filter(cls.users_group_name == group_name)
550 if cache:
550 if cache:
551 q = q.options(FromCache(
551 q = q.options(FromCache(
552 "sql_cache_short",
552 "sql_cache_short",
553 "get_user_%s" % _hash_key(group_name)
553 "get_user_%s" % _hash_key(group_name)
554 )
554 )
555 )
555 )
556 return q.scalar()
556 return q.scalar()
557
557
558 @classmethod
558 @classmethod
559 def get(cls, users_group_id, cache=False):
559 def get(cls, users_group_id, cache=False):
560 users_group = cls.query()
560 users_group = cls.query()
561 if cache:
561 if cache:
562 users_group = users_group.options(FromCache("sql_cache_short",
562 users_group = users_group.options(FromCache("sql_cache_short",
563 "get_users_group_%s" % users_group_id))
563 "get_users_group_%s" % users_group_id))
564 return users_group.get(users_group_id)
564 return users_group.get(users_group_id)
565
565
566 def get_api_data(self):
566 def get_api_data(self):
567 users_group = self
567 users_group = self
568
568
569 data = dict(
569 data = dict(
570 users_group_id=users_group.users_group_id,
570 users_group_id=users_group.users_group_id,
571 group_name=users_group.users_group_name,
571 group_name=users_group.users_group_name,
572 active=users_group.users_group_active,
572 active=users_group.users_group_active,
573 )
573 )
574
574
575 return data
575 return data
576
576
577
577
578 class UsersGroupMember(Base, BaseModel):
578 class UsersGroupMember(Base, BaseModel):
579 __tablename__ = 'users_groups_members'
579 __tablename__ = 'users_groups_members'
580 __table_args__ = (
580 __table_args__ = (
581 {'extend_existing': True, 'mysql_engine': 'InnoDB',
581 {'extend_existing': True, 'mysql_engine': 'InnoDB',
582 'mysql_charset': 'utf8'},
582 'mysql_charset': 'utf8'},
583 )
583 )
584
584
585 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
585 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
586 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
586 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
587 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
587 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
588
588
589 user = relationship('User', lazy='joined')
589 user = relationship('User', lazy='joined')
590 users_group = relationship('UsersGroup')
590 users_group = relationship('UsersGroup')
591
591
592 def __init__(self, gr_id='', u_id=''):
592 def __init__(self, gr_id='', u_id=''):
593 self.users_group_id = gr_id
593 self.users_group_id = gr_id
594 self.user_id = u_id
594 self.user_id = u_id
595
595
596
596
597 class Repository(Base, BaseModel):
597 class Repository(Base, BaseModel):
598 __tablename__ = 'repositories'
598 __tablename__ = 'repositories'
599 __table_args__ = (
599 __table_args__ = (
600 UniqueConstraint('repo_name'),
600 UniqueConstraint('repo_name'),
601 Index('r_repo_name_idx', 'repo_name'),
601 Index('r_repo_name_idx', 'repo_name'),
602 {'extend_existing': True, 'mysql_engine': 'InnoDB',
602 {'extend_existing': True, 'mysql_engine': 'InnoDB',
603 'mysql_charset': 'utf8'},
603 'mysql_charset': 'utf8'},
604 )
604 )
605
605
606 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
606 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
607 repo_name = Column("repo_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
607 repo_name = Column("repo_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
608 clone_uri = Column("clone_uri", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
608 clone_uri = Column("clone_uri", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
609 repo_type = Column("repo_type", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
609 repo_type = Column("repo_type", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
610 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
610 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
611 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
611 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
612 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
612 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
613 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
613 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
614 description = Column("description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
614 description = Column("description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
615 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
615 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
616 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
616 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
617 landing_rev = Column("landing_revision", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
617 landing_rev = Column("landing_revision", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
618 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
618 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
619 _locked = Column("locked", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
619 _locked = Column("locked", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
620
620
621 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
621 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
622 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
622 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
623
623
624 user = relationship('User')
624 user = relationship('User')
625 fork = relationship('Repository', remote_side=repo_id)
625 fork = relationship('Repository', remote_side=repo_id)
626 group = relationship('RepoGroup')
626 group = relationship('RepoGroup')
627 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
627 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
628 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
628 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
629 stats = relationship('Statistics', cascade='all', uselist=False)
629 stats = relationship('Statistics', cascade='all', uselist=False)
630
630
631 followers = relationship('UserFollowing',
631 followers = relationship('UserFollowing',
632 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
632 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
633 cascade='all')
633 cascade='all')
634
634
635 logs = relationship('UserLog')
635 logs = relationship('UserLog')
636 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
636 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
637
637
638 pull_requests_org = relationship('PullRequest',
638 pull_requests_org = relationship('PullRequest',
639 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
639 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
640 cascade="all, delete, delete-orphan")
640 cascade="all, delete, delete-orphan")
641
641
642 pull_requests_other = relationship('PullRequest',
642 pull_requests_other = relationship('PullRequest',
643 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
643 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
644 cascade="all, delete, delete-orphan")
644 cascade="all, delete, delete-orphan")
645
645
646 def __unicode__(self):
646 def __unicode__(self):
647 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
647 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
648 self.repo_name)
648 self.repo_name)
649
649
650 @hybrid_property
650 @hybrid_property
651 def locked(self):
651 def locked(self):
652 # always should return [user_id, timelocked]
652 # always should return [user_id, timelocked]
653 if self._locked:
653 if self._locked:
654 _lock_info = self._locked.split(':')
654 _lock_info = self._locked.split(':')
655 return int(_lock_info[0]), _lock_info[1]
655 return int(_lock_info[0]), _lock_info[1]
656 return [None, None]
656 return [None, None]
657
657
658 @locked.setter
658 @locked.setter
659 def locked(self, val):
659 def locked(self, val):
660 if val and isinstance(val, (list, tuple)):
660 if val and isinstance(val, (list, tuple)):
661 self._locked = ':'.join(map(str, val))
661 self._locked = ':'.join(map(str, val))
662 else:
662 else:
663 self._locked = None
663 self._locked = None
664
664
665 @classmethod
665 @classmethod
666 def url_sep(cls):
666 def url_sep(cls):
667 return URL_SEP
667 return URL_SEP
668
668
669 @classmethod
669 @classmethod
670 def get_by_repo_name(cls, repo_name):
670 def get_by_repo_name(cls, repo_name):
671 q = Session().query(cls).filter(cls.repo_name == repo_name)
671 q = Session().query(cls).filter(cls.repo_name == repo_name)
672 q = q.options(joinedload(Repository.fork))\
672 q = q.options(joinedload(Repository.fork))\
673 .options(joinedload(Repository.user))\
673 .options(joinedload(Repository.user))\
674 .options(joinedload(Repository.group))
674 .options(joinedload(Repository.group))
675 return q.scalar()
675 return q.scalar()
676
676
677 @classmethod
677 @classmethod
678 def get_by_full_path(cls, repo_full_path):
678 def get_by_full_path(cls, repo_full_path):
679 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
679 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
680 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
680 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
681
681
682 @classmethod
682 @classmethod
683 def get_repo_forks(cls, repo_id):
683 def get_repo_forks(cls, repo_id):
684 return cls.query().filter(Repository.fork_id == repo_id)
684 return cls.query().filter(Repository.fork_id == repo_id)
685
685
686 @classmethod
686 @classmethod
687 def base_path(cls):
687 def base_path(cls):
688 """
688 """
689 Returns base path when all repos are stored
689 Returns base path when all repos are stored
690
690
691 :param cls:
691 :param cls:
692 """
692 """
693 q = Session().query(RhodeCodeUi)\
693 q = Session().query(RhodeCodeUi)\
694 .filter(RhodeCodeUi.ui_key == cls.url_sep())
694 .filter(RhodeCodeUi.ui_key == cls.url_sep())
695 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
695 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
696 return q.one().ui_value
696 return q.one().ui_value
697
697
698 @property
698 @property
699 def forks(self):
699 def forks(self):
700 """
700 """
701 Return forks of this repo
701 Return forks of this repo
702 """
702 """
703 return Repository.get_repo_forks(self.repo_id)
703 return Repository.get_repo_forks(self.repo_id)
704
704
705 @property
705 @property
706 def parent(self):
706 def parent(self):
707 """
707 """
708 Returns fork parent
708 Returns fork parent
709 """
709 """
710 return self.fork
710 return self.fork
711
711
712 @property
712 @property
713 def just_name(self):
713 def just_name(self):
714 return self.repo_name.split(Repository.url_sep())[-1]
714 return self.repo_name.split(Repository.url_sep())[-1]
715
715
716 @property
716 @property
717 def groups_with_parents(self):
717 def groups_with_parents(self):
718 groups = []
718 groups = []
719 if self.group is None:
719 if self.group is None:
720 return groups
720 return groups
721
721
722 cur_gr = self.group
722 cur_gr = self.group
723 groups.insert(0, cur_gr)
723 groups.insert(0, cur_gr)
724 while 1:
724 while 1:
725 gr = getattr(cur_gr, 'parent_group', None)
725 gr = getattr(cur_gr, 'parent_group', None)
726 cur_gr = cur_gr.parent_group
726 cur_gr = cur_gr.parent_group
727 if gr is None:
727 if gr is None:
728 break
728 break
729 groups.insert(0, gr)
729 groups.insert(0, gr)
730
730
731 return groups
731 return groups
732
732
733 @property
733 @property
734 def groups_and_repo(self):
734 def groups_and_repo(self):
735 return self.groups_with_parents, self.just_name
735 return self.groups_with_parents, self.just_name
736
736
737 @LazyProperty
737 @LazyProperty
738 def repo_path(self):
738 def repo_path(self):
739 """
739 """
740 Returns base full path for that repository means where it actually
740 Returns base full path for that repository means where it actually
741 exists on a filesystem
741 exists on a filesystem
742 """
742 """
743 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
743 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
744 Repository.url_sep())
744 Repository.url_sep())
745 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
745 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
746 return q.one().ui_value
746 return q.one().ui_value
747
747
748 @property
748 @property
749 def repo_full_path(self):
749 def repo_full_path(self):
750 p = [self.repo_path]
750 p = [self.repo_path]
751 # we need to split the name by / since this is how we store the
751 # we need to split the name by / since this is how we store the
752 # names in the database, but that eventually needs to be converted
752 # names in the database, but that eventually needs to be converted
753 # into a valid system path
753 # into a valid system path
754 p += self.repo_name.split(Repository.url_sep())
754 p += self.repo_name.split(Repository.url_sep())
755 return os.path.join(*p)
755 return os.path.join(*p)
756
756
757 @property
757 @property
758 def cache_keys(self):
758 def cache_keys(self):
759 """
759 """
760 Returns associated cache keys for that repo
760 Returns associated cache keys for that repo
761 """
761 """
762 return CacheInvalidation.query()\
762 return CacheInvalidation.query()\
763 .filter(CacheInvalidation.cache_args == self.repo_name)\
763 .filter(CacheInvalidation.cache_args == self.repo_name)\
764 .order_by(CacheInvalidation.cache_key)\
764 .order_by(CacheInvalidation.cache_key)\
765 .all()
765 .all()
766
766
767 def get_new_name(self, repo_name):
767 def get_new_name(self, repo_name):
768 """
768 """
769 returns new full repository name based on assigned group and new new
769 returns new full repository name based on assigned group and new new
770
770
771 :param group_name:
771 :param group_name:
772 """
772 """
773 path_prefix = self.group.full_path_splitted if self.group else []
773 path_prefix = self.group.full_path_splitted if self.group else []
774 return Repository.url_sep().join(path_prefix + [repo_name])
774 return Repository.url_sep().join(path_prefix + [repo_name])
775
775
776 @property
776 @property
777 def _ui(self):
777 def _ui(self):
778 """
778 """
779 Creates an db based ui object for this repository
779 Creates an db based ui object for this repository
780 """
780 """
781 from rhodecode.lib.utils import make_ui
781 from rhodecode.lib.utils import make_ui
782 return make_ui('db', clear_session=False)
782 return make_ui('db', clear_session=False)
783
783
784 @classmethod
784 @classmethod
785 def inject_ui(cls, repo, extras={}):
785 def inject_ui(cls, repo, extras={}):
786 from rhodecode.lib.vcs.backends.hg import MercurialRepository
786 from rhodecode.lib.vcs.backends.hg import MercurialRepository
787 from rhodecode.lib.vcs.backends.git import GitRepository
787 from rhodecode.lib.vcs.backends.git import GitRepository
788 required = (MercurialRepository, GitRepository)
788 required = (MercurialRepository, GitRepository)
789 if not isinstance(repo, required):
789 if not isinstance(repo, required):
790 raise Exception('repo must be instance of %s' % required)
790 raise Exception('repo must be instance of %s' % required)
791
791
792 # inject ui extra param to log this action via push logger
792 # inject ui extra param to log this action via push logger
793 for k, v in extras.items():
793 for k, v in extras.items():
794 repo._repo.ui.setconfig('rhodecode_extras', k, v)
794 repo._repo.ui.setconfig('rhodecode_extras', k, v)
795
795
796 @classmethod
796 @classmethod
797 def is_valid(cls, repo_name):
797 def is_valid(cls, repo_name):
798 """
798 """
799 returns True if given repo name is a valid filesystem repository
799 returns True if given repo name is a valid filesystem repository
800
800
801 :param cls:
801 :param cls:
802 :param repo_name:
802 :param repo_name:
803 """
803 """
804 from rhodecode.lib.utils import is_valid_repo
804 from rhodecode.lib.utils import is_valid_repo
805
805
806 return is_valid_repo(repo_name, cls.base_path())
806 return is_valid_repo(repo_name, cls.base_path())
807
807
808 def get_api_data(self):
808 def get_api_data(self):
809 """
809 """
810 Common function for generating repo api data
810 Common function for generating repo api data
811
811
812 """
812 """
813 repo = self
813 repo = self
814 data = dict(
814 data = dict(
815 repo_id=repo.repo_id,
815 repo_id=repo.repo_id,
816 repo_name=repo.repo_name,
816 repo_name=repo.repo_name,
817 repo_type=repo.repo_type,
817 repo_type=repo.repo_type,
818 clone_uri=repo.clone_uri,
818 clone_uri=repo.clone_uri,
819 private=repo.private,
819 private=repo.private,
820 created_on=repo.created_on,
820 created_on=repo.created_on,
821 description=repo.description,
821 description=repo.description,
822 landing_rev=repo.landing_rev,
822 landing_rev=repo.landing_rev,
823 owner=repo.user.username,
823 owner=repo.user.username,
824 fork_of=repo.fork.repo_name if repo.fork else None
824 fork_of=repo.fork.repo_name if repo.fork else None
825 )
825 )
826
826
827 return data
827 return data
828
828
829 @classmethod
829 @classmethod
830 def lock(cls, repo, user_id):
830 def lock(cls, repo, user_id):
831 repo.locked = [user_id, time.time()]
831 repo.locked = [user_id, time.time()]
832 Session().add(repo)
832 Session().add(repo)
833 Session().commit()
833 Session().commit()
834
834
835 @classmethod
835 @classmethod
836 def unlock(cls, repo):
836 def unlock(cls, repo):
837 repo.locked = None
837 repo.locked = None
838 Session().add(repo)
838 Session().add(repo)
839 Session().commit()
839 Session().commit()
840
840
841 @property
841 @property
842 def last_db_change(self):
842 def last_db_change(self):
843 return self.updated_on
843 return self.updated_on
844
844
845 #==========================================================================
845 #==========================================================================
846 # SCM PROPERTIES
846 # SCM PROPERTIES
847 #==========================================================================
847 #==========================================================================
848
848
849 def get_changeset(self, rev=None):
849 def get_changeset(self, rev=None):
850 return get_changeset_safe(self.scm_instance, rev)
850 return get_changeset_safe(self.scm_instance, rev)
851
851
852 def get_landing_changeset(self):
852 def get_landing_changeset(self):
853 """
853 """
854 Returns landing changeset, or if that doesn't exist returns the tip
854 Returns landing changeset, or if that doesn't exist returns the tip
855 """
855 """
856 cs = self.get_changeset(self.landing_rev) or self.get_changeset()
856 cs = self.get_changeset(self.landing_rev) or self.get_changeset()
857 return cs
857 return cs
858
858
859 def update_last_change(self, last_change=None):
859 def update_last_change(self, last_change=None):
860 if last_change is None:
860 if last_change is None:
861 last_change = datetime.datetime.now()
861 last_change = datetime.datetime.now()
862 if self.updated_on is None or self.updated_on != last_change:
862 if self.updated_on is None or self.updated_on != last_change:
863 log.debug('updated repo %s with new date %s' % (self, last_change))
863 log.debug('updated repo %s with new date %s' % (self, last_change))
864 self.updated_on = last_change
864 self.updated_on = last_change
865 Session().add(self)
865 Session().add(self)
866 Session().commit()
866 Session().commit()
867
867
868 @property
868 @property
869 def tip(self):
869 def tip(self):
870 return self.get_changeset('tip')
870 return self.get_changeset('tip')
871
871
872 @property
872 @property
873 def author(self):
873 def author(self):
874 return self.tip.author
874 return self.tip.author
875
875
876 @property
876 @property
877 def last_change(self):
877 def last_change(self):
878 return self.scm_instance.last_change
878 return self.scm_instance.last_change
879
879
880 def get_comments(self, revisions=None):
880 def get_comments(self, revisions=None):
881 """
881 """
882 Returns comments for this repository grouped by revisions
882 Returns comments for this repository grouped by revisions
883
883
884 :param revisions: filter query by revisions only
884 :param revisions: filter query by revisions only
885 """
885 """
886 cmts = ChangesetComment.query()\
886 cmts = ChangesetComment.query()\
887 .filter(ChangesetComment.repo == self)
887 .filter(ChangesetComment.repo == self)
888 if revisions:
888 if revisions:
889 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
889 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
890 grouped = defaultdict(list)
890 grouped = defaultdict(list)
891 for cmt in cmts.all():
891 for cmt in cmts.all():
892 grouped[cmt.revision].append(cmt)
892 grouped[cmt.revision].append(cmt)
893 return grouped
893 return grouped
894
894
895 def statuses(self, revisions=None):
895 def statuses(self, revisions=None):
896 """
896 """
897 Returns statuses for this repository
897 Returns statuses for this repository
898
898
899 :param revisions: list of revisions to get statuses for
899 :param revisions: list of revisions to get statuses for
900 :type revisions: list
900 :type revisions: list
901 """
901 """
902
902
903 statuses = ChangesetStatus.query()\
903 statuses = ChangesetStatus.query()\
904 .filter(ChangesetStatus.repo == self)\
904 .filter(ChangesetStatus.repo == self)\
905 .filter(ChangesetStatus.version == 0)
905 .filter(ChangesetStatus.version == 0)
906 if revisions:
906 if revisions:
907 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
907 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
908 grouped = {}
908 grouped = {}
909
909
910 #maybe we have open new pullrequest without a status ?
910 #maybe we have open new pullrequest without a status ?
911 stat = ChangesetStatus.STATUS_UNDER_REVIEW
911 stat = ChangesetStatus.STATUS_UNDER_REVIEW
912 status_lbl = ChangesetStatus.get_status_lbl(stat)
912 status_lbl = ChangesetStatus.get_status_lbl(stat)
913 for pr in PullRequest.query().filter(PullRequest.org_repo == self).all():
913 for pr in PullRequest.query().filter(PullRequest.org_repo == self).all():
914 for rev in pr.revisions:
914 for rev in pr.revisions:
915 pr_id = pr.pull_request_id
915 pr_id = pr.pull_request_id
916 pr_repo = pr.other_repo.repo_name
916 pr_repo = pr.other_repo.repo_name
917 grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
917 grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
918
918
919 for stat in statuses.all():
919 for stat in statuses.all():
920 pr_id = pr_repo = None
920 pr_id = pr_repo = None
921 if stat.pull_request:
921 if stat.pull_request:
922 pr_id = stat.pull_request.pull_request_id
922 pr_id = stat.pull_request.pull_request_id
923 pr_repo = stat.pull_request.other_repo.repo_name
923 pr_repo = stat.pull_request.other_repo.repo_name
924 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
924 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
925 pr_id, pr_repo]
925 pr_id, pr_repo]
926 return grouped
926 return grouped
927
927
928 #==========================================================================
928 #==========================================================================
929 # SCM CACHE INSTANCE
929 # SCM CACHE INSTANCE
930 #==========================================================================
930 #==========================================================================
931
931
932 @property
932 @property
933 def invalidate(self):
933 def invalidate(self):
934 return CacheInvalidation.invalidate(self.repo_name)
934 return CacheInvalidation.invalidate(self.repo_name)
935
935
936 def set_invalidate(self):
936 def set_invalidate(self):
937 """
937 """
938 set a cache for invalidation for this instance
938 set a cache for invalidation for this instance
939 """
939 """
940 CacheInvalidation.set_invalidate(repo_name=self.repo_name)
940 CacheInvalidation.set_invalidate(repo_name=self.repo_name)
941
941
942 @LazyProperty
942 @LazyProperty
943 def scm_instance(self):
943 def scm_instance(self):
944 return self.scm_instance_cached()
944 return self.scm_instance_cached()
945 return self.__get_instance()
945 return self.__get_instance()
946
946
947 def scm_instance_cached(self, cache_map=None):
947 def scm_instance_cached(self, cache_map=None):
948 @cache_region('long_term')
948 @cache_region('long_term')
949 def _c(repo_name):
949 def _c(repo_name):
950 return self.__get_instance()
950 return self.__get_instance()
951 rn = self.repo_name
951 rn = self.repo_name
952 log.debug('Getting cached instance of repo')
952 log.debug('Getting cached instance of repo')
953
953
954 if cache_map:
954 if cache_map:
955 # get using prefilled cache_map
955 # get using prefilled cache_map
956 invalidate_repo = cache_map[self.repo_name]
956 invalidate_repo = cache_map[self.repo_name]
957 if invalidate_repo:
957 if invalidate_repo:
958 invalidate_repo = (None if invalidate_repo.cache_active
958 invalidate_repo = (None if invalidate_repo.cache_active
959 else invalidate_repo)
959 else invalidate_repo)
960 else:
960 else:
961 # get from invalidate
961 # get from invalidate
962 invalidate_repo = self.invalidate
962 invalidate_repo = self.invalidate
963
963
964 if invalidate_repo is not None:
964 if invalidate_repo is not None:
965 region_invalidate(_c, None, rn)
965 region_invalidate(_c, None, rn)
966 # update our cache
966 # update our cache
967 CacheInvalidation.set_valid(invalidate_repo.cache_key)
967 CacheInvalidation.set_valid(invalidate_repo.cache_key)
968 return _c(rn)
968 return _c(rn)
969
969
970 def __get_instance(self):
970 def __get_instance(self):
971 repo_full_path = self.repo_full_path
971 repo_full_path = self.repo_full_path
972 try:
972 try:
973 alias = get_scm(repo_full_path)[0]
973 alias = get_scm(repo_full_path)[0]
974 log.debug('Creating instance of %s repository' % alias)
974 log.debug('Creating instance of %s repository' % alias)
975 backend = get_backend(alias)
975 backend = get_backend(alias)
976 except VCSError:
976 except VCSError:
977 log.error(traceback.format_exc())
977 log.error(traceback.format_exc())
978 log.error('Perhaps this repository is in db and not in '
978 log.error('Perhaps this repository is in db and not in '
979 'filesystem run rescan repositories with '
979 'filesystem run rescan repositories with '
980 '"destroy old data " option from admin panel')
980 '"destroy old data " option from admin panel')
981 return
981 return
982
982
983 if alias == 'hg':
983 if alias == 'hg':
984
984
985 repo = backend(safe_str(repo_full_path), create=False,
985 repo = backend(safe_str(repo_full_path), create=False,
986 baseui=self._ui)
986 baseui=self._ui)
987 # skip hidden web repository
987 # skip hidden web repository
988 if repo._get_hidden():
988 if repo._get_hidden():
989 return
989 return
990 else:
990 else:
991 repo = backend(repo_full_path, create=False)
991 repo = backend(repo_full_path, create=False)
992
992
993 return repo
993 return repo
994
994
995
995
996 class RepoGroup(Base, BaseModel):
996 class RepoGroup(Base, BaseModel):
997 __tablename__ = 'groups'
997 __tablename__ = 'groups'
998 __table_args__ = (
998 __table_args__ = (
999 UniqueConstraint('group_name', 'group_parent_id'),
999 UniqueConstraint('group_name', 'group_parent_id'),
1000 CheckConstraint('group_id != group_parent_id'),
1000 CheckConstraint('group_id != group_parent_id'),
1001 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1001 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1002 'mysql_charset': 'utf8'},
1002 'mysql_charset': 'utf8'},
1003 )
1003 )
1004 __mapper_args__ = {'order_by': 'group_name'}
1004 __mapper_args__ = {'order_by': 'group_name'}
1005
1005
1006 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1006 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1007 group_name = Column("group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
1007 group_name = Column("group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
1008 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
1008 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
1009 group_description = Column("group_description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1009 group_description = Column("group_description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1010 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
1010 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
1011
1011
1012 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
1012 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
1013 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
1013 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
1014
1014
1015 parent_group = relationship('RepoGroup', remote_side=group_id)
1015 parent_group = relationship('RepoGroup', remote_side=group_id)
1016
1016
1017 def __init__(self, group_name='', parent_group=None):
1017 def __init__(self, group_name='', parent_group=None):
1018 self.group_name = group_name
1018 self.group_name = group_name
1019 self.parent_group = parent_group
1019 self.parent_group = parent_group
1020
1020
1021 def __unicode__(self):
1021 def __unicode__(self):
1022 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
1022 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
1023 self.group_name)
1023 self.group_name)
1024
1024
1025 @classmethod
1025 @classmethod
1026 def groups_choices(cls, check_perms=False):
1026 def groups_choices(cls, check_perms=False):
1027 from webhelpers.html import literal as _literal
1027 from webhelpers.html import literal as _literal
1028 from rhodecode.model.scm import ScmModel
1028 from rhodecode.model.scm import ScmModel
1029 groups = cls.query().all()
1029 groups = cls.query().all()
1030 if check_perms:
1030 if check_perms:
1031 #filter group user have access to, it's done
1031 #filter group user have access to, it's done
1032 #magically inside ScmModel based on current user
1032 #magically inside ScmModel based on current user
1033 groups = ScmModel().get_repos_groups(groups)
1033 groups = ScmModel().get_repos_groups(groups)
1034 repo_groups = [('', '')]
1034 repo_groups = [('', '')]
1035 sep = ' &raquo; '
1035 sep = ' &raquo; '
1036 _name = lambda k: _literal(sep.join(k))
1036 _name = lambda k: _literal(sep.join(k))
1037
1037
1038 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
1038 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
1039 for x in groups])
1039 for x in groups])
1040
1040
1041 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
1041 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
1042 return repo_groups
1042 return repo_groups
1043
1043
1044 @classmethod
1044 @classmethod
1045 def url_sep(cls):
1045 def url_sep(cls):
1046 return URL_SEP
1046 return URL_SEP
1047
1047
1048 @classmethod
1048 @classmethod
1049 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
1049 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
1050 if case_insensitive:
1050 if case_insensitive:
1051 gr = cls.query()\
1051 gr = cls.query()\
1052 .filter(cls.group_name.ilike(group_name))
1052 .filter(cls.group_name.ilike(group_name))
1053 else:
1053 else:
1054 gr = cls.query()\
1054 gr = cls.query()\
1055 .filter(cls.group_name == group_name)
1055 .filter(cls.group_name == group_name)
1056 if cache:
1056 if cache:
1057 gr = gr.options(FromCache(
1057 gr = gr.options(FromCache(
1058 "sql_cache_short",
1058 "sql_cache_short",
1059 "get_group_%s" % _hash_key(group_name)
1059 "get_group_%s" % _hash_key(group_name)
1060 )
1060 )
1061 )
1061 )
1062 return gr.scalar()
1062 return gr.scalar()
1063
1063
1064 @property
1064 @property
1065 def parents(self):
1065 def parents(self):
1066 parents_recursion_limit = 5
1066 parents_recursion_limit = 5
1067 groups = []
1067 groups = []
1068 if self.parent_group is None:
1068 if self.parent_group is None:
1069 return groups
1069 return groups
1070 cur_gr = self.parent_group
1070 cur_gr = self.parent_group
1071 groups.insert(0, cur_gr)
1071 groups.insert(0, cur_gr)
1072 cnt = 0
1072 cnt = 0
1073 while 1:
1073 while 1:
1074 cnt += 1
1074 cnt += 1
1075 gr = getattr(cur_gr, 'parent_group', None)
1075 gr = getattr(cur_gr, 'parent_group', None)
1076 cur_gr = cur_gr.parent_group
1076 cur_gr = cur_gr.parent_group
1077 if gr is None:
1077 if gr is None:
1078 break
1078 break
1079 if cnt == parents_recursion_limit:
1079 if cnt == parents_recursion_limit:
1080 # this will prevent accidental infinit loops
1080 # this will prevent accidental infinit loops
1081 log.error('group nested more than %s' %
1081 log.error('group nested more than %s' %
1082 parents_recursion_limit)
1082 parents_recursion_limit)
1083 break
1083 break
1084
1084
1085 groups.insert(0, gr)
1085 groups.insert(0, gr)
1086 return groups
1086 return groups
1087
1087
1088 @property
1088 @property
1089 def children(self):
1089 def children(self):
1090 return RepoGroup.query().filter(RepoGroup.parent_group == self)
1090 return RepoGroup.query().filter(RepoGroup.parent_group == self)
1091
1091
1092 @property
1092 @property
1093 def name(self):
1093 def name(self):
1094 return self.group_name.split(RepoGroup.url_sep())[-1]
1094 return self.group_name.split(RepoGroup.url_sep())[-1]
1095
1095
1096 @property
1096 @property
1097 def full_path(self):
1097 def full_path(self):
1098 return self.group_name
1098 return self.group_name
1099
1099
1100 @property
1100 @property
1101 def full_path_splitted(self):
1101 def full_path_splitted(self):
1102 return self.group_name.split(RepoGroup.url_sep())
1102 return self.group_name.split(RepoGroup.url_sep())
1103
1103
1104 @property
1104 @property
1105 def repositories(self):
1105 def repositories(self):
1106 return Repository.query()\
1106 return Repository.query()\
1107 .filter(Repository.group == self)\
1107 .filter(Repository.group == self)\
1108 .order_by(Repository.repo_name)
1108 .order_by(Repository.repo_name)
1109
1109
1110 @property
1110 @property
1111 def repositories_recursive_count(self):
1111 def repositories_recursive_count(self):
1112 cnt = self.repositories.count()
1112 cnt = self.repositories.count()
1113
1113
1114 def children_count(group):
1114 def children_count(group):
1115 cnt = 0
1115 cnt = 0
1116 for child in group.children:
1116 for child in group.children:
1117 cnt += child.repositories.count()
1117 cnt += child.repositories.count()
1118 cnt += children_count(child)
1118 cnt += children_count(child)
1119 return cnt
1119 return cnt
1120
1120
1121 return cnt + children_count(self)
1121 return cnt + children_count(self)
1122
1122
1123 def recursive_groups_and_repos(self):
1123 def recursive_groups_and_repos(self):
1124 """
1124 """
1125 Recursive return all groups, with repositories in those groups
1125 Recursive return all groups, with repositories in those groups
1126 """
1126 """
1127 all_ = []
1127 all_ = []
1128
1128
1129 def _get_members(root_gr):
1129 def _get_members(root_gr):
1130 for r in root_gr.repositories:
1130 for r in root_gr.repositories:
1131 all_.append(r)
1131 all_.append(r)
1132 childs = root_gr.children.all()
1132 childs = root_gr.children.all()
1133 if childs:
1133 if childs:
1134 for gr in childs:
1134 for gr in childs:
1135 all_.append(gr)
1135 all_.append(gr)
1136 _get_members(gr)
1136 _get_members(gr)
1137
1137
1138 _get_members(self)
1138 _get_members(self)
1139 return [self] + all_
1139 return [self] + all_
1140
1140
1141 def get_new_name(self, group_name):
1141 def get_new_name(self, group_name):
1142 """
1142 """
1143 returns new full group name based on parent and new name
1143 returns new full group name based on parent and new name
1144
1144
1145 :param group_name:
1145 :param group_name:
1146 """
1146 """
1147 path_prefix = (self.parent_group.full_path_splitted if
1147 path_prefix = (self.parent_group.full_path_splitted if
1148 self.parent_group else [])
1148 self.parent_group else [])
1149 return RepoGroup.url_sep().join(path_prefix + [group_name])
1149 return RepoGroup.url_sep().join(path_prefix + [group_name])
1150
1150
1151
1151
1152 class Permission(Base, BaseModel):
1152 class Permission(Base, BaseModel):
1153 __tablename__ = 'permissions'
1153 __tablename__ = 'permissions'
1154 __table_args__ = (
1154 __table_args__ = (
1155 Index('p_perm_name_idx', 'permission_name'),
1155 Index('p_perm_name_idx', 'permission_name'),
1156 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1156 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1157 'mysql_charset': 'utf8'},
1157 'mysql_charset': 'utf8'},
1158 )
1158 )
1159 PERMS = [
1159 PERMS = [
1160 ('repository.none', _('Repository no access')),
1160 ('repository.none', _('Repository no access')),
1161 ('repository.read', _('Repository read access')),
1161 ('repository.read', _('Repository read access')),
1162 ('repository.write', _('Repository write access')),
1162 ('repository.write', _('Repository write access')),
1163 ('repository.admin', _('Repository admin access')),
1163 ('repository.admin', _('Repository admin access')),
1164
1164
1165 ('group.none', _('Repositories Group no access')),
1165 ('group.none', _('Repositories Group no access')),
1166 ('group.read', _('Repositories Group read access')),
1166 ('group.read', _('Repositories Group read access')),
1167 ('group.write', _('Repositories Group write access')),
1167 ('group.write', _('Repositories Group write access')),
1168 ('group.admin', _('Repositories Group admin access')),
1168 ('group.admin', _('Repositories Group admin access')),
1169
1169
1170 ('hg.admin', _('RhodeCode Administrator')),
1170 ('hg.admin', _('RhodeCode Administrator')),
1171 ('hg.create.none', _('Repository creation disabled')),
1171 ('hg.create.none', _('Repository creation disabled')),
1172 ('hg.create.repository', _('Repository creation enabled')),
1172 ('hg.create.repository', _('Repository creation enabled')),
1173 ('hg.fork.none', _('Repository forking disabled')),
1173 ('hg.fork.none', _('Repository forking disabled')),
1174 ('hg.fork.repository', _('Repository forking enabled')),
1174 ('hg.fork.repository', _('Repository forking enabled')),
1175 ('hg.register.none', _('Register disabled')),
1175 ('hg.register.none', _('Register disabled')),
1176 ('hg.register.manual_activate', _('Register new user with RhodeCode '
1176 ('hg.register.manual_activate', _('Register new user with RhodeCode '
1177 'with manual activation')),
1177 'with manual activation')),
1178
1178
1179 ('hg.register.auto_activate', _('Register new user with RhodeCode '
1179 ('hg.register.auto_activate', _('Register new user with RhodeCode '
1180 'with auto activation')),
1180 'with auto activation')),
1181 ]
1181 ]
1182
1182
1183 # defines which permissions are more important higher the more important
1183 # defines which permissions are more important higher the more important
1184 PERM_WEIGHTS = {
1184 PERM_WEIGHTS = {
1185 'repository.none': 0,
1185 'repository.none': 0,
1186 'repository.read': 1,
1186 'repository.read': 1,
1187 'repository.write': 3,
1187 'repository.write': 3,
1188 'repository.admin': 4,
1188 'repository.admin': 4,
1189
1189
1190 'group.none': 0,
1190 'group.none': 0,
1191 'group.read': 1,
1191 'group.read': 1,
1192 'group.write': 3,
1192 'group.write': 3,
1193 'group.admin': 4,
1193 'group.admin': 4,
1194
1194
1195 'hg.fork.none': 0,
1195 'hg.fork.none': 0,
1196 'hg.fork.repository': 1,
1196 'hg.fork.repository': 1,
1197 'hg.create.none': 0,
1197 'hg.create.none': 0,
1198 'hg.create.repository':1
1198 'hg.create.repository':1
1199 }
1199 }
1200
1200
1201 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1201 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1202 permission_name = Column("permission_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1202 permission_name = Column("permission_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1203 permission_longname = Column("permission_longname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1203 permission_longname = Column("permission_longname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1204
1204
1205 def __unicode__(self):
1205 def __unicode__(self):
1206 return u"<%s('%s:%s')>" % (
1206 return u"<%s('%s:%s')>" % (
1207 self.__class__.__name__, self.permission_id, self.permission_name
1207 self.__class__.__name__, self.permission_id, self.permission_name
1208 )
1208 )
1209
1209
1210 @classmethod
1210 @classmethod
1211 def get_by_key(cls, key):
1211 def get_by_key(cls, key):
1212 return cls.query().filter(cls.permission_name == key).scalar()
1212 return cls.query().filter(cls.permission_name == key).scalar()
1213
1213
1214 @classmethod
1214 @classmethod
1215 def get_default_perms(cls, default_user_id):
1215 def get_default_perms(cls, default_user_id):
1216 q = Session().query(UserRepoToPerm, Repository, cls)\
1216 q = Session().query(UserRepoToPerm, Repository, cls)\
1217 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
1217 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
1218 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
1218 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
1219 .filter(UserRepoToPerm.user_id == default_user_id)
1219 .filter(UserRepoToPerm.user_id == default_user_id)
1220
1220
1221 return q.all()
1221 return q.all()
1222
1222
1223 @classmethod
1223 @classmethod
1224 def get_default_group_perms(cls, default_user_id):
1224 def get_default_group_perms(cls, default_user_id):
1225 q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
1225 q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
1226 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
1226 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
1227 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
1227 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
1228 .filter(UserRepoGroupToPerm.user_id == default_user_id)
1228 .filter(UserRepoGroupToPerm.user_id == default_user_id)
1229
1229
1230 return q.all()
1230 return q.all()
1231
1231
1232
1232
1233 class UserRepoToPerm(Base, BaseModel):
1233 class UserRepoToPerm(Base, BaseModel):
1234 __tablename__ = 'repo_to_perm'
1234 __tablename__ = 'repo_to_perm'
1235 __table_args__ = (
1235 __table_args__ = (
1236 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
1236 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
1237 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1237 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1238 'mysql_charset': 'utf8'}
1238 'mysql_charset': 'utf8'}
1239 )
1239 )
1240 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1240 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1241 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1241 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1242 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1242 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1243 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1243 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1244
1244
1245 user = relationship('User')
1245 user = relationship('User')
1246 repository = relationship('Repository')
1246 repository = relationship('Repository')
1247 permission = relationship('Permission')
1247 permission = relationship('Permission')
1248
1248
1249 @classmethod
1249 @classmethod
1250 def create(cls, user, repository, permission):
1250 def create(cls, user, repository, permission):
1251 n = cls()
1251 n = cls()
1252 n.user = user
1252 n.user = user
1253 n.repository = repository
1253 n.repository = repository
1254 n.permission = permission
1254 n.permission = permission
1255 Session().add(n)
1255 Session().add(n)
1256 return n
1256 return n
1257
1257
1258 def __unicode__(self):
1258 def __unicode__(self):
1259 return u'<user:%s => %s >' % (self.user, self.repository)
1259 return u'<user:%s => %s >' % (self.user, self.repository)
1260
1260
1261
1261
1262 class UserToPerm(Base, BaseModel):
1262 class UserToPerm(Base, BaseModel):
1263 __tablename__ = 'user_to_perm'
1263 __tablename__ = 'user_to_perm'
1264 __table_args__ = (
1264 __table_args__ = (
1265 UniqueConstraint('user_id', 'permission_id'),
1265 UniqueConstraint('user_id', 'permission_id'),
1266 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1266 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1267 'mysql_charset': 'utf8'}
1267 'mysql_charset': 'utf8'}
1268 )
1268 )
1269 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1269 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1270 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1270 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1271 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1271 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1272
1272
1273 user = relationship('User')
1273 user = relationship('User')
1274 permission = relationship('Permission', lazy='joined')
1274 permission = relationship('Permission', lazy='joined')
1275
1275
1276
1276
1277 class UsersGroupRepoToPerm(Base, BaseModel):
1277 class UsersGroupRepoToPerm(Base, BaseModel):
1278 __tablename__ = 'users_group_repo_to_perm'
1278 __tablename__ = 'users_group_repo_to_perm'
1279 __table_args__ = (
1279 __table_args__ = (
1280 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1280 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1281 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1281 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1282 'mysql_charset': 'utf8'}
1282 'mysql_charset': 'utf8'}
1283 )
1283 )
1284 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1284 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1285 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1285 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1286 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1286 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1287 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1287 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1288
1288
1289 users_group = relationship('UsersGroup')
1289 users_group = relationship('UsersGroup')
1290 permission = relationship('Permission')
1290 permission = relationship('Permission')
1291 repository = relationship('Repository')
1291 repository = relationship('Repository')
1292
1292
1293 @classmethod
1293 @classmethod
1294 def create(cls, users_group, repository, permission):
1294 def create(cls, users_group, repository, permission):
1295 n = cls()
1295 n = cls()
1296 n.users_group = users_group
1296 n.users_group = users_group
1297 n.repository = repository
1297 n.repository = repository
1298 n.permission = permission
1298 n.permission = permission
1299 Session().add(n)
1299 Session().add(n)
1300 return n
1300 return n
1301
1301
1302 def __unicode__(self):
1302 def __unicode__(self):
1303 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
1303 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
1304
1304
1305
1305
1306 class UsersGroupToPerm(Base, BaseModel):
1306 class UsersGroupToPerm(Base, BaseModel):
1307 __tablename__ = 'users_group_to_perm'
1307 __tablename__ = 'users_group_to_perm'
1308 __table_args__ = (
1308 __table_args__ = (
1309 UniqueConstraint('users_group_id', 'permission_id',),
1309 UniqueConstraint('users_group_id', 'permission_id',),
1310 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1310 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1311 'mysql_charset': 'utf8'}
1311 'mysql_charset': 'utf8'}
1312 )
1312 )
1313 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1313 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1314 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1314 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1315 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1315 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1316
1316
1317 users_group = relationship('UsersGroup')
1317 users_group = relationship('UsersGroup')
1318 permission = relationship('Permission')
1318 permission = relationship('Permission')
1319
1319
1320
1320
1321 class UserRepoGroupToPerm(Base, BaseModel):
1321 class UserRepoGroupToPerm(Base, BaseModel):
1322 __tablename__ = 'user_repo_group_to_perm'
1322 __tablename__ = 'user_repo_group_to_perm'
1323 __table_args__ = (
1323 __table_args__ = (
1324 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1324 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1325 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1325 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1326 'mysql_charset': 'utf8'}
1326 'mysql_charset': 'utf8'}
1327 )
1327 )
1328
1328
1329 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1329 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1330 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1330 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1331 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1331 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1332 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1332 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1333
1333
1334 user = relationship('User')
1334 user = relationship('User')
1335 group = relationship('RepoGroup')
1335 group = relationship('RepoGroup')
1336 permission = relationship('Permission')
1336 permission = relationship('Permission')
1337
1337
1338
1338
1339 class UsersGroupRepoGroupToPerm(Base, BaseModel):
1339 class UsersGroupRepoGroupToPerm(Base, BaseModel):
1340 __tablename__ = 'users_group_repo_group_to_perm'
1340 __tablename__ = 'users_group_repo_group_to_perm'
1341 __table_args__ = (
1341 __table_args__ = (
1342 UniqueConstraint('users_group_id', 'group_id'),
1342 UniqueConstraint('users_group_id', 'group_id'),
1343 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1343 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1344 'mysql_charset': 'utf8'}
1344 'mysql_charset': 'utf8'}
1345 )
1345 )
1346
1346
1347 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1347 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1348 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1348 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1349 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1349 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1350 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1350 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1351
1351
1352 users_group = relationship('UsersGroup')
1352 users_group = relationship('UsersGroup')
1353 permission = relationship('Permission')
1353 permission = relationship('Permission')
1354 group = relationship('RepoGroup')
1354 group = relationship('RepoGroup')
1355
1355
1356
1356
1357 class Statistics(Base, BaseModel):
1357 class Statistics(Base, BaseModel):
1358 __tablename__ = 'statistics'
1358 __tablename__ = 'statistics'
1359 __table_args__ = (
1359 __table_args__ = (
1360 UniqueConstraint('repository_id'),
1360 UniqueConstraint('repository_id'),
1361 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1361 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1362 'mysql_charset': 'utf8'}
1362 'mysql_charset': 'utf8'}
1363 )
1363 )
1364 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1364 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1365 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1365 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1366 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1366 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1367 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1367 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1368 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1368 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1369 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1369 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1370
1370
1371 repository = relationship('Repository', single_parent=True)
1371 repository = relationship('Repository', single_parent=True)
1372
1372
1373
1373
1374 class UserFollowing(Base, BaseModel):
1374 class UserFollowing(Base, BaseModel):
1375 __tablename__ = 'user_followings'
1375 __tablename__ = 'user_followings'
1376 __table_args__ = (
1376 __table_args__ = (
1377 UniqueConstraint('user_id', 'follows_repository_id'),
1377 UniqueConstraint('user_id', 'follows_repository_id'),
1378 UniqueConstraint('user_id', 'follows_user_id'),
1378 UniqueConstraint('user_id', 'follows_user_id'),
1379 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1379 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1380 'mysql_charset': 'utf8'}
1380 'mysql_charset': 'utf8'}
1381 )
1381 )
1382
1382
1383 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1383 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1384 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1384 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1385 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1385 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1386 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1386 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1387 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1387 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1388
1388
1389 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1389 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1390
1390
1391 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1391 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1392 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1392 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1393
1393
1394 @classmethod
1394 @classmethod
1395 def get_repo_followers(cls, repo_id):
1395 def get_repo_followers(cls, repo_id):
1396 return cls.query().filter(cls.follows_repo_id == repo_id)
1396 return cls.query().filter(cls.follows_repo_id == repo_id)
1397
1397
1398
1398
1399 class CacheInvalidation(Base, BaseModel):
1399 class CacheInvalidation(Base, BaseModel):
1400 __tablename__ = 'cache_invalidation'
1400 __tablename__ = 'cache_invalidation'
1401 __table_args__ = (
1401 __table_args__ = (
1402 UniqueConstraint('cache_key'),
1402 UniqueConstraint('cache_key'),
1403 Index('key_idx', 'cache_key'),
1403 Index('key_idx', 'cache_key'),
1404 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1404 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1405 'mysql_charset': 'utf8'},
1405 'mysql_charset': 'utf8'},
1406 )
1406 )
1407 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1407 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1408 cache_key = Column("cache_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1408 cache_key = Column("cache_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1409 cache_args = Column("cache_args", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1409 cache_args = Column("cache_args", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1410 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1410 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1411
1411
1412 def __init__(self, cache_key, cache_args=''):
1412 def __init__(self, cache_key, cache_args=''):
1413 self.cache_key = cache_key
1413 self.cache_key = cache_key
1414 self.cache_args = cache_args
1414 self.cache_args = cache_args
1415 self.cache_active = False
1415 self.cache_active = False
1416
1416
1417 def __unicode__(self):
1417 def __unicode__(self):
1418 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1418 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1419 self.cache_id, self.cache_key)
1419 self.cache_id, self.cache_key)
1420
1420
1421 @property
1421 @property
1422 def prefix(self):
1422 def prefix(self):
1423 _split = self.cache_key.split(self.cache_args, 1)
1423 _split = self.cache_key.split(self.cache_args, 1)
1424 if _split and len(_split) == 2:
1424 if _split and len(_split) == 2:
1425 return _split[0]
1425 return _split[0]
1426 return ''
1426 return ''
1427
1427
1428 @classmethod
1428 @classmethod
1429 def clear_cache(cls):
1429 def clear_cache(cls):
1430 cls.query().delete()
1430 cls.query().delete()
1431
1431
1432 @classmethod
1432 @classmethod
1433 def _get_key(cls, key):
1433 def _get_key(cls, key):
1434 """
1434 """
1435 Wrapper for generating a key, together with a prefix
1435 Wrapper for generating a key, together with a prefix
1436
1436
1437 :param key:
1437 :param key:
1438 """
1438 """
1439 import rhodecode
1439 import rhodecode
1440 prefix = ''
1440 prefix = ''
1441 org_key = key
1441 org_key = key
1442 iid = rhodecode.CONFIG.get('instance_id')
1442 iid = rhodecode.CONFIG.get('instance_id')
1443 if iid:
1443 if iid:
1444 prefix = iid
1444 prefix = iid
1445
1445
1446 return "%s%s" % (prefix, key), prefix, org_key
1446 return "%s%s" % (prefix, key), prefix, org_key
1447
1447
1448 @classmethod
1448 @classmethod
1449 def get_by_key(cls, key):
1449 def get_by_key(cls, key):
1450 return cls.query().filter(cls.cache_key == key).scalar()
1450 return cls.query().filter(cls.cache_key == key).scalar()
1451
1451
1452 @classmethod
1452 @classmethod
1453 def get_by_repo_name(cls, repo_name):
1453 def get_by_repo_name(cls, repo_name):
1454 return cls.query().filter(cls.cache_args == repo_name).all()
1454 return cls.query().filter(cls.cache_args == repo_name).all()
1455
1455
1456 @classmethod
1456 @classmethod
1457 def _get_or_create_key(cls, key, repo_name, commit=True):
1457 def _get_or_create_key(cls, key, repo_name, commit=True):
1458 inv_obj = Session().query(cls).filter(cls.cache_key == key).scalar()
1458 inv_obj = Session().query(cls).filter(cls.cache_key == key).scalar()
1459 if not inv_obj:
1459 if not inv_obj:
1460 try:
1460 try:
1461 inv_obj = CacheInvalidation(key, repo_name)
1461 inv_obj = CacheInvalidation(key, repo_name)
1462 Session().add(inv_obj)
1462 Session().add(inv_obj)
1463 if commit:
1463 if commit:
1464 Session().commit()
1464 Session().commit()
1465 except Exception:
1465 except Exception:
1466 log.error(traceback.format_exc())
1466 log.error(traceback.format_exc())
1467 Session().rollback()
1467 Session().rollback()
1468 return inv_obj
1468 return inv_obj
1469
1469
1470 @classmethod
1470 @classmethod
1471 def invalidate(cls, key):
1471 def invalidate(cls, key):
1472 """
1472 """
1473 Returns Invalidation object if this given key should be invalidated
1473 Returns Invalidation object if this given key should be invalidated
1474 None otherwise. `cache_active = False` means that this cache
1474 None otherwise. `cache_active = False` means that this cache
1475 state is not valid and needs to be invalidated
1475 state is not valid and needs to be invalidated
1476
1476
1477 :param key:
1477 :param key:
1478 """
1478 """
1479 repo_name = key
1479 repo_name = key
1480 repo_name = remove_suffix(repo_name, '_README')
1480 repo_name = remove_suffix(repo_name, '_README')
1481 repo_name = remove_suffix(repo_name, '_RSS')
1481 repo_name = remove_suffix(repo_name, '_RSS')
1482 repo_name = remove_suffix(repo_name, '_ATOM')
1482 repo_name = remove_suffix(repo_name, '_ATOM')
1483
1483
1484 # adds instance prefix
1484 # adds instance prefix
1485 key, _prefix, _org_key = cls._get_key(key)
1485 key, _prefix, _org_key = cls._get_key(key)
1486 inv = cls._get_or_create_key(key, repo_name)
1486 inv = cls._get_or_create_key(key, repo_name)
1487
1487
1488 if inv and inv.cache_active is False:
1488 if inv and inv.cache_active is False:
1489 return inv
1489 return inv
1490
1490
1491 @classmethod
1491 @classmethod
1492 def set_invalidate(cls, key=None, repo_name=None):
1492 def set_invalidate(cls, key=None, repo_name=None):
1493 """
1493 """
1494 Mark this Cache key for invalidation, either by key or whole
1494 Mark this Cache key for invalidation, either by key or whole
1495 cache sets based on repo_name
1495 cache sets based on repo_name
1496
1496
1497 :param key:
1497 :param key:
1498 """
1498 """
1499 if key:
1499 if key:
1500 key, _prefix, _org_key = cls._get_key(key)
1500 key, _prefix, _org_key = cls._get_key(key)
1501 inv_objs = Session().query(cls).filter(cls.cache_key == key).all()
1501 inv_objs = Session().query(cls).filter(cls.cache_key == key).all()
1502 elif repo_name:
1502 elif repo_name:
1503 inv_objs = Session().query(cls).filter(cls.cache_args == repo_name).all()
1503 inv_objs = Session().query(cls).filter(cls.cache_args == repo_name).all()
1504
1504
1505 log.debug('marking %s key[s] for invalidation based on key=%s,repo_name=%s'
1505 log.debug('marking %s key[s] for invalidation based on key=%s,repo_name=%s'
1506 % (len(inv_objs), key, repo_name))
1506 % (len(inv_objs), key, repo_name))
1507 try:
1507 try:
1508 for inv_obj in inv_objs:
1508 for inv_obj in inv_objs:
1509 print inv_obj
1510 inv_obj.cache_active = False
1509 inv_obj.cache_active = False
1511 Session().add(inv_obj)
1510 Session().add(inv_obj)
1512 Session().commit()
1511 Session().commit()
1513 except Exception:
1512 except Exception:
1514 log.error(traceback.format_exc())
1513 log.error(traceback.format_exc())
1515 Session().rollback()
1514 Session().rollback()
1516
1515
1517 @classmethod
1516 @classmethod
1518 def set_valid(cls, key):
1517 def set_valid(cls, key):
1519 """
1518 """
1520 Mark this cache key as active and currently cached
1519 Mark this cache key as active and currently cached
1521
1520
1522 :param key:
1521 :param key:
1523 """
1522 """
1524 inv_obj = cls.get_by_key(key)
1523 inv_obj = cls.get_by_key(key)
1525 inv_obj.cache_active = True
1524 inv_obj.cache_active = True
1526 Session().add(inv_obj)
1525 Session().add(inv_obj)
1527 Session().commit()
1526 Session().commit()
1528
1527
1529 @classmethod
1528 @classmethod
1530 def get_cache_map(cls):
1529 def get_cache_map(cls):
1531
1530
1532 class cachemapdict(dict):
1531 class cachemapdict(dict):
1533
1532
1534 def __init__(self, *args, **kwargs):
1533 def __init__(self, *args, **kwargs):
1535 fixkey = kwargs.get('fixkey')
1534 fixkey = kwargs.get('fixkey')
1536 if fixkey:
1535 if fixkey:
1537 del kwargs['fixkey']
1536 del kwargs['fixkey']
1538 self.fixkey = fixkey
1537 self.fixkey = fixkey
1539 super(cachemapdict, self).__init__(*args, **kwargs)
1538 super(cachemapdict, self).__init__(*args, **kwargs)
1540
1539
1541 def __getattr__(self, name):
1540 def __getattr__(self, name):
1542 key = name
1541 key = name
1543 if self.fixkey:
1542 if self.fixkey:
1544 key, _prefix, _org_key = cls._get_key(key)
1543 key, _prefix, _org_key = cls._get_key(key)
1545 if key in self.__dict__:
1544 if key in self.__dict__:
1546 return self.__dict__[key]
1545 return self.__dict__[key]
1547 else:
1546 else:
1548 return self[key]
1547 return self[key]
1549
1548
1550 def __getitem__(self, key):
1549 def __getitem__(self, key):
1551 if self.fixkey:
1550 if self.fixkey:
1552 key, _prefix, _org_key = cls._get_key(key)
1551 key, _prefix, _org_key = cls._get_key(key)
1553 try:
1552 try:
1554 return super(cachemapdict, self).__getitem__(key)
1553 return super(cachemapdict, self).__getitem__(key)
1555 except KeyError:
1554 except KeyError:
1556 return
1555 return
1557
1556
1558 cache_map = cachemapdict(fixkey=True)
1557 cache_map = cachemapdict(fixkey=True)
1559 for obj in cls.query().all():
1558 for obj in cls.query().all():
1560 cache_map[obj.cache_key] = cachemapdict(obj.get_dict())
1559 cache_map[obj.cache_key] = cachemapdict(obj.get_dict())
1561 return cache_map
1560 return cache_map
1562
1561
1563
1562
1564 class ChangesetComment(Base, BaseModel):
1563 class ChangesetComment(Base, BaseModel):
1565 __tablename__ = 'changeset_comments'
1564 __tablename__ = 'changeset_comments'
1566 __table_args__ = (
1565 __table_args__ = (
1567 Index('cc_revision_idx', 'revision'),
1566 Index('cc_revision_idx', 'revision'),
1568 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1567 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1569 'mysql_charset': 'utf8'},
1568 'mysql_charset': 'utf8'},
1570 )
1569 )
1571 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1570 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1572 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1571 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1573 revision = Column('revision', String(40), nullable=True)
1572 revision = Column('revision', String(40), nullable=True)
1574 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1573 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1575 line_no = Column('line_no', Unicode(10), nullable=True)
1574 line_no = Column('line_no', Unicode(10), nullable=True)
1576 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
1575 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
1577 f_path = Column('f_path', Unicode(1000), nullable=True)
1576 f_path = Column('f_path', Unicode(1000), nullable=True)
1578 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1577 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1579 text = Column('text', UnicodeText(25000), nullable=False)
1578 text = Column('text', UnicodeText(25000), nullable=False)
1580 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1579 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1581 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1580 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1582
1581
1583 author = relationship('User', lazy='joined')
1582 author = relationship('User', lazy='joined')
1584 repo = relationship('Repository')
1583 repo = relationship('Repository')
1585 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1584 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1586 pull_request = relationship('PullRequest', lazy='joined')
1585 pull_request = relationship('PullRequest', lazy='joined')
1587
1586
1588 @classmethod
1587 @classmethod
1589 def get_users(cls, revision=None, pull_request_id=None):
1588 def get_users(cls, revision=None, pull_request_id=None):
1590 """
1589 """
1591 Returns user associated with this ChangesetComment. ie those
1590 Returns user associated with this ChangesetComment. ie those
1592 who actually commented
1591 who actually commented
1593
1592
1594 :param cls:
1593 :param cls:
1595 :param revision:
1594 :param revision:
1596 """
1595 """
1597 q = Session().query(User)\
1596 q = Session().query(User)\
1598 .join(ChangesetComment.author)
1597 .join(ChangesetComment.author)
1599 if revision:
1598 if revision:
1600 q = q.filter(cls.revision == revision)
1599 q = q.filter(cls.revision == revision)
1601 elif pull_request_id:
1600 elif pull_request_id:
1602 q = q.filter(cls.pull_request_id == pull_request_id)
1601 q = q.filter(cls.pull_request_id == pull_request_id)
1603 return q.all()
1602 return q.all()
1604
1603
1605
1604
1606 class ChangesetStatus(Base, BaseModel):
1605 class ChangesetStatus(Base, BaseModel):
1607 __tablename__ = 'changeset_statuses'
1606 __tablename__ = 'changeset_statuses'
1608 __table_args__ = (
1607 __table_args__ = (
1609 Index('cs_revision_idx', 'revision'),
1608 Index('cs_revision_idx', 'revision'),
1610 Index('cs_version_idx', 'version'),
1609 Index('cs_version_idx', 'version'),
1611 UniqueConstraint('repo_id', 'revision', 'version'),
1610 UniqueConstraint('repo_id', 'revision', 'version'),
1612 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1611 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1613 'mysql_charset': 'utf8'}
1612 'mysql_charset': 'utf8'}
1614 )
1613 )
1615 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1614 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1616 STATUS_APPROVED = 'approved'
1615 STATUS_APPROVED = 'approved'
1617 STATUS_REJECTED = 'rejected'
1616 STATUS_REJECTED = 'rejected'
1618 STATUS_UNDER_REVIEW = 'under_review'
1617 STATUS_UNDER_REVIEW = 'under_review'
1619
1618
1620 STATUSES = [
1619 STATUSES = [
1621 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1620 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1622 (STATUS_APPROVED, _("Approved")),
1621 (STATUS_APPROVED, _("Approved")),
1623 (STATUS_REJECTED, _("Rejected")),
1622 (STATUS_REJECTED, _("Rejected")),
1624 (STATUS_UNDER_REVIEW, _("Under Review")),
1623 (STATUS_UNDER_REVIEW, _("Under Review")),
1625 ]
1624 ]
1626
1625
1627 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1626 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1628 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1627 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1629 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1628 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1630 revision = Column('revision', String(40), nullable=False)
1629 revision = Column('revision', String(40), nullable=False)
1631 status = Column('status', String(128), nullable=False, default=DEFAULT)
1630 status = Column('status', String(128), nullable=False, default=DEFAULT)
1632 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1631 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1633 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1632 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1634 version = Column('version', Integer(), nullable=False, default=0)
1633 version = Column('version', Integer(), nullable=False, default=0)
1635 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1634 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1636
1635
1637 author = relationship('User', lazy='joined')
1636 author = relationship('User', lazy='joined')
1638 repo = relationship('Repository')
1637 repo = relationship('Repository')
1639 comment = relationship('ChangesetComment', lazy='joined')
1638 comment = relationship('ChangesetComment', lazy='joined')
1640 pull_request = relationship('PullRequest', lazy='joined')
1639 pull_request = relationship('PullRequest', lazy='joined')
1641
1640
1642 def __unicode__(self):
1641 def __unicode__(self):
1643 return u"<%s('%s:%s')>" % (
1642 return u"<%s('%s:%s')>" % (
1644 self.__class__.__name__,
1643 self.__class__.__name__,
1645 self.status, self.author
1644 self.status, self.author
1646 )
1645 )
1647
1646
1648 @classmethod
1647 @classmethod
1649 def get_status_lbl(cls, value):
1648 def get_status_lbl(cls, value):
1650 return dict(cls.STATUSES).get(value)
1649 return dict(cls.STATUSES).get(value)
1651
1650
1652 @property
1651 @property
1653 def status_lbl(self):
1652 def status_lbl(self):
1654 return ChangesetStatus.get_status_lbl(self.status)
1653 return ChangesetStatus.get_status_lbl(self.status)
1655
1654
1656
1655
1657 class PullRequest(Base, BaseModel):
1656 class PullRequest(Base, BaseModel):
1658 __tablename__ = 'pull_requests'
1657 __tablename__ = 'pull_requests'
1659 __table_args__ = (
1658 __table_args__ = (
1660 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1659 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1661 'mysql_charset': 'utf8'},
1660 'mysql_charset': 'utf8'},
1662 )
1661 )
1663
1662
1664 STATUS_NEW = u'new'
1663 STATUS_NEW = u'new'
1665 STATUS_OPEN = u'open'
1664 STATUS_OPEN = u'open'
1666 STATUS_CLOSED = u'closed'
1665 STATUS_CLOSED = u'closed'
1667
1666
1668 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1667 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1669 title = Column('title', Unicode(256), nullable=True)
1668 title = Column('title', Unicode(256), nullable=True)
1670 description = Column('description', UnicodeText(10240), nullable=True)
1669 description = Column('description', UnicodeText(10240), nullable=True)
1671 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1670 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1672 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1671 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1673 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1672 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1674 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1673 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1675 _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
1674 _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
1676 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1675 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1677 org_ref = Column('org_ref', Unicode(256), nullable=False)
1676 org_ref = Column('org_ref', Unicode(256), nullable=False)
1678 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1677 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1679 other_ref = Column('other_ref', Unicode(256), nullable=False)
1678 other_ref = Column('other_ref', Unicode(256), nullable=False)
1680
1679
1681 @hybrid_property
1680 @hybrid_property
1682 def revisions(self):
1681 def revisions(self):
1683 return self._revisions.split(':')
1682 return self._revisions.split(':')
1684
1683
1685 @revisions.setter
1684 @revisions.setter
1686 def revisions(self, val):
1685 def revisions(self, val):
1687 self._revisions = ':'.join(val)
1686 self._revisions = ':'.join(val)
1688
1687
1689 author = relationship('User', lazy='joined')
1688 author = relationship('User', lazy='joined')
1690 reviewers = relationship('PullRequestReviewers',
1689 reviewers = relationship('PullRequestReviewers',
1691 cascade="all, delete, delete-orphan")
1690 cascade="all, delete, delete-orphan")
1692 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1691 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1693 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1692 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1694 statuses = relationship('ChangesetStatus')
1693 statuses = relationship('ChangesetStatus')
1695 comments = relationship('ChangesetComment',
1694 comments = relationship('ChangesetComment',
1696 cascade="all, delete, delete-orphan")
1695 cascade="all, delete, delete-orphan")
1697
1696
1698 def is_closed(self):
1697 def is_closed(self):
1699 return self.status == self.STATUS_CLOSED
1698 return self.status == self.STATUS_CLOSED
1700
1699
1701 def __json__(self):
1700 def __json__(self):
1702 return dict(
1701 return dict(
1703 revisions=self.revisions
1702 revisions=self.revisions
1704 )
1703 )
1705
1704
1706
1705
1707 class PullRequestReviewers(Base, BaseModel):
1706 class PullRequestReviewers(Base, BaseModel):
1708 __tablename__ = 'pull_request_reviewers'
1707 __tablename__ = 'pull_request_reviewers'
1709 __table_args__ = (
1708 __table_args__ = (
1710 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1709 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1711 'mysql_charset': 'utf8'},
1710 'mysql_charset': 'utf8'},
1712 )
1711 )
1713
1712
1714 def __init__(self, user=None, pull_request=None):
1713 def __init__(self, user=None, pull_request=None):
1715 self.user = user
1714 self.user = user
1716 self.pull_request = pull_request
1715 self.pull_request = pull_request
1717
1716
1718 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1717 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1719 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1718 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1720 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1719 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1721
1720
1722 user = relationship('User')
1721 user = relationship('User')
1723 pull_request = relationship('PullRequest')
1722 pull_request = relationship('PullRequest')
1724
1723
1725
1724
1726 class Notification(Base, BaseModel):
1725 class Notification(Base, BaseModel):
1727 __tablename__ = 'notifications'
1726 __tablename__ = 'notifications'
1728 __table_args__ = (
1727 __table_args__ = (
1729 Index('notification_type_idx', 'type'),
1728 Index('notification_type_idx', 'type'),
1730 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1729 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1731 'mysql_charset': 'utf8'},
1730 'mysql_charset': 'utf8'},
1732 )
1731 )
1733
1732
1734 TYPE_CHANGESET_COMMENT = u'cs_comment'
1733 TYPE_CHANGESET_COMMENT = u'cs_comment'
1735 TYPE_MESSAGE = u'message'
1734 TYPE_MESSAGE = u'message'
1736 TYPE_MENTION = u'mention'
1735 TYPE_MENTION = u'mention'
1737 TYPE_REGISTRATION = u'registration'
1736 TYPE_REGISTRATION = u'registration'
1738 TYPE_PULL_REQUEST = u'pull_request'
1737 TYPE_PULL_REQUEST = u'pull_request'
1739 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1738 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1740
1739
1741 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1740 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1742 subject = Column('subject', Unicode(512), nullable=True)
1741 subject = Column('subject', Unicode(512), nullable=True)
1743 body = Column('body', UnicodeText(50000), nullable=True)
1742 body = Column('body', UnicodeText(50000), nullable=True)
1744 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1743 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1745 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1744 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1746 type_ = Column('type', Unicode(256))
1745 type_ = Column('type', Unicode(256))
1747
1746
1748 created_by_user = relationship('User')
1747 created_by_user = relationship('User')
1749 notifications_to_users = relationship('UserNotification', lazy='joined',
1748 notifications_to_users = relationship('UserNotification', lazy='joined',
1750 cascade="all, delete, delete-orphan")
1749 cascade="all, delete, delete-orphan")
1751
1750
1752 @property
1751 @property
1753 def recipients(self):
1752 def recipients(self):
1754 return [x.user for x in UserNotification.query()\
1753 return [x.user for x in UserNotification.query()\
1755 .filter(UserNotification.notification == self)\
1754 .filter(UserNotification.notification == self)\
1756 .order_by(UserNotification.user_id.asc()).all()]
1755 .order_by(UserNotification.user_id.asc()).all()]
1757
1756
1758 @classmethod
1757 @classmethod
1759 def create(cls, created_by, subject, body, recipients, type_=None):
1758 def create(cls, created_by, subject, body, recipients, type_=None):
1760 if type_ is None:
1759 if type_ is None:
1761 type_ = Notification.TYPE_MESSAGE
1760 type_ = Notification.TYPE_MESSAGE
1762
1761
1763 notification = cls()
1762 notification = cls()
1764 notification.created_by_user = created_by
1763 notification.created_by_user = created_by
1765 notification.subject = subject
1764 notification.subject = subject
1766 notification.body = body
1765 notification.body = body
1767 notification.type_ = type_
1766 notification.type_ = type_
1768 notification.created_on = datetime.datetime.now()
1767 notification.created_on = datetime.datetime.now()
1769
1768
1770 for u in recipients:
1769 for u in recipients:
1771 assoc = UserNotification()
1770 assoc = UserNotification()
1772 assoc.notification = notification
1771 assoc.notification = notification
1773 u.notifications.append(assoc)
1772 u.notifications.append(assoc)
1774 Session().add(notification)
1773 Session().add(notification)
1775 return notification
1774 return notification
1776
1775
1777 @property
1776 @property
1778 def description(self):
1777 def description(self):
1779 from rhodecode.model.notification import NotificationModel
1778 from rhodecode.model.notification import NotificationModel
1780 return NotificationModel().make_description(self)
1779 return NotificationModel().make_description(self)
1781
1780
1782
1781
1783 class UserNotification(Base, BaseModel):
1782 class UserNotification(Base, BaseModel):
1784 __tablename__ = 'user_to_notification'
1783 __tablename__ = 'user_to_notification'
1785 __table_args__ = (
1784 __table_args__ = (
1786 UniqueConstraint('user_id', 'notification_id'),
1785 UniqueConstraint('user_id', 'notification_id'),
1787 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1786 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1788 'mysql_charset': 'utf8'}
1787 'mysql_charset': 'utf8'}
1789 )
1788 )
1790 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1789 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1791 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1790 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1792 read = Column('read', Boolean, default=False)
1791 read = Column('read', Boolean, default=False)
1793 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1792 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1794
1793
1795 user = relationship('User', lazy="joined")
1794 user = relationship('User', lazy="joined")
1796 notification = relationship('Notification', lazy="joined",
1795 notification = relationship('Notification', lazy="joined",
1797 order_by=lambda: Notification.created_on.desc(),)
1796 order_by=lambda: Notification.created_on.desc(),)
1798
1797
1799 def mark_as_read(self):
1798 def mark_as_read(self):
1800 self.read = True
1799 self.read = True
1801 Session().add(self)
1800 Session().add(self)
1802
1801
1803
1802
1804 class DbMigrateVersion(Base, BaseModel):
1803 class DbMigrateVersion(Base, BaseModel):
1805 __tablename__ = 'db_migrate_version'
1804 __tablename__ = 'db_migrate_version'
1806 __table_args__ = (
1805 __table_args__ = (
1807 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1806 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1808 'mysql_charset': 'utf8'},
1807 'mysql_charset': 'utf8'},
1809 )
1808 )
1810 repository_id = Column('repository_id', String(250), primary_key=True)
1809 repository_id = Column('repository_id', String(250), primary_key=True)
1811 repository_path = Column('repository_path', Text)
1810 repository_path = Column('repository_path', Text)
1812 version = Column('version', Integer)
1811 version = Column('version', Integer)
@@ -1,88 +1,88 b''
1 from rhodecode.tests import *
1 from rhodecode.tests import *
2 from rhodecode.model.db import RhodeCodeSetting
2 from rhodecode.model.db import RhodeCodeSetting
3 from nose.plugins.skip import SkipTest
3 from nose.plugins.skip import SkipTest
4
4
5 skip_ldap_test = False
5 skip_ldap_test = False
6 try:
6 try:
7 import ldap
7 import ldap
8 except ImportError:
8 except ImportError:
9 # means that python-ldap is not installed
9 # means that python-ldap is not installed
10 skip_ldap_test = True
10 skip_ldap_test = True
11 pass
11 pass
12
12
13
13 class TestLdapSettingsController(TestController):
14 class TestLdapSettingsController(TestController):
14
15
15 def test_index(self):
16 def test_index(self):
16 self.log_user()
17 self.log_user()
17 response = self.app.get(url(controller='admin/ldap_settings',
18 response = self.app.get(url(controller='admin/ldap_settings',
18 action='index'))
19 action='index'))
19 self.assertTrue('LDAP administration' in response.body)
20 self.assertTrue('LDAP administration' in response.body)
20
21
21 def test_ldap_save_settings(self):
22 def test_ldap_save_settings(self):
22 self.log_user()
23 self.log_user()
23 if skip_ldap_test:
24 if skip_ldap_test:
24 raise SkipTest('skipping due to missing ldap lib')
25 raise SkipTest('skipping due to missing ldap lib')
25
26
26 test_url = url(controller='admin/ldap_settings',
27 test_url = url(controller='admin/ldap_settings',
27 action='ldap_settings')
28 action='ldap_settings')
28
29
29 response = self.app.post(url=test_url,
30 response = self.app.post(url=test_url,
30 params={'ldap_host' : u'dc.example.com',
31 params={'ldap_host' : u'dc.example.com',
31 'ldap_port' : '999',
32 'ldap_port' : '999',
32 'ldap_tls_kind' : 'PLAIN',
33 'ldap_tls_kind' : 'PLAIN',
33 'ldap_tls_reqcert' : 'NEVER',
34 'ldap_tls_reqcert' : 'NEVER',
34 'ldap_dn_user':'test_user',
35 'ldap_dn_user':'test_user',
35 'ldap_dn_pass':'test_pass',
36 'ldap_dn_pass':'test_pass',
36 'ldap_base_dn':'test_base_dn',
37 'ldap_base_dn':'test_base_dn',
37 'ldap_filter':'test_filter',
38 'ldap_filter':'test_filter',
38 'ldap_search_scope':'BASE',
39 'ldap_search_scope':'BASE',
39 'ldap_attr_login':'test_attr_login',
40 'ldap_attr_login':'test_attr_login',
40 'ldap_attr_firstname':'ima',
41 'ldap_attr_firstname':'ima',
41 'ldap_attr_lastname':'tester',
42 'ldap_attr_lastname':'tester',
42 'ldap_attr_email':'test@example.com' })
43 'ldap_attr_email':'test@example.com' })
43
44
44 new_settings = RhodeCodeSetting.get_ldap_settings()
45 new_settings = RhodeCodeSetting.get_ldap_settings()
45 print new_settings
46 self.assertEqual(new_settings['ldap_host'], u'dc.example.com',
46 self.assertEqual(new_settings['ldap_host'], u'dc.example.com',
47 'fail db write compare')
47 'fail db write compare')
48
48
49 self.checkSessionFlash(response,
49 self.checkSessionFlash(response,
50 'Ldap settings updated successfully')
50 'Ldap settings updated successfully')
51
51
52 def test_ldap_error_form(self):
52 def test_ldap_error_form(self):
53 self.log_user()
53 self.log_user()
54 if skip_ldap_test:
54 if skip_ldap_test:
55 raise SkipTest('skipping due to missing ldap lib')
55 raise SkipTest('skipping due to missing ldap lib')
56
56
57 test_url = url(controller='admin/ldap_settings',
57 test_url = url(controller='admin/ldap_settings',
58 action='ldap_settings')
58 action='ldap_settings')
59
59
60 response = self.app.post(url=test_url,
60 response = self.app.post(url=test_url,
61 params={'ldap_host' : '',
61 params={'ldap_host' : '',
62 'ldap_port' : 'i-should-be-number',
62 'ldap_port' : 'i-should-be-number',
63 'ldap_tls_kind' : 'PLAIN',
63 'ldap_tls_kind' : 'PLAIN',
64 'ldap_tls_reqcert' : 'NEVER',
64 'ldap_tls_reqcert' : 'NEVER',
65 'ldap_dn_user':'',
65 'ldap_dn_user':'',
66 'ldap_dn_pass':'',
66 'ldap_dn_pass':'',
67 'ldap_base_dn':'',
67 'ldap_base_dn':'',
68 'ldap_filter':'',
68 'ldap_filter':'',
69 'ldap_search_scope':'BASE',
69 'ldap_search_scope':'BASE',
70 'ldap_attr_login':'', # <----- missing required input
70 'ldap_attr_login':'', # <----- missing required input
71 'ldap_attr_firstname':'',
71 'ldap_attr_firstname':'',
72 'ldap_attr_lastname':'',
72 'ldap_attr_lastname':'',
73 'ldap_attr_email':'' })
73 'ldap_attr_email':'' })
74
74
75 self.assertTrue("""<span class="error-message">The LDAP Login"""
75 self.assertTrue("""<span class="error-message">The LDAP Login"""
76 """ attribute of the CN must be specified""" in
76 """ attribute of the CN must be specified""" in
77 response.body)
77 response.body)
78
78
79
79
80
80
81 self.assertTrue("""<span class="error-message">Please """
81 self.assertTrue("""<span class="error-message">Please """
82 """enter a number</span>""" in response.body)
82 """enter a number</span>""" in response.body)
83
83
84 def test_ldap_login(self):
84 def test_ldap_login(self):
85 pass
85 pass
86
86
87 def test_ldap_login_incorrect(self):
87 def test_ldap_login_incorrect(self):
88 pass
88 pass
General Comments 0
You need to be logged in to leave comments. Login now