##// END OF EJS Templates
filter out repo groups choices to only ones that you have write+ access to. Before it was read+ access and you got proper...
marcink -
r3239:a9565b8b beta
parent child Browse files
Show More
@@ -1,490 +1,492 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.repos
3 rhodecode.controllers.admin.repos
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Repositories controller for RhodeCode
6 Repositories controller for RhodeCode
7
7
8 :created_on: Apr 7, 2010
8 :created_on: Apr 7, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import logging
26 import logging
27 import traceback
27 import traceback
28 import formencode
28 import formencode
29 from formencode import htmlfill
29 from formencode import htmlfill
30
30
31 from webob.exc import HTTPInternalServerError
31 from webob.exc import HTTPInternalServerError
32 from pylons import request, session, tmpl_context as c, url
32 from pylons import request, session, tmpl_context as c, url
33 from pylons.controllers.util import redirect
33 from pylons.controllers.util import redirect
34 from pylons.i18n.translation import _
34 from pylons.i18n.translation import _
35 from sqlalchemy.exc import IntegrityError
35 from sqlalchemy.exc import IntegrityError
36
36
37 import rhodecode
37 import rhodecode
38 from rhodecode.lib import helpers as h
38 from rhodecode.lib import helpers as h
39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
40 HasPermissionAnyDecorator, HasRepoPermissionAllDecorator
40 HasPermissionAnyDecorator, HasRepoPermissionAllDecorator
41 from rhodecode.lib.base import BaseController, render
41 from rhodecode.lib.base import BaseController, render
42 from rhodecode.lib.utils import invalidate_cache, action_logger, repo_name_slug
42 from rhodecode.lib.utils import invalidate_cache, action_logger, repo_name_slug
43 from rhodecode.lib.helpers import get_token
43 from rhodecode.lib.helpers import get_token
44 from rhodecode.model.meta import Session
44 from rhodecode.model.meta import Session
45 from rhodecode.model.db import User, Repository, UserFollowing, RepoGroup,\
45 from rhodecode.model.db import User, Repository, UserFollowing, RepoGroup,\
46 RhodeCodeSetting
46 RhodeCodeSetting
47 from rhodecode.model.forms import RepoForm
47 from rhodecode.model.forms import RepoForm
48 from rhodecode.model.scm import ScmModel
48 from rhodecode.model.scm import ScmModel, GroupList
49 from rhodecode.model.repo import RepoModel
49 from rhodecode.model.repo import RepoModel
50 from rhodecode.lib.compat import json
50 from rhodecode.lib.compat import json
51 from sqlalchemy.sql.expression import func
51 from sqlalchemy.sql.expression import func
52
52
53 log = logging.getLogger(__name__)
53 log = logging.getLogger(__name__)
54
54
55
55
56 class ReposController(BaseController):
56 class ReposController(BaseController):
57 """
57 """
58 REST Controller styled on the Atom Publishing Protocol"""
58 REST Controller styled on the Atom Publishing Protocol"""
59 # To properly map this controller, ensure your config/routing.py
59 # To properly map this controller, ensure your config/routing.py
60 # file has a resource setup:
60 # file has a resource setup:
61 # map.resource('repo', 'repos')
61 # map.resource('repo', 'repos')
62
62
63 @LoginRequired()
63 @LoginRequired()
64 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
64 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
65 def __before__(self):
65 def __before__(self):
66 c.admin_user = session.get('admin_user')
66 c.admin_user = session.get('admin_user')
67 c.admin_username = session.get('admin_username')
67 c.admin_username = session.get('admin_username')
68 super(ReposController, self).__before__()
68 super(ReposController, self).__before__()
69
69
70 def __load_defaults(self):
70 def __load_defaults(self):
71 c.repo_groups = RepoGroup.groups_choices(check_perms=True)
71 acl_groups = GroupList(RepoGroup.query().all(),
72 perm_set=['group.write', 'group.admin'])
73 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
72 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
74 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
73
75
74 repo_model = RepoModel()
76 repo_model = RepoModel()
75 c.users_array = repo_model.get_users_js()
77 c.users_array = repo_model.get_users_js()
76 c.users_groups_array = repo_model.get_users_groups_js()
78 c.users_groups_array = repo_model.get_users_groups_js()
77 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
79 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
78 c.landing_revs_choices = choices
80 c.landing_revs_choices = choices
79
81
80 def __load_data(self, repo_name=None):
82 def __load_data(self, repo_name=None):
81 """
83 """
82 Load defaults settings for edit, and update
84 Load defaults settings for edit, and update
83
85
84 :param repo_name:
86 :param repo_name:
85 """
87 """
86 self.__load_defaults()
88 self.__load_defaults()
87
89
88 c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
90 c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
89 repo = db_repo.scm_instance
91 repo = db_repo.scm_instance
90
92
91 if c.repo_info is None:
93 if c.repo_info is None:
92 h.not_mapped_error(repo_name)
94 h.not_mapped_error(repo_name)
93 return redirect(url('repos'))
95 return redirect(url('repos'))
94
96
95 ##override defaults for exact repo info here git/hg etc
97 ##override defaults for exact repo info here git/hg etc
96 choices, c.landing_revs = ScmModel().get_repo_landing_revs(c.repo_info)
98 choices, c.landing_revs = ScmModel().get_repo_landing_revs(c.repo_info)
97 c.landing_revs_choices = choices
99 c.landing_revs_choices = choices
98
100
99 c.default_user_id = User.get_by_username('default').user_id
101 c.default_user_id = User.get_by_username('default').user_id
100 c.in_public_journal = UserFollowing.query()\
102 c.in_public_journal = UserFollowing.query()\
101 .filter(UserFollowing.user_id == c.default_user_id)\
103 .filter(UserFollowing.user_id == c.default_user_id)\
102 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
104 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
103
105
104 if c.repo_info.stats:
106 if c.repo_info.stats:
105 # this is on what revision we ended up so we add +1 for count
107 # this is on what revision we ended up so we add +1 for count
106 last_rev = c.repo_info.stats.stat_on_revision + 1
108 last_rev = c.repo_info.stats.stat_on_revision + 1
107 else:
109 else:
108 last_rev = 0
110 last_rev = 0
109 c.stats_revision = last_rev
111 c.stats_revision = last_rev
110
112
111 c.repo_last_rev = repo.count() if repo.revisions else 0
113 c.repo_last_rev = repo.count() if repo.revisions else 0
112
114
113 if last_rev == 0 or c.repo_last_rev == 0:
115 if last_rev == 0 or c.repo_last_rev == 0:
114 c.stats_percentage = 0
116 c.stats_percentage = 0
115 else:
117 else:
116 c.stats_percentage = '%.2f' % ((float((last_rev)) /
118 c.stats_percentage = '%.2f' % ((float((last_rev)) /
117 c.repo_last_rev) * 100)
119 c.repo_last_rev) * 100)
118
120
119 defaults = RepoModel()._get_defaults(repo_name)
121 defaults = RepoModel()._get_defaults(repo_name)
120
122
121 c.repos_list = [('', _('--REMOVE FORK--'))]
123 c.repos_list = [('', _('--REMOVE FORK--'))]
122 c.repos_list += [(x.repo_id, x.repo_name) for x in
124 c.repos_list += [(x.repo_id, x.repo_name) for x in
123 Repository.query().order_by(Repository.repo_name).all()
125 Repository.query().order_by(Repository.repo_name).all()
124 if x.repo_id != c.repo_info.repo_id]
126 if x.repo_id != c.repo_info.repo_id]
125
127
126 defaults['id_fork_of'] = db_repo.fork.repo_id if db_repo.fork else ''
128 defaults['id_fork_of'] = db_repo.fork.repo_id if db_repo.fork else ''
127 return defaults
129 return defaults
128
130
129 @HasPermissionAllDecorator('hg.admin')
131 @HasPermissionAllDecorator('hg.admin')
130 def index(self, format='html'):
132 def index(self, format='html'):
131 """GET /repos: All items in the collection"""
133 """GET /repos: All items in the collection"""
132 # url('repos')
134 # url('repos')
133
135
134 c.repos_list = Repository.query()\
136 c.repos_list = Repository.query()\
135 .order_by(func.lower(Repository.repo_name))\
137 .order_by(func.lower(Repository.repo_name))\
136 .all()
138 .all()
137
139
138 repos_data = RepoModel().get_repos_as_dict(repos_list=c.repos_list,
140 repos_data = RepoModel().get_repos_as_dict(repos_list=c.repos_list,
139 admin=True)
141 admin=True)
140 #json used to render the grid
142 #json used to render the grid
141 c.data = json.dumps(repos_data)
143 c.data = json.dumps(repos_data)
142
144
143 return render('admin/repos/repos.html')
145 return render('admin/repos/repos.html')
144
146
145 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
147 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
146 def create(self):
148 def create(self):
147 """
149 """
148 POST /repos: Create a new item"""
150 POST /repos: Create a new item"""
149 # url('repos')
151 # url('repos')
150
152
151 self.__load_defaults()
153 self.__load_defaults()
152 form_result = {}
154 form_result = {}
153 try:
155 try:
154 form_result = RepoForm(repo_groups=c.repo_groups_choices,
156 form_result = RepoForm(repo_groups=c.repo_groups_choices,
155 landing_revs=c.landing_revs_choices)()\
157 landing_revs=c.landing_revs_choices)()\
156 .to_python(dict(request.POST))
158 .to_python(dict(request.POST))
157 new_repo = RepoModel().create(form_result,
159 new_repo = RepoModel().create(form_result,
158 self.rhodecode_user.user_id)
160 self.rhodecode_user.user_id)
159 if form_result['clone_uri']:
161 if form_result['clone_uri']:
160 h.flash(_('created repository %s from %s') \
162 h.flash(_('created repository %s from %s') \
161 % (form_result['repo_name'], form_result['clone_uri']),
163 % (form_result['repo_name'], form_result['clone_uri']),
162 category='success')
164 category='success')
163 else:
165 else:
164 h.flash(_('created repository %s') % form_result['repo_name'],
166 h.flash(_('created repository %s') % form_result['repo_name'],
165 category='success')
167 category='success')
166
168
167 if request.POST.get('user_created'):
169 if request.POST.get('user_created'):
168 # created by regular non admin user
170 # created by regular non admin user
169 action_logger(self.rhodecode_user, 'user_created_repo',
171 action_logger(self.rhodecode_user, 'user_created_repo',
170 form_result['repo_name_full'], self.ip_addr,
172 form_result['repo_name_full'], self.ip_addr,
171 self.sa)
173 self.sa)
172 else:
174 else:
173 action_logger(self.rhodecode_user, 'admin_created_repo',
175 action_logger(self.rhodecode_user, 'admin_created_repo',
174 form_result['repo_name_full'], self.ip_addr,
176 form_result['repo_name_full'], self.ip_addr,
175 self.sa)
177 self.sa)
176 Session().commit()
178 Session().commit()
177 except formencode.Invalid, errors:
179 except formencode.Invalid, errors:
178
180
179 c.new_repo = errors.value['repo_name']
181 c.new_repo = errors.value['repo_name']
180
182
181 if request.POST.get('user_created'):
183 if request.POST.get('user_created'):
182 r = render('admin/repos/repo_add_create_repository.html')
184 r = render('admin/repos/repo_add_create_repository.html')
183 else:
185 else:
184 r = render('admin/repos/repo_add.html')
186 r = render('admin/repos/repo_add.html')
185
187
186 return htmlfill.render(
188 return htmlfill.render(
187 r,
189 r,
188 defaults=errors.value,
190 defaults=errors.value,
189 errors=errors.error_dict or {},
191 errors=errors.error_dict or {},
190 prefix_error=False,
192 prefix_error=False,
191 encoding="UTF-8")
193 encoding="UTF-8")
192
194
193 except Exception:
195 except Exception:
194 log.error(traceback.format_exc())
196 log.error(traceback.format_exc())
195 msg = _('error occurred during creation of repository %s') \
197 msg = _('error occurred during creation of repository %s') \
196 % form_result.get('repo_name')
198 % form_result.get('repo_name')
197 h.flash(msg, category='error')
199 h.flash(msg, category='error')
198 return redirect(url('repos'))
200 return redirect(url('repos'))
199 #redirect to our new repo !
201 #redirect to our new repo !
200 return redirect(url('summary_home', repo_name=new_repo.repo_name))
202 return redirect(url('summary_home', repo_name=new_repo.repo_name))
201
203
202 @HasPermissionAllDecorator('hg.admin')
204 @HasPermissionAllDecorator('hg.admin')
203 def new(self, format='html'):
205 def new(self, format='html'):
204 """
206 """
205 WARNING: this function is depracated see settings.create_repo !!
207 WARNING: this function is depracated see settings.create_repo !!
206
208
207 GET /repos/new: Form to create a new item
209 GET /repos/new: Form to create a new item
208 """
210 """
209
211
210 new_repo = request.GET.get('repo', '')
212 new_repo = request.GET.get('repo', '')
211 parent_group = request.GET.get('parent_group')
213 parent_group = request.GET.get('parent_group')
212
214
213 c.new_repo = repo_name_slug(new_repo)
215 c.new_repo = repo_name_slug(new_repo)
214 self.__load_defaults()
216 self.__load_defaults()
215 ## apply the defaults from defaults page
217 ## apply the defaults from defaults page
216 defaults = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
218 defaults = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
217 if parent_group:
219 if parent_group:
218 defaults.update({'repo_group': parent_group})
220 defaults.update({'repo_group': parent_group})
219
221
220 return htmlfill.render(
222 return htmlfill.render(
221 render('admin/repos/repo_add.html'),
223 render('admin/repos/repo_add.html'),
222 defaults=defaults,
224 defaults=defaults,
223 errors={},
225 errors={},
224 prefix_error=False,
226 prefix_error=False,
225 encoding="UTF-8"
227 encoding="UTF-8"
226 )
228 )
227
229
228 @HasPermissionAllDecorator('hg.admin')
230 @HasPermissionAllDecorator('hg.admin')
229 def update(self, repo_name):
231 def update(self, repo_name):
230 """
232 """
231 PUT /repos/repo_name: Update an existing item"""
233 PUT /repos/repo_name: Update an existing item"""
232 # Forms posted to this method should contain a hidden field:
234 # Forms posted to this method should contain a hidden field:
233 # <input type="hidden" name="_method" value="PUT" />
235 # <input type="hidden" name="_method" value="PUT" />
234 # Or using helpers:
236 # Or using helpers:
235 # h.form(url('repo', repo_name=ID),
237 # h.form(url('repo', repo_name=ID),
236 # method='put')
238 # method='put')
237 # url('repo', repo_name=ID)
239 # url('repo', repo_name=ID)
238 self.__load_defaults()
240 self.__load_defaults()
239 repo_model = RepoModel()
241 repo_model = RepoModel()
240 changed_name = repo_name
242 changed_name = repo_name
241 #override the choices with extracted revisions !
243 #override the choices with extracted revisions !
242 choices, c.landing_revs = ScmModel().get_repo_landing_revs(repo_name)
244 choices, c.landing_revs = ScmModel().get_repo_landing_revs(repo_name)
243 c.landing_revs_choices = choices
245 c.landing_revs_choices = choices
244
246
245 _form = RepoForm(edit=True, old_data={'repo_name': repo_name},
247 _form = RepoForm(edit=True, old_data={'repo_name': repo_name},
246 repo_groups=c.repo_groups_choices,
248 repo_groups=c.repo_groups_choices,
247 landing_revs=c.landing_revs_choices)()
249 landing_revs=c.landing_revs_choices)()
248 try:
250 try:
249 form_result = _form.to_python(dict(request.POST))
251 form_result = _form.to_python(dict(request.POST))
250 repo = repo_model.update(repo_name, **form_result)
252 repo = repo_model.update(repo_name, **form_result)
251 invalidate_cache('get_repo_cached_%s' % repo_name)
253 invalidate_cache('get_repo_cached_%s' % repo_name)
252 h.flash(_('Repository %s updated successfully') % repo_name,
254 h.flash(_('Repository %s updated successfully') % repo_name,
253 category='success')
255 category='success')
254 changed_name = repo.repo_name
256 changed_name = repo.repo_name
255 action_logger(self.rhodecode_user, 'admin_updated_repo',
257 action_logger(self.rhodecode_user, 'admin_updated_repo',
256 changed_name, self.ip_addr, self.sa)
258 changed_name, self.ip_addr, self.sa)
257 Session().commit()
259 Session().commit()
258 except formencode.Invalid, errors:
260 except formencode.Invalid, errors:
259 defaults = self.__load_data(repo_name)
261 defaults = self.__load_data(repo_name)
260 defaults.update(errors.value)
262 defaults.update(errors.value)
261 return htmlfill.render(
263 return htmlfill.render(
262 render('admin/repos/repo_edit.html'),
264 render('admin/repos/repo_edit.html'),
263 defaults=defaults,
265 defaults=defaults,
264 errors=errors.error_dict or {},
266 errors=errors.error_dict or {},
265 prefix_error=False,
267 prefix_error=False,
266 encoding="UTF-8")
268 encoding="UTF-8")
267
269
268 except Exception:
270 except Exception:
269 log.error(traceback.format_exc())
271 log.error(traceback.format_exc())
270 h.flash(_('error occurred during update of repository %s') \
272 h.flash(_('error occurred during update of repository %s') \
271 % repo_name, category='error')
273 % repo_name, category='error')
272 return redirect(url('edit_repo', repo_name=changed_name))
274 return redirect(url('edit_repo', repo_name=changed_name))
273
275
274 @HasPermissionAllDecorator('hg.admin')
276 @HasPermissionAllDecorator('hg.admin')
275 def delete(self, repo_name):
277 def delete(self, repo_name):
276 """
278 """
277 DELETE /repos/repo_name: Delete an existing item"""
279 DELETE /repos/repo_name: Delete an existing item"""
278 # Forms posted to this method should contain a hidden field:
280 # Forms posted to this method should contain a hidden field:
279 # <input type="hidden" name="_method" value="DELETE" />
281 # <input type="hidden" name="_method" value="DELETE" />
280 # Or using helpers:
282 # Or using helpers:
281 # h.form(url('repo', repo_name=ID),
283 # h.form(url('repo', repo_name=ID),
282 # method='delete')
284 # method='delete')
283 # url('repo', repo_name=ID)
285 # url('repo', repo_name=ID)
284
286
285 repo_model = RepoModel()
287 repo_model = RepoModel()
286 repo = repo_model.get_by_repo_name(repo_name)
288 repo = repo_model.get_by_repo_name(repo_name)
287 if not repo:
289 if not repo:
288 h.not_mapped_error(repo_name)
290 h.not_mapped_error(repo_name)
289 return redirect(url('repos'))
291 return redirect(url('repos'))
290 try:
292 try:
291 action_logger(self.rhodecode_user, 'admin_deleted_repo',
293 action_logger(self.rhodecode_user, 'admin_deleted_repo',
292 repo_name, self.ip_addr, self.sa)
294 repo_name, self.ip_addr, self.sa)
293 repo_model.delete(repo)
295 repo_model.delete(repo)
294 invalidate_cache('get_repo_cached_%s' % repo_name)
296 invalidate_cache('get_repo_cached_%s' % repo_name)
295 h.flash(_('deleted repository %s') % repo_name, category='success')
297 h.flash(_('deleted repository %s') % repo_name, category='success')
296 Session().commit()
298 Session().commit()
297 except IntegrityError, e:
299 except IntegrityError, e:
298 if e.message.find('repositories_fork_id_fkey') != -1:
300 if e.message.find('repositories_fork_id_fkey') != -1:
299 log.error(traceback.format_exc())
301 log.error(traceback.format_exc())
300 h.flash(_('Cannot delete %s it still contains attached '
302 h.flash(_('Cannot delete %s it still contains attached '
301 'forks') % repo_name,
303 'forks') % repo_name,
302 category='warning')
304 category='warning')
303 else:
305 else:
304 log.error(traceback.format_exc())
306 log.error(traceback.format_exc())
305 h.flash(_('An error occurred during '
307 h.flash(_('An error occurred during '
306 'deletion of %s') % repo_name,
308 'deletion of %s') % repo_name,
307 category='error')
309 category='error')
308
310
309 except Exception, e:
311 except Exception, e:
310 log.error(traceback.format_exc())
312 log.error(traceback.format_exc())
311 h.flash(_('An error occurred during deletion of %s') % repo_name,
313 h.flash(_('An error occurred during deletion of %s') % repo_name,
312 category='error')
314 category='error')
313
315
314 return redirect(url('repos'))
316 return redirect(url('repos'))
315
317
316 @HasRepoPermissionAllDecorator('repository.admin')
318 @HasRepoPermissionAllDecorator('repository.admin')
317 def delete_perm_user(self, repo_name):
319 def delete_perm_user(self, repo_name):
318 """
320 """
319 DELETE an existing repository permission user
321 DELETE an existing repository permission user
320
322
321 :param repo_name:
323 :param repo_name:
322 """
324 """
323 try:
325 try:
324 RepoModel().revoke_user_permission(repo=repo_name,
326 RepoModel().revoke_user_permission(repo=repo_name,
325 user=request.POST['user_id'])
327 user=request.POST['user_id'])
326 Session().commit()
328 Session().commit()
327 except Exception:
329 except Exception:
328 log.error(traceback.format_exc())
330 log.error(traceback.format_exc())
329 h.flash(_('An error occurred during deletion of repository user'),
331 h.flash(_('An error occurred during deletion of repository user'),
330 category='error')
332 category='error')
331 raise HTTPInternalServerError()
333 raise HTTPInternalServerError()
332
334
333 @HasRepoPermissionAllDecorator('repository.admin')
335 @HasRepoPermissionAllDecorator('repository.admin')
334 def delete_perm_users_group(self, repo_name):
336 def delete_perm_users_group(self, repo_name):
335 """
337 """
336 DELETE an existing repository permission users group
338 DELETE an existing repository permission users group
337
339
338 :param repo_name:
340 :param repo_name:
339 """
341 """
340
342
341 try:
343 try:
342 RepoModel().revoke_users_group_permission(
344 RepoModel().revoke_users_group_permission(
343 repo=repo_name, group_name=request.POST['users_group_id']
345 repo=repo_name, group_name=request.POST['users_group_id']
344 )
346 )
345 Session().commit()
347 Session().commit()
346 except Exception:
348 except Exception:
347 log.error(traceback.format_exc())
349 log.error(traceback.format_exc())
348 h.flash(_('An error occurred during deletion of repository'
350 h.flash(_('An error occurred during deletion of repository'
349 ' users groups'),
351 ' users groups'),
350 category='error')
352 category='error')
351 raise HTTPInternalServerError()
353 raise HTTPInternalServerError()
352
354
353 @HasPermissionAllDecorator('hg.admin')
355 @HasPermissionAllDecorator('hg.admin')
354 def repo_stats(self, repo_name):
356 def repo_stats(self, repo_name):
355 """
357 """
356 DELETE an existing repository statistics
358 DELETE an existing repository statistics
357
359
358 :param repo_name:
360 :param repo_name:
359 """
361 """
360
362
361 try:
363 try:
362 RepoModel().delete_stats(repo_name)
364 RepoModel().delete_stats(repo_name)
363 Session().commit()
365 Session().commit()
364 except Exception, e:
366 except Exception, e:
365 log.error(traceback.format_exc())
367 log.error(traceback.format_exc())
366 h.flash(_('An error occurred during deletion of repository stats'),
368 h.flash(_('An error occurred during deletion of repository stats'),
367 category='error')
369 category='error')
368 return redirect(url('edit_repo', repo_name=repo_name))
370 return redirect(url('edit_repo', repo_name=repo_name))
369
371
370 @HasPermissionAllDecorator('hg.admin')
372 @HasPermissionAllDecorator('hg.admin')
371 def repo_cache(self, repo_name):
373 def repo_cache(self, repo_name):
372 """
374 """
373 INVALIDATE existing repository cache
375 INVALIDATE existing repository cache
374
376
375 :param repo_name:
377 :param repo_name:
376 """
378 """
377
379
378 try:
380 try:
379 ScmModel().mark_for_invalidation(repo_name)
381 ScmModel().mark_for_invalidation(repo_name)
380 Session().commit()
382 Session().commit()
381 except Exception, e:
383 except Exception, e:
382 log.error(traceback.format_exc())
384 log.error(traceback.format_exc())
383 h.flash(_('An error occurred during cache invalidation'),
385 h.flash(_('An error occurred during cache invalidation'),
384 category='error')
386 category='error')
385 return redirect(url('edit_repo', repo_name=repo_name))
387 return redirect(url('edit_repo', repo_name=repo_name))
386
388
387 @HasPermissionAllDecorator('hg.admin')
389 @HasPermissionAllDecorator('hg.admin')
388 def repo_locking(self, repo_name):
390 def repo_locking(self, repo_name):
389 """
391 """
390 Unlock repository when it is locked !
392 Unlock repository when it is locked !
391
393
392 :param repo_name:
394 :param repo_name:
393 """
395 """
394
396
395 try:
397 try:
396 repo = Repository.get_by_repo_name(repo_name)
398 repo = Repository.get_by_repo_name(repo_name)
397 if request.POST.get('set_lock'):
399 if request.POST.get('set_lock'):
398 Repository.lock(repo, c.rhodecode_user.user_id)
400 Repository.lock(repo, c.rhodecode_user.user_id)
399 elif request.POST.get('set_unlock'):
401 elif request.POST.get('set_unlock'):
400 Repository.unlock(repo)
402 Repository.unlock(repo)
401 except Exception, e:
403 except Exception, e:
402 log.error(traceback.format_exc())
404 log.error(traceback.format_exc())
403 h.flash(_('An error occurred during unlocking'),
405 h.flash(_('An error occurred during unlocking'),
404 category='error')
406 category='error')
405 return redirect(url('edit_repo', repo_name=repo_name))
407 return redirect(url('edit_repo', repo_name=repo_name))
406
408
407 @HasPermissionAllDecorator('hg.admin')
409 @HasPermissionAllDecorator('hg.admin')
408 def repo_public_journal(self, repo_name):
410 def repo_public_journal(self, repo_name):
409 """
411 """
410 Set's this repository to be visible in public journal,
412 Set's this repository to be visible in public journal,
411 in other words assing default user to follow this repo
413 in other words assing default user to follow this repo
412
414
413 :param repo_name:
415 :param repo_name:
414 """
416 """
415
417
416 cur_token = request.POST.get('auth_token')
418 cur_token = request.POST.get('auth_token')
417 token = get_token()
419 token = get_token()
418 if cur_token == token:
420 if cur_token == token:
419 try:
421 try:
420 repo_id = Repository.get_by_repo_name(repo_name).repo_id
422 repo_id = Repository.get_by_repo_name(repo_name).repo_id
421 user_id = User.get_by_username('default').user_id
423 user_id = User.get_by_username('default').user_id
422 self.scm_model.toggle_following_repo(repo_id, user_id)
424 self.scm_model.toggle_following_repo(repo_id, user_id)
423 h.flash(_('Updated repository visibility in public journal'),
425 h.flash(_('Updated repository visibility in public journal'),
424 category='success')
426 category='success')
425 Session().commit()
427 Session().commit()
426 except:
428 except:
427 h.flash(_('An error occurred during setting this'
429 h.flash(_('An error occurred during setting this'
428 ' repository in public journal'),
430 ' repository in public journal'),
429 category='error')
431 category='error')
430
432
431 else:
433 else:
432 h.flash(_('Token mismatch'), category='error')
434 h.flash(_('Token mismatch'), category='error')
433 return redirect(url('edit_repo', repo_name=repo_name))
435 return redirect(url('edit_repo', repo_name=repo_name))
434
436
435 @HasPermissionAllDecorator('hg.admin')
437 @HasPermissionAllDecorator('hg.admin')
436 def repo_pull(self, repo_name):
438 def repo_pull(self, repo_name):
437 """
439 """
438 Runs task to update given repository with remote changes,
440 Runs task to update given repository with remote changes,
439 ie. make pull on remote location
441 ie. make pull on remote location
440
442
441 :param repo_name:
443 :param repo_name:
442 """
444 """
443 try:
445 try:
444 ScmModel().pull_changes(repo_name, self.rhodecode_user.username)
446 ScmModel().pull_changes(repo_name, self.rhodecode_user.username)
445 h.flash(_('Pulled from remote location'), category='success')
447 h.flash(_('Pulled from remote location'), category='success')
446 except Exception, e:
448 except Exception, e:
447 h.flash(_('An error occurred during pull from remote location'),
449 h.flash(_('An error occurred during pull from remote location'),
448 category='error')
450 category='error')
449
451
450 return redirect(url('edit_repo', repo_name=repo_name))
452 return redirect(url('edit_repo', repo_name=repo_name))
451
453
452 @HasPermissionAllDecorator('hg.admin')
454 @HasPermissionAllDecorator('hg.admin')
453 def repo_as_fork(self, repo_name):
455 def repo_as_fork(self, repo_name):
454 """
456 """
455 Mark given repository as a fork of another
457 Mark given repository as a fork of another
456
458
457 :param repo_name:
459 :param repo_name:
458 """
460 """
459 try:
461 try:
460 fork_id = request.POST.get('id_fork_of')
462 fork_id = request.POST.get('id_fork_of')
461 repo = ScmModel().mark_as_fork(repo_name, fork_id,
463 repo = ScmModel().mark_as_fork(repo_name, fork_id,
462 self.rhodecode_user.username)
464 self.rhodecode_user.username)
463 fork = repo.fork.repo_name if repo.fork else _('Nothing')
465 fork = repo.fork.repo_name if repo.fork else _('Nothing')
464 Session().commit()
466 Session().commit()
465 h.flash(_('Marked repo %s as fork of %s') % (repo_name, fork),
467 h.flash(_('Marked repo %s as fork of %s') % (repo_name, fork),
466 category='success')
468 category='success')
467 except Exception, e:
469 except Exception, e:
468 log.error(traceback.format_exc())
470 log.error(traceback.format_exc())
469 h.flash(_('An error occurred during this operation'),
471 h.flash(_('An error occurred during this operation'),
470 category='error')
472 category='error')
471
473
472 return redirect(url('edit_repo', repo_name=repo_name))
474 return redirect(url('edit_repo', repo_name=repo_name))
473
475
474 @HasPermissionAllDecorator('hg.admin')
476 @HasPermissionAllDecorator('hg.admin')
475 def show(self, repo_name, format='html'):
477 def show(self, repo_name, format='html'):
476 """GET /repos/repo_name: Show a specific item"""
478 """GET /repos/repo_name: Show a specific item"""
477 # url('repo', repo_name=ID)
479 # url('repo', repo_name=ID)
478
480
479 @HasPermissionAllDecorator('hg.admin')
481 @HasPermissionAllDecorator('hg.admin')
480 def edit(self, repo_name, format='html'):
482 def edit(self, repo_name, format='html'):
481 """GET /repos/repo_name/edit: Form to edit an existing item"""
483 """GET /repos/repo_name/edit: Form to edit an existing item"""
482 # url('edit_repo', repo_name=ID)
484 # url('edit_repo', repo_name=ID)
483 defaults = self.__load_data(repo_name)
485 defaults = self.__load_data(repo_name)
484
486
485 return htmlfill.render(
487 return htmlfill.render(
486 render('admin/repos/repo_edit.html'),
488 render('admin/repos/repo_edit.html'),
487 defaults=defaults,
489 defaults=defaults,
488 encoding="UTF-8",
490 encoding="UTF-8",
489 force_defaults=False
491 force_defaults=False
490 )
492 )
@@ -1,529 +1,531 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.settings
3 rhodecode.controllers.admin.settings
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 settings controller for rhodecode admin
6 settings controller for rhodecode admin
7
7
8 :created_on: Jul 14, 2010
8 :created_on: Jul 14, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import logging
26 import logging
27 import traceback
27 import traceback
28 import formencode
28 import formencode
29 import pkg_resources
29 import pkg_resources
30 import platform
30 import platform
31
31
32 from sqlalchemy import func
32 from sqlalchemy import func
33 from formencode import htmlfill
33 from formencode import htmlfill
34 from pylons import request, session, tmpl_context as c, url, config
34 from pylons import request, session, tmpl_context as c, url, config
35 from pylons.controllers.util import abort, redirect
35 from pylons.controllers.util import abort, redirect
36 from pylons.i18n.translation import _
36 from pylons.i18n.translation import _
37
37
38 from rhodecode.lib import helpers as h
38 from rhodecode.lib import helpers as h
39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
40 HasPermissionAnyDecorator, NotAnonymous
40 HasPermissionAnyDecorator, NotAnonymous
41 from rhodecode.lib.base import BaseController, render
41 from rhodecode.lib.base import BaseController, render
42 from rhodecode.lib.celerylib import tasks, run_task
42 from rhodecode.lib.celerylib import tasks, run_task
43 from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
43 from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
44 set_rhodecode_config, repo_name_slug, check_git_version
44 set_rhodecode_config, repo_name_slug, check_git_version
45 from rhodecode.model.db import RhodeCodeUi, Repository, RepoGroup, \
45 from rhodecode.model.db import RhodeCodeUi, Repository, RepoGroup, \
46 RhodeCodeSetting, PullRequest, PullRequestReviewers
46 RhodeCodeSetting, PullRequest, PullRequestReviewers
47 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
47 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
48 ApplicationUiSettingsForm, ApplicationVisualisationForm
48 ApplicationUiSettingsForm, ApplicationVisualisationForm
49 from rhodecode.model.scm import ScmModel
49 from rhodecode.model.scm import ScmModel, GroupList
50 from rhodecode.model.user import UserModel
50 from rhodecode.model.user import UserModel
51 from rhodecode.model.repo import RepoModel
51 from rhodecode.model.repo import RepoModel
52 from rhodecode.model.db import User
52 from rhodecode.model.db import User
53 from rhodecode.model.notification import EmailNotificationModel
53 from rhodecode.model.notification import EmailNotificationModel
54 from rhodecode.model.meta import Session
54 from rhodecode.model.meta import Session
55 from rhodecode.lib.utils2 import str2bool, safe_unicode
55 from rhodecode.lib.utils2 import str2bool, safe_unicode
56 from rhodecode.lib.compat import json
56 from rhodecode.lib.compat import json
57 log = logging.getLogger(__name__)
57 log = logging.getLogger(__name__)
58
58
59
59
60 class SettingsController(BaseController):
60 class SettingsController(BaseController):
61 """REST Controller styled on the Atom Publishing Protocol"""
61 """REST Controller styled on the Atom Publishing Protocol"""
62 # To properly map this controller, ensure your config/routing.py
62 # To properly map this controller, ensure your config/routing.py
63 # file has a resource setup:
63 # file has a resource setup:
64 # map.resource('setting', 'settings', controller='admin/settings',
64 # map.resource('setting', 'settings', controller='admin/settings',
65 # path_prefix='/admin', name_prefix='admin_')
65 # path_prefix='/admin', name_prefix='admin_')
66
66
67 @LoginRequired()
67 @LoginRequired()
68 def __before__(self):
68 def __before__(self):
69 c.admin_user = session.get('admin_user')
69 c.admin_user = session.get('admin_user')
70 c.admin_username = session.get('admin_username')
70 c.admin_username = session.get('admin_username')
71 c.modules = sorted([(p.project_name, p.version)
71 c.modules = sorted([(p.project_name, p.version)
72 for p in pkg_resources.working_set]
72 for p in pkg_resources.working_set]
73 + [('git', check_git_version())],
73 + [('git', check_git_version())],
74 key=lambda k: k[0].lower())
74 key=lambda k: k[0].lower())
75 c.py_version = platform.python_version()
75 c.py_version = platform.python_version()
76 c.platform = platform.platform()
76 c.platform = platform.platform()
77 super(SettingsController, self).__before__()
77 super(SettingsController, self).__before__()
78
78
79 @HasPermissionAllDecorator('hg.admin')
79 @HasPermissionAllDecorator('hg.admin')
80 def index(self, format='html'):
80 def index(self, format='html'):
81 """GET /admin/settings: All items in the collection"""
81 """GET /admin/settings: All items in the collection"""
82 # url('admin_settings')
82 # url('admin_settings')
83
83
84 defaults = RhodeCodeSetting.get_app_settings()
84 defaults = RhodeCodeSetting.get_app_settings()
85 defaults.update(self._get_hg_ui_settings())
85 defaults.update(self._get_hg_ui_settings())
86
86
87 return htmlfill.render(
87 return htmlfill.render(
88 render('admin/settings/settings.html'),
88 render('admin/settings/settings.html'),
89 defaults=defaults,
89 defaults=defaults,
90 encoding="UTF-8",
90 encoding="UTF-8",
91 force_defaults=False
91 force_defaults=False
92 )
92 )
93
93
94 @HasPermissionAllDecorator('hg.admin')
94 @HasPermissionAllDecorator('hg.admin')
95 def create(self):
95 def create(self):
96 """POST /admin/settings: Create a new item"""
96 """POST /admin/settings: Create a new item"""
97 # url('admin_settings')
97 # url('admin_settings')
98
98
99 @HasPermissionAllDecorator('hg.admin')
99 @HasPermissionAllDecorator('hg.admin')
100 def new(self, format='html'):
100 def new(self, format='html'):
101 """GET /admin/settings/new: Form to create a new item"""
101 """GET /admin/settings/new: Form to create a new item"""
102 # url('admin_new_setting')
102 # url('admin_new_setting')
103
103
104 @HasPermissionAllDecorator('hg.admin')
104 @HasPermissionAllDecorator('hg.admin')
105 def update(self, setting_id):
105 def update(self, setting_id):
106 """PUT /admin/settings/setting_id: Update an existing item"""
106 """PUT /admin/settings/setting_id: Update an existing item"""
107 # Forms posted to this method should contain a hidden field:
107 # Forms posted to this method should contain a hidden field:
108 # <input type="hidden" name="_method" value="PUT" />
108 # <input type="hidden" name="_method" value="PUT" />
109 # Or using helpers:
109 # Or using helpers:
110 # h.form(url('admin_setting', setting_id=ID),
110 # h.form(url('admin_setting', setting_id=ID),
111 # method='put')
111 # method='put')
112 # url('admin_setting', setting_id=ID)
112 # url('admin_setting', setting_id=ID)
113
113
114 if setting_id == 'mapping':
114 if setting_id == 'mapping':
115 rm_obsolete = request.POST.get('destroy', False)
115 rm_obsolete = request.POST.get('destroy', False)
116 log.debug('Rescanning directories with destroy=%s' % rm_obsolete)
116 log.debug('Rescanning directories with destroy=%s' % rm_obsolete)
117 initial = ScmModel().repo_scan()
117 initial = ScmModel().repo_scan()
118 log.debug('invalidating all repositories')
118 log.debug('invalidating all repositories')
119 for repo_name in initial.keys():
119 for repo_name in initial.keys():
120 invalidate_cache('get_repo_cached_%s' % repo_name)
120 invalidate_cache('get_repo_cached_%s' % repo_name)
121
121
122 added, removed = repo2db_mapper(initial, rm_obsolete)
122 added, removed = repo2db_mapper(initial, rm_obsolete)
123 _repr = lambda l: ', '.join(map(safe_unicode, l)) or '-'
123 _repr = lambda l: ', '.join(map(safe_unicode, l)) or '-'
124 h.flash(_('Repositories successfully '
124 h.flash(_('Repositories successfully '
125 'rescanned added: %s ; removed: %s') %
125 'rescanned added: %s ; removed: %s') %
126 (_repr(added), _repr(removed)),
126 (_repr(added), _repr(removed)),
127 category='success')
127 category='success')
128
128
129 if setting_id == 'whoosh':
129 if setting_id == 'whoosh':
130 repo_location = self._get_hg_ui_settings()['paths_root_path']
130 repo_location = self._get_hg_ui_settings()['paths_root_path']
131 full_index = request.POST.get('full_index', False)
131 full_index = request.POST.get('full_index', False)
132 run_task(tasks.whoosh_index, repo_location, full_index)
132 run_task(tasks.whoosh_index, repo_location, full_index)
133 h.flash(_('Whoosh reindex task scheduled'), category='success')
133 h.flash(_('Whoosh reindex task scheduled'), category='success')
134
134
135 if setting_id == 'global':
135 if setting_id == 'global':
136
136
137 application_form = ApplicationSettingsForm()()
137 application_form = ApplicationSettingsForm()()
138 try:
138 try:
139 form_result = application_form.to_python(dict(request.POST))
139 form_result = application_form.to_python(dict(request.POST))
140 except formencode.Invalid, errors:
140 except formencode.Invalid, errors:
141 return htmlfill.render(
141 return htmlfill.render(
142 render('admin/settings/settings.html'),
142 render('admin/settings/settings.html'),
143 defaults=errors.value,
143 defaults=errors.value,
144 errors=errors.error_dict or {},
144 errors=errors.error_dict or {},
145 prefix_error=False,
145 prefix_error=False,
146 encoding="UTF-8"
146 encoding="UTF-8"
147 )
147 )
148
148
149 try:
149 try:
150 sett1 = RhodeCodeSetting.get_by_name_or_create('title')
150 sett1 = RhodeCodeSetting.get_by_name_or_create('title')
151 sett1.app_settings_value = form_result['rhodecode_title']
151 sett1.app_settings_value = form_result['rhodecode_title']
152 Session().add(sett1)
152 Session().add(sett1)
153
153
154 sett2 = RhodeCodeSetting.get_by_name_or_create('realm')
154 sett2 = RhodeCodeSetting.get_by_name_or_create('realm')
155 sett2.app_settings_value = form_result['rhodecode_realm']
155 sett2.app_settings_value = form_result['rhodecode_realm']
156 Session().add(sett2)
156 Session().add(sett2)
157
157
158 sett3 = RhodeCodeSetting.get_by_name_or_create('ga_code')
158 sett3 = RhodeCodeSetting.get_by_name_or_create('ga_code')
159 sett3.app_settings_value = form_result['rhodecode_ga_code']
159 sett3.app_settings_value = form_result['rhodecode_ga_code']
160 Session().add(sett3)
160 Session().add(sett3)
161
161
162 Session().commit()
162 Session().commit()
163 set_rhodecode_config(config)
163 set_rhodecode_config(config)
164 h.flash(_('Updated application settings'), category='success')
164 h.flash(_('Updated application settings'), category='success')
165
165
166 except Exception:
166 except Exception:
167 log.error(traceback.format_exc())
167 log.error(traceback.format_exc())
168 h.flash(_('error occurred during updating '
168 h.flash(_('error occurred during updating '
169 'application settings'),
169 'application settings'),
170 category='error')
170 category='error')
171
171
172 if setting_id == 'visual':
172 if setting_id == 'visual':
173
173
174 application_form = ApplicationVisualisationForm()()
174 application_form = ApplicationVisualisationForm()()
175 try:
175 try:
176 form_result = application_form.to_python(dict(request.POST))
176 form_result = application_form.to_python(dict(request.POST))
177 except formencode.Invalid, errors:
177 except formencode.Invalid, errors:
178 return htmlfill.render(
178 return htmlfill.render(
179 render('admin/settings/settings.html'),
179 render('admin/settings/settings.html'),
180 defaults=errors.value,
180 defaults=errors.value,
181 errors=errors.error_dict or {},
181 errors=errors.error_dict or {},
182 prefix_error=False,
182 prefix_error=False,
183 encoding="UTF-8"
183 encoding="UTF-8"
184 )
184 )
185
185
186 try:
186 try:
187 sett1 = RhodeCodeSetting.get_by_name_or_create('show_public_icon')
187 sett1 = RhodeCodeSetting.get_by_name_or_create('show_public_icon')
188 sett1.app_settings_value = \
188 sett1.app_settings_value = \
189 form_result['rhodecode_show_public_icon']
189 form_result['rhodecode_show_public_icon']
190 Session().add(sett1)
190 Session().add(sett1)
191
191
192 sett2 = RhodeCodeSetting.get_by_name_or_create('show_private_icon')
192 sett2 = RhodeCodeSetting.get_by_name_or_create('show_private_icon')
193 sett2.app_settings_value = \
193 sett2.app_settings_value = \
194 form_result['rhodecode_show_private_icon']
194 form_result['rhodecode_show_private_icon']
195 Session().add(sett2)
195 Session().add(sett2)
196
196
197 sett3 = RhodeCodeSetting.get_by_name_or_create('stylify_metatags')
197 sett3 = RhodeCodeSetting.get_by_name_or_create('stylify_metatags')
198 sett3.app_settings_value = \
198 sett3.app_settings_value = \
199 form_result['rhodecode_stylify_metatags']
199 form_result['rhodecode_stylify_metatags']
200 Session().add(sett3)
200 Session().add(sett3)
201
201
202 sett4 = RhodeCodeSetting.get_by_name_or_create('lightweight_dashboard')
202 sett4 = RhodeCodeSetting.get_by_name_or_create('lightweight_dashboard')
203 sett4.app_settings_value = \
203 sett4.app_settings_value = \
204 form_result['rhodecode_lightweight_dashboard']
204 form_result['rhodecode_lightweight_dashboard']
205 Session().add(sett4)
205 Session().add(sett4)
206
206
207 Session().commit()
207 Session().commit()
208 set_rhodecode_config(config)
208 set_rhodecode_config(config)
209 h.flash(_('Updated visualisation settings'),
209 h.flash(_('Updated visualisation settings'),
210 category='success')
210 category='success')
211
211
212 except Exception:
212 except Exception:
213 log.error(traceback.format_exc())
213 log.error(traceback.format_exc())
214 h.flash(_('error occurred during updating '
214 h.flash(_('error occurred during updating '
215 'visualisation settings'),
215 'visualisation settings'),
216 category='error')
216 category='error')
217
217
218 if setting_id == 'vcs':
218 if setting_id == 'vcs':
219 application_form = ApplicationUiSettingsForm()()
219 application_form = ApplicationUiSettingsForm()()
220 try:
220 try:
221 form_result = application_form.to_python(dict(request.POST))
221 form_result = application_form.to_python(dict(request.POST))
222 except formencode.Invalid, errors:
222 except formencode.Invalid, errors:
223 return htmlfill.render(
223 return htmlfill.render(
224 render('admin/settings/settings.html'),
224 render('admin/settings/settings.html'),
225 defaults=errors.value,
225 defaults=errors.value,
226 errors=errors.error_dict or {},
226 errors=errors.error_dict or {},
227 prefix_error=False,
227 prefix_error=False,
228 encoding="UTF-8"
228 encoding="UTF-8"
229 )
229 )
230
230
231 try:
231 try:
232 # fix namespaces for hooks and extensions
232 # fix namespaces for hooks and extensions
233 _f = lambda s: s.replace('.', '_')
233 _f = lambda s: s.replace('.', '_')
234
234
235 sett = RhodeCodeUi.get_by_key('push_ssl')
235 sett = RhodeCodeUi.get_by_key('push_ssl')
236 sett.ui_value = form_result['web_push_ssl']
236 sett.ui_value = form_result['web_push_ssl']
237 Session().add(sett)
237 Session().add(sett)
238
238
239 sett = RhodeCodeUi.get_by_key('/')
239 sett = RhodeCodeUi.get_by_key('/')
240 sett.ui_value = form_result['paths_root_path']
240 sett.ui_value = form_result['paths_root_path']
241 Session().add(sett)
241 Session().add(sett)
242
242
243 #HOOKS
243 #HOOKS
244 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_UPDATE)
244 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_UPDATE)
245 sett.ui_active = form_result[_f('hooks_%s' %
245 sett.ui_active = form_result[_f('hooks_%s' %
246 RhodeCodeUi.HOOK_UPDATE)]
246 RhodeCodeUi.HOOK_UPDATE)]
247 Session().add(sett)
247 Session().add(sett)
248
248
249 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_REPO_SIZE)
249 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_REPO_SIZE)
250 sett.ui_active = form_result[_f('hooks_%s' %
250 sett.ui_active = form_result[_f('hooks_%s' %
251 RhodeCodeUi.HOOK_REPO_SIZE)]
251 RhodeCodeUi.HOOK_REPO_SIZE)]
252 Session().add(sett)
252 Session().add(sett)
253
253
254 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_PUSH)
254 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_PUSH)
255 sett.ui_active = form_result[_f('hooks_%s' %
255 sett.ui_active = form_result[_f('hooks_%s' %
256 RhodeCodeUi.HOOK_PUSH)]
256 RhodeCodeUi.HOOK_PUSH)]
257 Session().add(sett)
257 Session().add(sett)
258
258
259 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_PULL)
259 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_PULL)
260 sett.ui_active = form_result[_f('hooks_%s' %
260 sett.ui_active = form_result[_f('hooks_%s' %
261 RhodeCodeUi.HOOK_PULL)]
261 RhodeCodeUi.HOOK_PULL)]
262
262
263 Session().add(sett)
263 Session().add(sett)
264
264
265 ## EXTENSIONS
265 ## EXTENSIONS
266 sett = RhodeCodeUi.get_by_key('largefiles')
266 sett = RhodeCodeUi.get_by_key('largefiles')
267 if not sett:
267 if not sett:
268 #make one if it's not there !
268 #make one if it's not there !
269 sett = RhodeCodeUi()
269 sett = RhodeCodeUi()
270 sett.ui_key = 'largefiles'
270 sett.ui_key = 'largefiles'
271 sett.ui_section = 'extensions'
271 sett.ui_section = 'extensions'
272 sett.ui_active = form_result[_f('extensions_largefiles')]
272 sett.ui_active = form_result[_f('extensions_largefiles')]
273 Session().add(sett)
273 Session().add(sett)
274
274
275 sett = RhodeCodeUi.get_by_key('hgsubversion')
275 sett = RhodeCodeUi.get_by_key('hgsubversion')
276 if not sett:
276 if not sett:
277 #make one if it's not there !
277 #make one if it's not there !
278 sett = RhodeCodeUi()
278 sett = RhodeCodeUi()
279 sett.ui_key = 'hgsubversion'
279 sett.ui_key = 'hgsubversion'
280 sett.ui_section = 'extensions'
280 sett.ui_section = 'extensions'
281
281
282 sett.ui_active = form_result[_f('extensions_hgsubversion')]
282 sett.ui_active = form_result[_f('extensions_hgsubversion')]
283 Session().add(sett)
283 Session().add(sett)
284
284
285 # sett = RhodeCodeUi.get_by_key('hggit')
285 # sett = RhodeCodeUi.get_by_key('hggit')
286 # if not sett:
286 # if not sett:
287 # #make one if it's not there !
287 # #make one if it's not there !
288 # sett = RhodeCodeUi()
288 # sett = RhodeCodeUi()
289 # sett.ui_key = 'hggit'
289 # sett.ui_key = 'hggit'
290 # sett.ui_section = 'extensions'
290 # sett.ui_section = 'extensions'
291 #
291 #
292 # sett.ui_active = form_result[_f('extensions_hggit')]
292 # sett.ui_active = form_result[_f('extensions_hggit')]
293 # Session().add(sett)
293 # Session().add(sett)
294
294
295 Session().commit()
295 Session().commit()
296
296
297 h.flash(_('Updated VCS settings'), category='success')
297 h.flash(_('Updated VCS settings'), category='success')
298
298
299 except Exception:
299 except Exception:
300 log.error(traceback.format_exc())
300 log.error(traceback.format_exc())
301 h.flash(_('error occurred during updating '
301 h.flash(_('error occurred during updating '
302 'application settings'), category='error')
302 'application settings'), category='error')
303
303
304 if setting_id == 'hooks':
304 if setting_id == 'hooks':
305 ui_key = request.POST.get('new_hook_ui_key')
305 ui_key = request.POST.get('new_hook_ui_key')
306 ui_value = request.POST.get('new_hook_ui_value')
306 ui_value = request.POST.get('new_hook_ui_value')
307 try:
307 try:
308
308
309 if ui_value and ui_key:
309 if ui_value and ui_key:
310 RhodeCodeUi.create_or_update_hook(ui_key, ui_value)
310 RhodeCodeUi.create_or_update_hook(ui_key, ui_value)
311 h.flash(_('Added new hook'),
311 h.flash(_('Added new hook'),
312 category='success')
312 category='success')
313
313
314 # check for edits
314 # check for edits
315 update = False
315 update = False
316 _d = request.POST.dict_of_lists()
316 _d = request.POST.dict_of_lists()
317 for k, v in zip(_d.get('hook_ui_key', []),
317 for k, v in zip(_d.get('hook_ui_key', []),
318 _d.get('hook_ui_value_new', [])):
318 _d.get('hook_ui_value_new', [])):
319 RhodeCodeUi.create_or_update_hook(k, v)
319 RhodeCodeUi.create_or_update_hook(k, v)
320 update = True
320 update = True
321
321
322 if update:
322 if update:
323 h.flash(_('Updated hooks'), category='success')
323 h.flash(_('Updated hooks'), category='success')
324 Session().commit()
324 Session().commit()
325 except Exception:
325 except Exception:
326 log.error(traceback.format_exc())
326 log.error(traceback.format_exc())
327 h.flash(_('error occurred during hook creation'),
327 h.flash(_('error occurred during hook creation'),
328 category='error')
328 category='error')
329
329
330 return redirect(url('admin_edit_setting', setting_id='hooks'))
330 return redirect(url('admin_edit_setting', setting_id='hooks'))
331
331
332 if setting_id == 'email':
332 if setting_id == 'email':
333 test_email = request.POST.get('test_email')
333 test_email = request.POST.get('test_email')
334 test_email_subj = 'RhodeCode TestEmail'
334 test_email_subj = 'RhodeCode TestEmail'
335 test_email_body = 'RhodeCode Email test'
335 test_email_body = 'RhodeCode Email test'
336
336
337 test_email_html_body = EmailNotificationModel()\
337 test_email_html_body = EmailNotificationModel()\
338 .get_email_tmpl(EmailNotificationModel.TYPE_DEFAULT,
338 .get_email_tmpl(EmailNotificationModel.TYPE_DEFAULT,
339 body=test_email_body)
339 body=test_email_body)
340
340
341 recipients = [test_email] if test_email else None
341 recipients = [test_email] if test_email else None
342
342
343 run_task(tasks.send_email, recipients, test_email_subj,
343 run_task(tasks.send_email, recipients, test_email_subj,
344 test_email_body, test_email_html_body)
344 test_email_body, test_email_html_body)
345
345
346 h.flash(_('Email task created'), category='success')
346 h.flash(_('Email task created'), category='success')
347 return redirect(url('admin_settings'))
347 return redirect(url('admin_settings'))
348
348
349 @HasPermissionAllDecorator('hg.admin')
349 @HasPermissionAllDecorator('hg.admin')
350 def delete(self, setting_id):
350 def delete(self, setting_id):
351 """DELETE /admin/settings/setting_id: Delete an existing item"""
351 """DELETE /admin/settings/setting_id: Delete an existing item"""
352 # Forms posted to this method should contain a hidden field:
352 # Forms posted to this method should contain a hidden field:
353 # <input type="hidden" name="_method" value="DELETE" />
353 # <input type="hidden" name="_method" value="DELETE" />
354 # Or using helpers:
354 # Or using helpers:
355 # h.form(url('admin_setting', setting_id=ID),
355 # h.form(url('admin_setting', setting_id=ID),
356 # method='delete')
356 # method='delete')
357 # url('admin_setting', setting_id=ID)
357 # url('admin_setting', setting_id=ID)
358 if setting_id == 'hooks':
358 if setting_id == 'hooks':
359 hook_id = request.POST.get('hook_id')
359 hook_id = request.POST.get('hook_id')
360 RhodeCodeUi.delete(hook_id)
360 RhodeCodeUi.delete(hook_id)
361 Session().commit()
361 Session().commit()
362
362
363 @HasPermissionAllDecorator('hg.admin')
363 @HasPermissionAllDecorator('hg.admin')
364 def show(self, setting_id, format='html'):
364 def show(self, setting_id, format='html'):
365 """
365 """
366 GET /admin/settings/setting_id: Show a specific item"""
366 GET /admin/settings/setting_id: Show a specific item"""
367 # url('admin_setting', setting_id=ID)
367 # url('admin_setting', setting_id=ID)
368
368
369 @HasPermissionAllDecorator('hg.admin')
369 @HasPermissionAllDecorator('hg.admin')
370 def edit(self, setting_id, format='html'):
370 def edit(self, setting_id, format='html'):
371 """
371 """
372 GET /admin/settings/setting_id/edit: Form to
372 GET /admin/settings/setting_id/edit: Form to
373 edit an existing item"""
373 edit an existing item"""
374 # url('admin_edit_setting', setting_id=ID)
374 # url('admin_edit_setting', setting_id=ID)
375
375
376 c.hooks = RhodeCodeUi.get_builtin_hooks()
376 c.hooks = RhodeCodeUi.get_builtin_hooks()
377 c.custom_hooks = RhodeCodeUi.get_custom_hooks()
377 c.custom_hooks = RhodeCodeUi.get_custom_hooks()
378
378
379 return htmlfill.render(
379 return htmlfill.render(
380 render('admin/settings/hooks.html'),
380 render('admin/settings/hooks.html'),
381 defaults={},
381 defaults={},
382 encoding="UTF-8",
382 encoding="UTF-8",
383 force_defaults=False
383 force_defaults=False
384 )
384 )
385
385
386 def _load_my_repos_data(self):
386 def _load_my_repos_data(self):
387 repos_list = Session().query(Repository)\
387 repos_list = Session().query(Repository)\
388 .filter(Repository.user_id ==
388 .filter(Repository.user_id ==
389 self.rhodecode_user.user_id)\
389 self.rhodecode_user.user_id)\
390 .order_by(func.lower(Repository.repo_name)).all()
390 .order_by(func.lower(Repository.repo_name)).all()
391
391
392 repos_data = RepoModel().get_repos_as_dict(repos_list=repos_list,
392 repos_data = RepoModel().get_repos_as_dict(repos_list=repos_list,
393 admin=True)
393 admin=True)
394 #json used to render the grid
394 #json used to render the grid
395 return json.dumps(repos_data)
395 return json.dumps(repos_data)
396
396
397 @NotAnonymous()
397 @NotAnonymous()
398 def my_account(self):
398 def my_account(self):
399 """
399 """
400 GET /_admin/my_account Displays info about my account
400 GET /_admin/my_account Displays info about my account
401 """
401 """
402 # url('admin_settings_my_account')
402 # url('admin_settings_my_account')
403
403
404 c.user = User.get(self.rhodecode_user.user_id)
404 c.user = User.get(self.rhodecode_user.user_id)
405 c.ldap_dn = c.user.ldap_dn
405 c.ldap_dn = c.user.ldap_dn
406
406
407 if c.user.username == 'default':
407 if c.user.username == 'default':
408 h.flash(_("You can't edit this user since it's"
408 h.flash(_("You can't edit this user since it's"
409 " crucial for entire application"), category='warning')
409 " crucial for entire application"), category='warning')
410 return redirect(url('users'))
410 return redirect(url('users'))
411
411
412 #json used to render the grid
412 #json used to render the grid
413 c.data = self._load_my_repos_data()
413 c.data = self._load_my_repos_data()
414
414
415 defaults = c.user.get_dict()
415 defaults = c.user.get_dict()
416
416
417 c.form = htmlfill.render(
417 c.form = htmlfill.render(
418 render('admin/users/user_edit_my_account_form.html'),
418 render('admin/users/user_edit_my_account_form.html'),
419 defaults=defaults,
419 defaults=defaults,
420 encoding="UTF-8",
420 encoding="UTF-8",
421 force_defaults=False
421 force_defaults=False
422 )
422 )
423 return render('admin/users/user_edit_my_account.html')
423 return render('admin/users/user_edit_my_account.html')
424
424
425 @NotAnonymous()
425 @NotAnonymous()
426 def my_account_update(self):
426 def my_account_update(self):
427 """PUT /_admin/my_account_update: Update an existing item"""
427 """PUT /_admin/my_account_update: Update an existing item"""
428 # Forms posted to this method should contain a hidden field:
428 # Forms posted to this method should contain a hidden field:
429 # <input type="hidden" name="_method" value="PUT" />
429 # <input type="hidden" name="_method" value="PUT" />
430 # Or using helpers:
430 # Or using helpers:
431 # h.form(url('admin_settings_my_account_update'),
431 # h.form(url('admin_settings_my_account_update'),
432 # method='put')
432 # method='put')
433 # url('admin_settings_my_account_update', id=ID)
433 # url('admin_settings_my_account_update', id=ID)
434 uid = self.rhodecode_user.user_id
434 uid = self.rhodecode_user.user_id
435 c.user = User.get(self.rhodecode_user.user_id)
435 c.user = User.get(self.rhodecode_user.user_id)
436 c.ldap_dn = c.user.ldap_dn
436 c.ldap_dn = c.user.ldap_dn
437 email = self.rhodecode_user.email
437 email = self.rhodecode_user.email
438 _form = UserForm(edit=True,
438 _form = UserForm(edit=True,
439 old_data={'user_id': uid, 'email': email})()
439 old_data={'user_id': uid, 'email': email})()
440 form_result = {}
440 form_result = {}
441 try:
441 try:
442 form_result = _form.to_python(dict(request.POST))
442 form_result = _form.to_python(dict(request.POST))
443 skip_attrs = ['admin', 'active'] # skip attr for my account
443 skip_attrs = ['admin', 'active'] # skip attr for my account
444 if c.ldap_dn:
444 if c.ldap_dn:
445 #forbid updating username for ldap accounts
445 #forbid updating username for ldap accounts
446 skip_attrs.append('username')
446 skip_attrs.append('username')
447 UserModel().update(uid, form_result, skip_attrs=skip_attrs)
447 UserModel().update(uid, form_result, skip_attrs=skip_attrs)
448 h.flash(_('Your account was updated successfully'),
448 h.flash(_('Your account was updated successfully'),
449 category='success')
449 category='success')
450 Session().commit()
450 Session().commit()
451 except formencode.Invalid, errors:
451 except formencode.Invalid, errors:
452 #json used to render the grid
452 #json used to render the grid
453 c.data = self._load_my_repos_data()
453 c.data = self._load_my_repos_data()
454 c.form = htmlfill.render(
454 c.form = htmlfill.render(
455 render('admin/users/user_edit_my_account_form.html'),
455 render('admin/users/user_edit_my_account_form.html'),
456 defaults=errors.value,
456 defaults=errors.value,
457 errors=errors.error_dict or {},
457 errors=errors.error_dict or {},
458 prefix_error=False,
458 prefix_error=False,
459 encoding="UTF-8")
459 encoding="UTF-8")
460 return render('admin/users/user_edit_my_account.html')
460 return render('admin/users/user_edit_my_account.html')
461 except Exception:
461 except Exception:
462 log.error(traceback.format_exc())
462 log.error(traceback.format_exc())
463 h.flash(_('error occurred during update of user %s') \
463 h.flash(_('error occurred during update of user %s') \
464 % form_result.get('username'), category='error')
464 % form_result.get('username'), category='error')
465
465
466 return redirect(url('my_account'))
466 return redirect(url('my_account'))
467
467
468 @NotAnonymous()
468 @NotAnonymous()
469 def my_account_my_pullrequests(self):
469 def my_account_my_pullrequests(self):
470 c.my_pull_requests = PullRequest.query()\
470 c.my_pull_requests = PullRequest.query()\
471 .filter(PullRequest.user_id ==
471 .filter(PullRequest.user_id ==
472 self.rhodecode_user.user_id)\
472 self.rhodecode_user.user_id)\
473 .all()
473 .all()
474 c.participate_in_pull_requests = \
474 c.participate_in_pull_requests = \
475 [x.pull_request for x in PullRequestReviewers.query()\
475 [x.pull_request for x in PullRequestReviewers.query()\
476 .filter(PullRequestReviewers.user_id ==
476 .filter(PullRequestReviewers.user_id ==
477 self.rhodecode_user.user_id)\
477 self.rhodecode_user.user_id)\
478 .all()]
478 .all()]
479 return render('admin/users/user_edit_my_account_pullrequests.html')
479 return render('admin/users/user_edit_my_account_pullrequests.html')
480
480
481 @NotAnonymous()
481 @NotAnonymous()
482 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
482 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
483 def create_repository(self):
483 def create_repository(self):
484 """GET /_admin/create_repository: Form to create a new item"""
484 """GET /_admin/create_repository: Form to create a new item"""
485
485
486 c.repo_groups = RepoGroup.groups_choices(check_perms=True)
486 acl_groups = GroupList(RepoGroup.query().all(),
487 perm_set=['group.write', 'group.admin'])
488 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
487 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
489 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
488 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
490 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
489
491
490 new_repo = request.GET.get('repo', '')
492 new_repo = request.GET.get('repo', '')
491 parent_group = request.GET.get('parent_group')
493 parent_group = request.GET.get('parent_group')
492 c.new_repo = repo_name_slug(new_repo)
494 c.new_repo = repo_name_slug(new_repo)
493
495
494 ## apply the defaults from defaults page
496 ## apply the defaults from defaults page
495 defaults = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
497 defaults = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
496 if parent_group:
498 if parent_group:
497 defaults.update({'repo_group': parent_group})
499 defaults.update({'repo_group': parent_group})
498
500
499 return htmlfill.render(
501 return htmlfill.render(
500 render('admin/repos/repo_add_create_repository.html'),
502 render('admin/repos/repo_add_create_repository.html'),
501 defaults=defaults,
503 defaults=defaults,
502 errors={},
504 errors={},
503 prefix_error=False,
505 prefix_error=False,
504 encoding="UTF-8"
506 encoding="UTF-8"
505 )
507 )
506
508
507 def _get_hg_ui_settings(self):
509 def _get_hg_ui_settings(self):
508 ret = RhodeCodeUi.query().all()
510 ret = RhodeCodeUi.query().all()
509
511
510 if not ret:
512 if not ret:
511 raise Exception('Could not get application ui settings !')
513 raise Exception('Could not get application ui settings !')
512 settings = {}
514 settings = {}
513 for each in ret:
515 for each in ret:
514 k = each.ui_key
516 k = each.ui_key
515 v = each.ui_value
517 v = each.ui_value
516 if k == '/':
518 if k == '/':
517 k = 'root_path'
519 k = 'root_path'
518
520
519 if k == 'push_ssl':
521 if k == 'push_ssl':
520 v = str2bool(v)
522 v = str2bool(v)
521
523
522 if k.find('.') != -1:
524 if k.find('.') != -1:
523 k = k.replace('.', '_')
525 k = k.replace('.', '_')
524
526
525 if each.ui_section in ['hooks', 'extensions']:
527 if each.ui_section in ['hooks', 'extensions']:
526 v = each.ui_active
528 v = each.ui_active
527
529
528 settings[each.ui_section + '_' + k] = v
530 settings[each.ui_section + '_' + k] = v
529 return settings
531 return settings
@@ -1,180 +1,182 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.forks
3 rhodecode.controllers.forks
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 forks controller for rhodecode
6 forks controller for rhodecode
7
7
8 :created_on: Apr 23, 2011
8 :created_on: Apr 23, 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 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 import logging
25 import logging
26 import formencode
26 import formencode
27 import traceback
27 import traceback
28 from formencode import htmlfill
28 from formencode import htmlfill
29
29
30 from pylons import tmpl_context as c, request, url
30 from pylons import tmpl_context as c, request, url
31 from pylons.controllers.util import redirect
31 from pylons.controllers.util import redirect
32 from pylons.i18n.translation import _
32 from pylons.i18n.translation import _
33
33
34 import rhodecode.lib.helpers as h
34 import rhodecode.lib.helpers as h
35
35
36 from rhodecode.lib.helpers import Page
36 from rhodecode.lib.helpers import Page
37 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator, \
37 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator, \
38 NotAnonymous, HasRepoPermissionAny, HasPermissionAllDecorator,\
38 NotAnonymous, HasRepoPermissionAny, HasPermissionAllDecorator,\
39 HasPermissionAnyDecorator
39 HasPermissionAnyDecorator
40 from rhodecode.lib.base import BaseRepoController, render
40 from rhodecode.lib.base import BaseRepoController, render
41 from rhodecode.model.db import Repository, RepoGroup, UserFollowing, User
41 from rhodecode.model.db import Repository, RepoGroup, UserFollowing, User
42 from rhodecode.model.repo import RepoModel
42 from rhodecode.model.repo import RepoModel
43 from rhodecode.model.forms import RepoForkForm
43 from rhodecode.model.forms import RepoForkForm
44 from rhodecode.model.scm import ScmModel
44 from rhodecode.model.scm import ScmModel, GroupList
45 from rhodecode.lib.utils2 import safe_int
45 from rhodecode.lib.utils2 import safe_int
46
46
47 log = logging.getLogger(__name__)
47 log = logging.getLogger(__name__)
48
48
49
49
50 class ForksController(BaseRepoController):
50 class ForksController(BaseRepoController):
51
51
52 @LoginRequired()
52 @LoginRequired()
53 def __before__(self):
53 def __before__(self):
54 super(ForksController, self).__before__()
54 super(ForksController, self).__before__()
55
55
56 def __load_defaults(self):
56 def __load_defaults(self):
57 c.repo_groups = RepoGroup.groups_choices(check_perms=True)
57 acl_groups = GroupList(RepoGroup.query().all(),
58 perm_set=['group.write', 'group.admin'])
59 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
58 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
60 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
59 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
61 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
60 c.landing_revs_choices = choices
62 c.landing_revs_choices = choices
61
63
62 def __load_data(self, repo_name=None):
64 def __load_data(self, repo_name=None):
63 """
65 """
64 Load defaults settings for edit, and update
66 Load defaults settings for edit, and update
65
67
66 :param repo_name:
68 :param repo_name:
67 """
69 """
68 self.__load_defaults()
70 self.__load_defaults()
69
71
70 c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
72 c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
71 repo = db_repo.scm_instance
73 repo = db_repo.scm_instance
72
74
73 if c.repo_info is None:
75 if c.repo_info is None:
74 h.not_mapped_error(repo_name)
76 h.not_mapped_error(repo_name)
75 return redirect(url('repos'))
77 return redirect(url('repos'))
76
78
77 c.default_user_id = User.get_by_username('default').user_id
79 c.default_user_id = User.get_by_username('default').user_id
78 c.in_public_journal = UserFollowing.query()\
80 c.in_public_journal = UserFollowing.query()\
79 .filter(UserFollowing.user_id == c.default_user_id)\
81 .filter(UserFollowing.user_id == c.default_user_id)\
80 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
82 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
81
83
82 if c.repo_info.stats:
84 if c.repo_info.stats:
83 last_rev = c.repo_info.stats.stat_on_revision+1
85 last_rev = c.repo_info.stats.stat_on_revision+1
84 else:
86 else:
85 last_rev = 0
87 last_rev = 0
86 c.stats_revision = last_rev
88 c.stats_revision = last_rev
87
89
88 c.repo_last_rev = repo.count() if repo.revisions else 0
90 c.repo_last_rev = repo.count() if repo.revisions else 0
89
91
90 if last_rev == 0 or c.repo_last_rev == 0:
92 if last_rev == 0 or c.repo_last_rev == 0:
91 c.stats_percentage = 0
93 c.stats_percentage = 0
92 else:
94 else:
93 c.stats_percentage = '%.2f' % ((float((last_rev)) /
95 c.stats_percentage = '%.2f' % ((float((last_rev)) /
94 c.repo_last_rev) * 100)
96 c.repo_last_rev) * 100)
95
97
96 defaults = RepoModel()._get_defaults(repo_name)
98 defaults = RepoModel()._get_defaults(repo_name)
97 # alter the description to indicate a fork
99 # alter the description to indicate a fork
98 defaults['description'] = ('fork of repository: %s \n%s'
100 defaults['description'] = ('fork of repository: %s \n%s'
99 % (defaults['repo_name'],
101 % (defaults['repo_name'],
100 defaults['description']))
102 defaults['description']))
101 # add suffix to fork
103 # add suffix to fork
102 defaults['repo_name'] = '%s-fork' % defaults['repo_name']
104 defaults['repo_name'] = '%s-fork' % defaults['repo_name']
103
105
104 return defaults
106 return defaults
105
107
106 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
108 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
107 'repository.admin')
109 'repository.admin')
108 def forks(self, repo_name):
110 def forks(self, repo_name):
109 p = safe_int(request.params.get('page', 1), 1)
111 p = safe_int(request.params.get('page', 1), 1)
110 repo_id = c.rhodecode_db_repo.repo_id
112 repo_id = c.rhodecode_db_repo.repo_id
111 d = []
113 d = []
112 for r in Repository.get_repo_forks(repo_id):
114 for r in Repository.get_repo_forks(repo_id):
113 if not HasRepoPermissionAny(
115 if not HasRepoPermissionAny(
114 'repository.read', 'repository.write', 'repository.admin'
116 'repository.read', 'repository.write', 'repository.admin'
115 )(r.repo_name, 'get forks check'):
117 )(r.repo_name, 'get forks check'):
116 continue
118 continue
117 d.append(r)
119 d.append(r)
118 c.forks_pager = Page(d, page=p, items_per_page=20)
120 c.forks_pager = Page(d, page=p, items_per_page=20)
119
121
120 c.forks_data = render('/forks/forks_data.html')
122 c.forks_data = render('/forks/forks_data.html')
121
123
122 if request.environ.get('HTTP_X_PARTIAL_XHR'):
124 if request.environ.get('HTTP_X_PARTIAL_XHR'):
123 return c.forks_data
125 return c.forks_data
124
126
125 return render('/forks/forks.html')
127 return render('/forks/forks.html')
126
128
127 @NotAnonymous()
129 @NotAnonymous()
128 @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
130 @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
129 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
131 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
130 'repository.admin')
132 'repository.admin')
131 def fork(self, repo_name):
133 def fork(self, repo_name):
132 c.repo_info = Repository.get_by_repo_name(repo_name)
134 c.repo_info = Repository.get_by_repo_name(repo_name)
133 if not c.repo_info:
135 if not c.repo_info:
134 h.not_mapped_error(repo_name)
136 h.not_mapped_error(repo_name)
135 return redirect(url('home'))
137 return redirect(url('home'))
136
138
137 defaults = self.__load_data(repo_name)
139 defaults = self.__load_data(repo_name)
138
140
139 return htmlfill.render(
141 return htmlfill.render(
140 render('forks/fork.html'),
142 render('forks/fork.html'),
141 defaults=defaults,
143 defaults=defaults,
142 encoding="UTF-8",
144 encoding="UTF-8",
143 force_defaults=False
145 force_defaults=False
144 )
146 )
145
147
146 @NotAnonymous()
148 @NotAnonymous()
147 @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
149 @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
148 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
150 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
149 'repository.admin')
151 'repository.admin')
150 def fork_create(self, repo_name):
152 def fork_create(self, repo_name):
151 self.__load_defaults()
153 self.__load_defaults()
152 c.repo_info = Repository.get_by_repo_name(repo_name)
154 c.repo_info = Repository.get_by_repo_name(repo_name)
153 _form = RepoForkForm(old_data={'repo_type': c.repo_info.repo_type},
155 _form = RepoForkForm(old_data={'repo_type': c.repo_info.repo_type},
154 repo_groups=c.repo_groups_choices,
156 repo_groups=c.repo_groups_choices,
155 landing_revs=c.landing_revs_choices)()
157 landing_revs=c.landing_revs_choices)()
156 form_result = {}
158 form_result = {}
157 try:
159 try:
158 form_result = _form.to_python(dict(request.POST))
160 form_result = _form.to_python(dict(request.POST))
159
161
160 # create fork is done sometimes async on celery, db transaction
162 # create fork is done sometimes async on celery, db transaction
161 # management is handled there.
163 # management is handled there.
162 RepoModel().create_fork(form_result, self.rhodecode_user.user_id)
164 RepoModel().create_fork(form_result, self.rhodecode_user.user_id)
163 h.flash(_('forked %s repository as %s') \
165 h.flash(_('forked %s repository as %s') \
164 % (repo_name, form_result['repo_name']),
166 % (repo_name, form_result['repo_name']),
165 category='success')
167 category='success')
166 except formencode.Invalid, errors:
168 except formencode.Invalid, errors:
167 c.new_repo = errors.value['repo_name']
169 c.new_repo = errors.value['repo_name']
168
170
169 return htmlfill.render(
171 return htmlfill.render(
170 render('forks/fork.html'),
172 render('forks/fork.html'),
171 defaults=errors.value,
173 defaults=errors.value,
172 errors=errors.error_dict or {},
174 errors=errors.error_dict or {},
173 prefix_error=False,
175 prefix_error=False,
174 encoding="UTF-8")
176 encoding="UTF-8")
175 except Exception:
177 except Exception:
176 log.error(traceback.format_exc())
178 log.error(traceback.format_exc())
177 h.flash(_('An error occurred during repository forking %s') %
179 h.flash(_('An error occurred during repository forking %s') %
178 repo_name, category='error')
180 repo_name, category='error')
179
181
180 return redirect(url('home'))
182 return redirect(url('home'))
@@ -1,196 +1,197 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.settings
3 rhodecode.controllers.settings
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Settings controller for rhodecode
6 Settings controller for rhodecode
7
7
8 :created_on: Jun 30, 2010
8 :created_on: Jun 30, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import logging
26 import logging
27 import traceback
27 import traceback
28 import formencode
28 import formencode
29
29
30 from formencode import htmlfill
30 from formencode import htmlfill
31
31
32 from pylons import tmpl_context as c, request, url
32 from pylons import tmpl_context as c, request, url
33 from pylons.controllers.util import redirect
33 from pylons.controllers.util import redirect
34 from pylons.i18n.translation import _
34 from pylons.i18n.translation import _
35
35
36 import rhodecode.lib.helpers as h
36 import rhodecode.lib.helpers as h
37
37
38 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAllDecorator,\
38 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAllDecorator,\
39 HasRepoPermissionAnyDecorator
39 HasRepoPermissionAnyDecorator
40 from rhodecode.lib.base import BaseRepoController, render
40 from rhodecode.lib.base import BaseRepoController, render
41 from rhodecode.lib.utils import invalidate_cache, action_logger
41 from rhodecode.lib.utils import invalidate_cache, action_logger
42
42
43 from rhodecode.model.forms import RepoSettingsForm
43 from rhodecode.model.forms import RepoSettingsForm
44 from rhodecode.model.repo import RepoModel
44 from rhodecode.model.repo import RepoModel
45 from rhodecode.model.db import RepoGroup, Repository
45 from rhodecode.model.db import RepoGroup, Repository
46 from rhodecode.model.meta import Session
46 from rhodecode.model.meta import Session
47 from rhodecode.model.scm import ScmModel
47 from rhodecode.model.scm import ScmModel, GroupList
48
48
49 log = logging.getLogger(__name__)
49 log = logging.getLogger(__name__)
50
50
51
51
52 class SettingsController(BaseRepoController):
52 class SettingsController(BaseRepoController):
53
53
54 @LoginRequired()
54 @LoginRequired()
55 def __before__(self):
55 def __before__(self):
56 super(SettingsController, self).__before__()
56 super(SettingsController, self).__before__()
57
57
58 def __load_defaults(self):
58 def __load_defaults(self):
59 c.repo_groups = RepoGroup.groups_choices(check_perms=True)
59 acl_groups = GroupList(RepoGroup.query().all(),
60 perm_set=['group.write', 'group.admin'])
61 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
60 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
62 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
61
63
62 repo_model = RepoModel()
64 repo_model = RepoModel()
63 c.users_array = repo_model.get_users_js()
65 c.users_array = repo_model.get_users_js()
64 c.users_groups_array = repo_model.get_users_groups_js()
66 c.users_groups_array = repo_model.get_users_groups_js()
65 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
67 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
66 c.landing_revs_choices = choices
68 c.landing_revs_choices = choices
67
69
68 def __load_data(self, repo_name=None):
70 def __load_data(self, repo_name=None):
69 """
71 """
70 Load defaults settings for edit, and update
72 Load defaults settings for edit, and update
71
73
72 :param repo_name:
74 :param repo_name:
73 """
75 """
74 self.__load_defaults()
76 self.__load_defaults()
75
77
76 c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
78 c.repo_info = Repository.get_by_repo_name(repo_name)
77 repo = db_repo.scm_instance
78
79
79 if c.repo_info is None:
80 if c.repo_info is None:
80 h.not_mapped_error(repo_name)
81 h.not_mapped_error(repo_name)
81 return redirect(url('home'))
82 return redirect(url('home'))
82
83
83 ##override defaults for exact repo info here git/hg etc
84 ##override defaults for exact repo info here git/hg etc
84 choices, c.landing_revs = ScmModel().get_repo_landing_revs(c.repo_info)
85 choices, c.landing_revs = ScmModel().get_repo_landing_revs(c.repo_info)
85 c.landing_revs_choices = choices
86 c.landing_revs_choices = choices
86
87
87 defaults = RepoModel()._get_defaults(repo_name)
88 defaults = RepoModel()._get_defaults(repo_name)
88
89
89 return defaults
90 return defaults
90
91
91 @HasRepoPermissionAllDecorator('repository.admin')
92 @HasRepoPermissionAllDecorator('repository.admin')
92 def index(self, repo_name):
93 def index(self, repo_name):
93 defaults = self.__load_data(repo_name)
94 defaults = self.__load_data(repo_name)
94
95
95 return htmlfill.render(
96 return htmlfill.render(
96 render('settings/repo_settings.html'),
97 render('settings/repo_settings.html'),
97 defaults=defaults,
98 defaults=defaults,
98 encoding="UTF-8",
99 encoding="UTF-8",
99 force_defaults=False
100 force_defaults=False
100 )
101 )
101
102
102 @HasRepoPermissionAllDecorator('repository.admin')
103 @HasRepoPermissionAllDecorator('repository.admin')
103 def update(self, repo_name):
104 def update(self, repo_name):
104 self.__load_defaults()
105 self.__load_defaults()
105 repo_model = RepoModel()
106 repo_model = RepoModel()
106 changed_name = repo_name
107 changed_name = repo_name
107 #override the choices with extracted revisions !
108 #override the choices with extracted revisions !
108 choices, c.landing_revs = ScmModel().get_repo_landing_revs(repo_name)
109 choices, c.landing_revs = ScmModel().get_repo_landing_revs(repo_name)
109 c.landing_revs_choices = choices
110 c.landing_revs_choices = choices
110
111
111 _form = RepoSettingsForm(edit=True,
112 _form = RepoSettingsForm(edit=True,
112 old_data={'repo_name': repo_name},
113 old_data={'repo_name': repo_name},
113 repo_groups=c.repo_groups_choices,
114 repo_groups=c.repo_groups_choices,
114 landing_revs=c.landing_revs_choices)()
115 landing_revs=c.landing_revs_choices)()
115 try:
116 try:
116 form_result = _form.to_python(dict(request.POST))
117 form_result = _form.to_python(dict(request.POST))
117 repo_model.update(repo_name, **form_result)
118 repo_model.update(repo_name, **form_result)
118 invalidate_cache('get_repo_cached_%s' % repo_name)
119 invalidate_cache('get_repo_cached_%s' % repo_name)
119 h.flash(_('Repository %s updated successfully') % repo_name,
120 h.flash(_('Repository %s updated successfully') % repo_name,
120 category='success')
121 category='success')
121 changed_name = form_result['repo_name_full']
122 changed_name = form_result['repo_name_full']
122 action_logger(self.rhodecode_user, 'user_updated_repo',
123 action_logger(self.rhodecode_user, 'user_updated_repo',
123 changed_name, self.ip_addr, self.sa)
124 changed_name, self.ip_addr, self.sa)
124 Session().commit()
125 Session().commit()
125 except formencode.Invalid, errors:
126 except formencode.Invalid, errors:
126 defaults = self.__load_data(repo_name)
127 defaults = self.__load_data(repo_name)
127 defaults.update(errors.value)
128 defaults.update(errors.value)
128 return htmlfill.render(
129 return htmlfill.render(
129 render('settings/repo_settings.html'),
130 render('settings/repo_settings.html'),
130 defaults=errors.value,
131 defaults=errors.value,
131 errors=errors.error_dict or {},
132 errors=errors.error_dict or {},
132 prefix_error=False,
133 prefix_error=False,
133 encoding="UTF-8")
134 encoding="UTF-8")
134
135
135 except Exception:
136 except Exception:
136 log.error(traceback.format_exc())
137 log.error(traceback.format_exc())
137 h.flash(_('error occurred during update of repository %s') \
138 h.flash(_('error occurred during update of repository %s') \
138 % repo_name, category='error')
139 % repo_name, category='error')
139
140
140 return redirect(url('repo_settings_home', repo_name=changed_name))
141 return redirect(url('repo_settings_home', repo_name=changed_name))
141
142
142 @HasRepoPermissionAllDecorator('repository.admin')
143 @HasRepoPermissionAllDecorator('repository.admin')
143 def delete(self, repo_name):
144 def delete(self, repo_name):
144 """DELETE /repos/repo_name: Delete an existing item"""
145 """DELETE /repos/repo_name: Delete an existing item"""
145 # Forms posted to this method should contain a hidden field:
146 # Forms posted to this method should contain a hidden field:
146 # <input type="hidden" name="_method" value="DELETE" />
147 # <input type="hidden" name="_method" value="DELETE" />
147 # Or using helpers:
148 # Or using helpers:
148 # h.form(url('repo_settings_delete', repo_name=ID),
149 # h.form(url('repo_settings_delete', repo_name=ID),
149 # method='delete')
150 # method='delete')
150 # url('repo_settings_delete', repo_name=ID)
151 # url('repo_settings_delete', repo_name=ID)
151
152
152 repo_model = RepoModel()
153 repo_model = RepoModel()
153 repo = repo_model.get_by_repo_name(repo_name)
154 repo = repo_model.get_by_repo_name(repo_name)
154 if not repo:
155 if not repo:
155 h.not_mapped_error(repo_name)
156 h.not_mapped_error(repo_name)
156 return redirect(url('home'))
157 return redirect(url('home'))
157 try:
158 try:
158 action_logger(self.rhodecode_user, 'user_deleted_repo',
159 action_logger(self.rhodecode_user, 'user_deleted_repo',
159 repo_name, self.ip_addr, self.sa)
160 repo_name, self.ip_addr, self.sa)
160 repo_model.delete(repo)
161 repo_model.delete(repo)
161 invalidate_cache('get_repo_cached_%s' % repo_name)
162 invalidate_cache('get_repo_cached_%s' % repo_name)
162 h.flash(_('deleted repository %s') % repo_name, category='success')
163 h.flash(_('deleted repository %s') % repo_name, category='success')
163 Session().commit()
164 Session().commit()
164 except Exception:
165 except Exception:
165 log.error(traceback.format_exc())
166 log.error(traceback.format_exc())
166 h.flash(_('An error occurred during deletion of %s') % repo_name,
167 h.flash(_('An error occurred during deletion of %s') % repo_name,
167 category='error')
168 category='error')
168
169
169 return redirect(url('admin_settings_my_account', anchor='my'))
170 return redirect(url('admin_settings_my_account', anchor='my'))
170
171
171 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
172 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
172 def toggle_locking(self, repo_name):
173 def toggle_locking(self, repo_name):
173 """
174 """
174 Toggle locking of repository by simple GET call to url
175 Toggle locking of repository by simple GET call to url
175
176
176 :param repo_name:
177 :param repo_name:
177 """
178 """
178
179
179 try:
180 try:
180 repo = Repository.get_by_repo_name(repo_name)
181 repo = Repository.get_by_repo_name(repo_name)
181
182
182 if repo.enable_locking:
183 if repo.enable_locking:
183 if repo.locked[0]:
184 if repo.locked[0]:
184 Repository.unlock(repo)
185 Repository.unlock(repo)
185 action = _('unlocked')
186 action = _('unlocked')
186 else:
187 else:
187 Repository.lock(repo, c.rhodecode_user.user_id)
188 Repository.lock(repo, c.rhodecode_user.user_id)
188 action = _('locked')
189 action = _('locked')
189
190
190 h.flash(_('Repository has been %s') % action,
191 h.flash(_('Repository has been %s') % action,
191 category='success')
192 category='success')
192 except Exception, e:
193 except Exception, e:
193 log.error(traceback.format_exc())
194 log.error(traceback.format_exc())
194 h.flash(_('An error occurred during unlocking'),
195 h.flash(_('An error occurred during unlocking'),
195 category='error')
196 category='error')
196 return redirect(url('summary_home', repo_name=repo_name))
197 return redirect(url('summary_home', repo_name=repo_name))
@@ -1,1979 +1,1975 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, remove_prefix
49 safe_unicode, remove_suffix, remove_prefix
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 in ["ldap_active",
170 if self.app_settings_name in ["ldap_active",
171 "default_repo_enable_statistics",
171 "default_repo_enable_statistics",
172 "default_repo_enable_locking",
172 "default_repo_enable_locking",
173 "default_repo_private",
173 "default_repo_private",
174 "default_repo_enable_downloads"]:
174 "default_repo_enable_downloads"]:
175 v = str2bool(v)
175 v = str2bool(v)
176 return v
176 return v
177
177
178 @app_settings_value.setter
178 @app_settings_value.setter
179 def app_settings_value(self, val):
179 def app_settings_value(self, val):
180 """
180 """
181 Setter that will always make sure we use unicode in app_settings_value
181 Setter that will always make sure we use unicode in app_settings_value
182
182
183 :param val:
183 :param val:
184 """
184 """
185 self._app_settings_value = safe_unicode(val)
185 self._app_settings_value = safe_unicode(val)
186
186
187 def __unicode__(self):
187 def __unicode__(self):
188 return u"<%s('%s:%s')>" % (
188 return u"<%s('%s:%s')>" % (
189 self.__class__.__name__,
189 self.__class__.__name__,
190 self.app_settings_name, self.app_settings_value
190 self.app_settings_name, self.app_settings_value
191 )
191 )
192
192
193 @classmethod
193 @classmethod
194 def get_by_name(cls, key):
194 def get_by_name(cls, key):
195 return cls.query()\
195 return cls.query()\
196 .filter(cls.app_settings_name == key).scalar()
196 .filter(cls.app_settings_name == key).scalar()
197
197
198 @classmethod
198 @classmethod
199 def get_by_name_or_create(cls, key):
199 def get_by_name_or_create(cls, key):
200 res = cls.get_by_name(key)
200 res = cls.get_by_name(key)
201 if not res:
201 if not res:
202 res = cls(key)
202 res = cls(key)
203 return res
203 return res
204
204
205 @classmethod
205 @classmethod
206 def get_app_settings(cls, cache=False):
206 def get_app_settings(cls, cache=False):
207
207
208 ret = cls.query()
208 ret = cls.query()
209
209
210 if cache:
210 if cache:
211 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
211 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
212
212
213 if not ret:
213 if not ret:
214 raise Exception('Could not get application settings !')
214 raise Exception('Could not get application settings !')
215 settings = {}
215 settings = {}
216 for each in ret:
216 for each in ret:
217 settings['rhodecode_' + each.app_settings_name] = \
217 settings['rhodecode_' + each.app_settings_name] = \
218 each.app_settings_value
218 each.app_settings_value
219
219
220 return settings
220 return settings
221
221
222 @classmethod
222 @classmethod
223 def get_ldap_settings(cls, cache=False):
223 def get_ldap_settings(cls, cache=False):
224 ret = cls.query()\
224 ret = cls.query()\
225 .filter(cls.app_settings_name.startswith('ldap_')).all()
225 .filter(cls.app_settings_name.startswith('ldap_')).all()
226 fd = {}
226 fd = {}
227 for row in ret:
227 for row in ret:
228 fd.update({row.app_settings_name: row.app_settings_value})
228 fd.update({row.app_settings_name: row.app_settings_value})
229
229
230 return fd
230 return fd
231
231
232 @classmethod
232 @classmethod
233 def get_default_repo_settings(cls, cache=False, strip_prefix=False):
233 def get_default_repo_settings(cls, cache=False, strip_prefix=False):
234 ret = cls.query()\
234 ret = cls.query()\
235 .filter(cls.app_settings_name.startswith('default_')).all()
235 .filter(cls.app_settings_name.startswith('default_')).all()
236 fd = {}
236 fd = {}
237 for row in ret:
237 for row in ret:
238 key = row.app_settings_name
238 key = row.app_settings_name
239 if strip_prefix:
239 if strip_prefix:
240 key = remove_prefix(key, prefix='default_')
240 key = remove_prefix(key, prefix='default_')
241 fd.update({key: row.app_settings_value})
241 fd.update({key: row.app_settings_value})
242
242
243 return fd
243 return fd
244
244
245
245
246 class RhodeCodeUi(Base, BaseModel):
246 class RhodeCodeUi(Base, BaseModel):
247 __tablename__ = 'rhodecode_ui'
247 __tablename__ = 'rhodecode_ui'
248 __table_args__ = (
248 __table_args__ = (
249 UniqueConstraint('ui_key'),
249 UniqueConstraint('ui_key'),
250 {'extend_existing': True, 'mysql_engine': 'InnoDB',
250 {'extend_existing': True, 'mysql_engine': 'InnoDB',
251 'mysql_charset': 'utf8'}
251 'mysql_charset': 'utf8'}
252 )
252 )
253
253
254 HOOK_UPDATE = 'changegroup.update'
254 HOOK_UPDATE = 'changegroup.update'
255 HOOK_REPO_SIZE = 'changegroup.repo_size'
255 HOOK_REPO_SIZE = 'changegroup.repo_size'
256 HOOK_PUSH = 'changegroup.push_logger'
256 HOOK_PUSH = 'changegroup.push_logger'
257 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
257 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
258 HOOK_PULL = 'outgoing.pull_logger'
258 HOOK_PULL = 'outgoing.pull_logger'
259 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
259 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
260
260
261 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
261 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
262 ui_section = Column("ui_section", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
262 ui_section = Column("ui_section", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
263 ui_key = Column("ui_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
263 ui_key = Column("ui_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
264 ui_value = Column("ui_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
264 ui_value = Column("ui_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
265 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
265 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
266
266
267 @classmethod
267 @classmethod
268 def get_by_key(cls, key):
268 def get_by_key(cls, key):
269 return cls.query().filter(cls.ui_key == key).scalar()
269 return cls.query().filter(cls.ui_key == key).scalar()
270
270
271 @classmethod
271 @classmethod
272 def get_builtin_hooks(cls):
272 def get_builtin_hooks(cls):
273 q = cls.query()
273 q = cls.query()
274 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
274 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
275 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
275 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
276 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
276 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
277 return q.all()
277 return q.all()
278
278
279 @classmethod
279 @classmethod
280 def get_custom_hooks(cls):
280 def get_custom_hooks(cls):
281 q = cls.query()
281 q = cls.query()
282 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
282 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
283 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
283 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
284 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
284 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
285 q = q.filter(cls.ui_section == 'hooks')
285 q = q.filter(cls.ui_section == 'hooks')
286 return q.all()
286 return q.all()
287
287
288 @classmethod
288 @classmethod
289 def get_repos_location(cls):
289 def get_repos_location(cls):
290 return cls.get_by_key('/').ui_value
290 return cls.get_by_key('/').ui_value
291
291
292 @classmethod
292 @classmethod
293 def create_or_update_hook(cls, key, val):
293 def create_or_update_hook(cls, key, val):
294 new_ui = cls.get_by_key(key) or cls()
294 new_ui = cls.get_by_key(key) or cls()
295 new_ui.ui_section = 'hooks'
295 new_ui.ui_section = 'hooks'
296 new_ui.ui_active = True
296 new_ui.ui_active = True
297 new_ui.ui_key = key
297 new_ui.ui_key = key
298 new_ui.ui_value = val
298 new_ui.ui_value = val
299
299
300 Session().add(new_ui)
300 Session().add(new_ui)
301
301
302 def __repr__(self):
302 def __repr__(self):
303 return '<DB:%s[%s:%s]>' % (self.__class__.__name__, self.ui_key,
303 return '<DB:%s[%s:%s]>' % (self.__class__.__name__, self.ui_key,
304 self.ui_value)
304 self.ui_value)
305
305
306
306
307 class User(Base, BaseModel):
307 class User(Base, BaseModel):
308 __tablename__ = 'users'
308 __tablename__ = 'users'
309 __table_args__ = (
309 __table_args__ = (
310 UniqueConstraint('username'), UniqueConstraint('email'),
310 UniqueConstraint('username'), UniqueConstraint('email'),
311 Index('u_username_idx', 'username'),
311 Index('u_username_idx', 'username'),
312 Index('u_email_idx', 'email'),
312 Index('u_email_idx', 'email'),
313 {'extend_existing': True, 'mysql_engine': 'InnoDB',
313 {'extend_existing': True, 'mysql_engine': 'InnoDB',
314 'mysql_charset': 'utf8'}
314 'mysql_charset': 'utf8'}
315 )
315 )
316 DEFAULT_USER = 'default'
316 DEFAULT_USER = 'default'
317 DEFAULT_PERMISSIONS = [
317 DEFAULT_PERMISSIONS = [
318 'hg.register.manual_activate', 'hg.create.repository',
318 'hg.register.manual_activate', 'hg.create.repository',
319 'hg.fork.repository', 'repository.read', 'group.read'
319 'hg.fork.repository', 'repository.read', 'group.read'
320 ]
320 ]
321 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
321 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
322 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
322 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
323 password = Column("password", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
323 password = Column("password", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
324 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
324 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
325 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
325 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
326 name = Column("firstname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
326 name = Column("firstname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
327 lastname = Column("lastname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
327 lastname = Column("lastname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
328 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
328 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
329 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
329 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
330 ldap_dn = Column("ldap_dn", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
330 ldap_dn = Column("ldap_dn", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
331 api_key = Column("api_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
331 api_key = Column("api_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
332 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
332 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
333
333
334 user_log = relationship('UserLog')
334 user_log = relationship('UserLog')
335 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
335 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
336
336
337 repositories = relationship('Repository')
337 repositories = relationship('Repository')
338 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
338 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
339 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
339 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
340
340
341 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
341 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
342 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
342 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
343
343
344 group_member = relationship('UsersGroupMember', cascade='all')
344 group_member = relationship('UsersGroupMember', cascade='all')
345
345
346 notifications = relationship('UserNotification', cascade='all')
346 notifications = relationship('UserNotification', cascade='all')
347 # notifications assigned to this user
347 # notifications assigned to this user
348 user_created_notifications = relationship('Notification', cascade='all')
348 user_created_notifications = relationship('Notification', cascade='all')
349 # comments created by this user
349 # comments created by this user
350 user_comments = relationship('ChangesetComment', cascade='all')
350 user_comments = relationship('ChangesetComment', cascade='all')
351 #extra emails for this user
351 #extra emails for this user
352 user_emails = relationship('UserEmailMap', cascade='all')
352 user_emails = relationship('UserEmailMap', cascade='all')
353
353
354 @hybrid_property
354 @hybrid_property
355 def email(self):
355 def email(self):
356 return self._email
356 return self._email
357
357
358 @email.setter
358 @email.setter
359 def email(self, val):
359 def email(self, val):
360 self._email = val.lower() if val else None
360 self._email = val.lower() if val else None
361
361
362 @property
362 @property
363 def firstname(self):
363 def firstname(self):
364 # alias for future
364 # alias for future
365 return self.name
365 return self.name
366
366
367 @property
367 @property
368 def emails(self):
368 def emails(self):
369 other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
369 other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
370 return [self.email] + [x.email for x in other]
370 return [self.email] + [x.email for x in other]
371
371
372 @property
372 @property
373 def ip_addresses(self):
373 def ip_addresses(self):
374 ret = UserIpMap.query().filter(UserIpMap.user == self).all()
374 ret = UserIpMap.query().filter(UserIpMap.user == self).all()
375 return [x.ip_addr for x in ret]
375 return [x.ip_addr for x in ret]
376
376
377 @property
377 @property
378 def username_and_name(self):
378 def username_and_name(self):
379 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
379 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
380
380
381 @property
381 @property
382 def full_name(self):
382 def full_name(self):
383 return '%s %s' % (self.firstname, self.lastname)
383 return '%s %s' % (self.firstname, self.lastname)
384
384
385 @property
385 @property
386 def full_name_or_username(self):
386 def full_name_or_username(self):
387 return ('%s %s' % (self.firstname, self.lastname)
387 return ('%s %s' % (self.firstname, self.lastname)
388 if (self.firstname and self.lastname) else self.username)
388 if (self.firstname and self.lastname) else self.username)
389
389
390 @property
390 @property
391 def full_contact(self):
391 def full_contact(self):
392 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
392 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
393
393
394 @property
394 @property
395 def short_contact(self):
395 def short_contact(self):
396 return '%s %s' % (self.firstname, self.lastname)
396 return '%s %s' % (self.firstname, self.lastname)
397
397
398 @property
398 @property
399 def is_admin(self):
399 def is_admin(self):
400 return self.admin
400 return self.admin
401
401
402 def __unicode__(self):
402 def __unicode__(self):
403 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
403 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
404 self.user_id, self.username)
404 self.user_id, self.username)
405
405
406 @classmethod
406 @classmethod
407 def get_by_username(cls, username, case_insensitive=False, cache=False):
407 def get_by_username(cls, username, case_insensitive=False, cache=False):
408 if case_insensitive:
408 if case_insensitive:
409 q = cls.query().filter(cls.username.ilike(username))
409 q = cls.query().filter(cls.username.ilike(username))
410 else:
410 else:
411 q = cls.query().filter(cls.username == username)
411 q = cls.query().filter(cls.username == username)
412
412
413 if cache:
413 if cache:
414 q = q.options(FromCache(
414 q = q.options(FromCache(
415 "sql_cache_short",
415 "sql_cache_short",
416 "get_user_%s" % _hash_key(username)
416 "get_user_%s" % _hash_key(username)
417 )
417 )
418 )
418 )
419 return q.scalar()
419 return q.scalar()
420
420
421 @classmethod
421 @classmethod
422 def get_by_api_key(cls, api_key, cache=False):
422 def get_by_api_key(cls, api_key, cache=False):
423 q = cls.query().filter(cls.api_key == api_key)
423 q = cls.query().filter(cls.api_key == api_key)
424
424
425 if cache:
425 if cache:
426 q = q.options(FromCache("sql_cache_short",
426 q = q.options(FromCache("sql_cache_short",
427 "get_api_key_%s" % api_key))
427 "get_api_key_%s" % api_key))
428 return q.scalar()
428 return q.scalar()
429
429
430 @classmethod
430 @classmethod
431 def get_by_email(cls, email, case_insensitive=False, cache=False):
431 def get_by_email(cls, email, case_insensitive=False, cache=False):
432 if case_insensitive:
432 if case_insensitive:
433 q = cls.query().filter(cls.email.ilike(email))
433 q = cls.query().filter(cls.email.ilike(email))
434 else:
434 else:
435 q = cls.query().filter(cls.email == email)
435 q = cls.query().filter(cls.email == email)
436
436
437 if cache:
437 if cache:
438 q = q.options(FromCache("sql_cache_short",
438 q = q.options(FromCache("sql_cache_short",
439 "get_email_key_%s" % email))
439 "get_email_key_%s" % email))
440
440
441 ret = q.scalar()
441 ret = q.scalar()
442 if ret is None:
442 if ret is None:
443 q = UserEmailMap.query()
443 q = UserEmailMap.query()
444 # try fetching in alternate email map
444 # try fetching in alternate email map
445 if case_insensitive:
445 if case_insensitive:
446 q = q.filter(UserEmailMap.email.ilike(email))
446 q = q.filter(UserEmailMap.email.ilike(email))
447 else:
447 else:
448 q = q.filter(UserEmailMap.email == email)
448 q = q.filter(UserEmailMap.email == email)
449 q = q.options(joinedload(UserEmailMap.user))
449 q = q.options(joinedload(UserEmailMap.user))
450 if cache:
450 if cache:
451 q = q.options(FromCache("sql_cache_short",
451 q = q.options(FromCache("sql_cache_short",
452 "get_email_map_key_%s" % email))
452 "get_email_map_key_%s" % email))
453 ret = getattr(q.scalar(), 'user', None)
453 ret = getattr(q.scalar(), 'user', None)
454
454
455 return ret
455 return ret
456
456
457 @classmethod
457 @classmethod
458 def get_from_cs_author(cls, author):
458 def get_from_cs_author(cls, author):
459 """
459 """
460 Tries to get User objects out of commit author string
460 Tries to get User objects out of commit author string
461
461
462 :param author:
462 :param author:
463 """
463 """
464 from rhodecode.lib.helpers import email, author_name
464 from rhodecode.lib.helpers import email, author_name
465 # Valid email in the attribute passed, see if they're in the system
465 # Valid email in the attribute passed, see if they're in the system
466 _email = email(author)
466 _email = email(author)
467 if _email:
467 if _email:
468 user = cls.get_by_email(_email, case_insensitive=True)
468 user = cls.get_by_email(_email, case_insensitive=True)
469 if user:
469 if user:
470 return user
470 return user
471 # Maybe we can match by username?
471 # Maybe we can match by username?
472 _author = author_name(author)
472 _author = author_name(author)
473 user = cls.get_by_username(_author, case_insensitive=True)
473 user = cls.get_by_username(_author, case_insensitive=True)
474 if user:
474 if user:
475 return user
475 return user
476
476
477 def update_lastlogin(self):
477 def update_lastlogin(self):
478 """Update user lastlogin"""
478 """Update user lastlogin"""
479 self.last_login = datetime.datetime.now()
479 self.last_login = datetime.datetime.now()
480 Session().add(self)
480 Session().add(self)
481 log.debug('updated user %s lastlogin' % self.username)
481 log.debug('updated user %s lastlogin' % self.username)
482
482
483 def get_api_data(self):
483 def get_api_data(self):
484 """
484 """
485 Common function for generating user related data for API
485 Common function for generating user related data for API
486 """
486 """
487 user = self
487 user = self
488 data = dict(
488 data = dict(
489 user_id=user.user_id,
489 user_id=user.user_id,
490 username=user.username,
490 username=user.username,
491 firstname=user.name,
491 firstname=user.name,
492 lastname=user.lastname,
492 lastname=user.lastname,
493 email=user.email,
493 email=user.email,
494 emails=user.emails,
494 emails=user.emails,
495 api_key=user.api_key,
495 api_key=user.api_key,
496 active=user.active,
496 active=user.active,
497 admin=user.admin,
497 admin=user.admin,
498 ldap_dn=user.ldap_dn,
498 ldap_dn=user.ldap_dn,
499 last_login=user.last_login,
499 last_login=user.last_login,
500 ip_addresses=user.ip_addresses
500 ip_addresses=user.ip_addresses
501 )
501 )
502 return data
502 return data
503
503
504 def __json__(self):
504 def __json__(self):
505 data = dict(
505 data = dict(
506 full_name=self.full_name,
506 full_name=self.full_name,
507 full_name_or_username=self.full_name_or_username,
507 full_name_or_username=self.full_name_or_username,
508 short_contact=self.short_contact,
508 short_contact=self.short_contact,
509 full_contact=self.full_contact
509 full_contact=self.full_contact
510 )
510 )
511 data.update(self.get_api_data())
511 data.update(self.get_api_data())
512 return data
512 return data
513
513
514
514
515 class UserEmailMap(Base, BaseModel):
515 class UserEmailMap(Base, BaseModel):
516 __tablename__ = 'user_email_map'
516 __tablename__ = 'user_email_map'
517 __table_args__ = (
517 __table_args__ = (
518 Index('uem_email_idx', 'email'),
518 Index('uem_email_idx', 'email'),
519 UniqueConstraint('email'),
519 UniqueConstraint('email'),
520 {'extend_existing': True, 'mysql_engine': 'InnoDB',
520 {'extend_existing': True, 'mysql_engine': 'InnoDB',
521 'mysql_charset': 'utf8'}
521 'mysql_charset': 'utf8'}
522 )
522 )
523 __mapper_args__ = {}
523 __mapper_args__ = {}
524
524
525 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
525 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
526 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
526 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
527 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
527 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
528 user = relationship('User', lazy='joined')
528 user = relationship('User', lazy='joined')
529
529
530 @validates('_email')
530 @validates('_email')
531 def validate_email(self, key, email):
531 def validate_email(self, key, email):
532 # check if this email is not main one
532 # check if this email is not main one
533 main_email = Session().query(User).filter(User.email == email).scalar()
533 main_email = Session().query(User).filter(User.email == email).scalar()
534 if main_email is not None:
534 if main_email is not None:
535 raise AttributeError('email %s is present is user table' % email)
535 raise AttributeError('email %s is present is user table' % email)
536 return email
536 return email
537
537
538 @hybrid_property
538 @hybrid_property
539 def email(self):
539 def email(self):
540 return self._email
540 return self._email
541
541
542 @email.setter
542 @email.setter
543 def email(self, val):
543 def email(self, val):
544 self._email = val.lower() if val else None
544 self._email = val.lower() if val else None
545
545
546
546
547 class UserIpMap(Base, BaseModel):
547 class UserIpMap(Base, BaseModel):
548 __tablename__ = 'user_ip_map'
548 __tablename__ = 'user_ip_map'
549 __table_args__ = (
549 __table_args__ = (
550 UniqueConstraint('user_id', 'ip_addr'),
550 UniqueConstraint('user_id', 'ip_addr'),
551 {'extend_existing': True, 'mysql_engine': 'InnoDB',
551 {'extend_existing': True, 'mysql_engine': 'InnoDB',
552 'mysql_charset': 'utf8'}
552 'mysql_charset': 'utf8'}
553 )
553 )
554 __mapper_args__ = {}
554 __mapper_args__ = {}
555
555
556 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
556 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
557 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
557 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
558 ip_addr = Column("ip_addr", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
558 ip_addr = Column("ip_addr", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
559 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
559 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
560 user = relationship('User', lazy='joined')
560 user = relationship('User', lazy='joined')
561
561
562 @classmethod
562 @classmethod
563 def _get_ip_range(cls, ip_addr):
563 def _get_ip_range(cls, ip_addr):
564 from rhodecode.lib import ipaddr
564 from rhodecode.lib import ipaddr
565 net = ipaddr.IPNetwork(address=ip_addr)
565 net = ipaddr.IPNetwork(address=ip_addr)
566 return [str(net.network), str(net.broadcast)]
566 return [str(net.network), str(net.broadcast)]
567
567
568 def __json__(self):
568 def __json__(self):
569 return dict(
569 return dict(
570 ip_addr=self.ip_addr,
570 ip_addr=self.ip_addr,
571 ip_range=self._get_ip_range(self.ip_addr)
571 ip_range=self._get_ip_range(self.ip_addr)
572 )
572 )
573
573
574
574
575 class UserLog(Base, BaseModel):
575 class UserLog(Base, BaseModel):
576 __tablename__ = 'user_logs'
576 __tablename__ = 'user_logs'
577 __table_args__ = (
577 __table_args__ = (
578 {'extend_existing': True, 'mysql_engine': 'InnoDB',
578 {'extend_existing': True, 'mysql_engine': 'InnoDB',
579 'mysql_charset': 'utf8'},
579 'mysql_charset': 'utf8'},
580 )
580 )
581 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
581 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
582 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
582 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
583 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
583 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
584 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
584 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
585 repository_name = Column("repository_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
585 repository_name = Column("repository_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
586 user_ip = Column("user_ip", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
586 user_ip = Column("user_ip", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
587 action = Column("action", UnicodeText(1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
587 action = Column("action", UnicodeText(1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
588 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
588 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
589
589
590 @property
590 @property
591 def action_as_day(self):
591 def action_as_day(self):
592 return datetime.date(*self.action_date.timetuple()[:3])
592 return datetime.date(*self.action_date.timetuple()[:3])
593
593
594 user = relationship('User')
594 user = relationship('User')
595 repository = relationship('Repository', cascade='')
595 repository = relationship('Repository', cascade='')
596
596
597
597
598 class UsersGroup(Base, BaseModel):
598 class UsersGroup(Base, BaseModel):
599 __tablename__ = 'users_groups'
599 __tablename__ = 'users_groups'
600 __table_args__ = (
600 __table_args__ = (
601 {'extend_existing': True, 'mysql_engine': 'InnoDB',
601 {'extend_existing': True, 'mysql_engine': 'InnoDB',
602 'mysql_charset': 'utf8'},
602 'mysql_charset': 'utf8'},
603 )
603 )
604
604
605 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
605 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
606 users_group_name = Column("users_group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
606 users_group_name = Column("users_group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
607 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
607 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
608 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
608 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
609
609
610 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
610 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
611 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
611 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
612 users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
612 users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
613
613
614 def __unicode__(self):
614 def __unicode__(self):
615 return u'<userGroup(%s)>' % (self.users_group_name)
615 return u'<userGroup(%s)>' % (self.users_group_name)
616
616
617 @classmethod
617 @classmethod
618 def get_by_group_name(cls, group_name, cache=False,
618 def get_by_group_name(cls, group_name, cache=False,
619 case_insensitive=False):
619 case_insensitive=False):
620 if case_insensitive:
620 if case_insensitive:
621 q = cls.query().filter(cls.users_group_name.ilike(group_name))
621 q = cls.query().filter(cls.users_group_name.ilike(group_name))
622 else:
622 else:
623 q = cls.query().filter(cls.users_group_name == group_name)
623 q = cls.query().filter(cls.users_group_name == group_name)
624 if cache:
624 if cache:
625 q = q.options(FromCache(
625 q = q.options(FromCache(
626 "sql_cache_short",
626 "sql_cache_short",
627 "get_user_%s" % _hash_key(group_name)
627 "get_user_%s" % _hash_key(group_name)
628 )
628 )
629 )
629 )
630 return q.scalar()
630 return q.scalar()
631
631
632 @classmethod
632 @classmethod
633 def get(cls, users_group_id, cache=False):
633 def get(cls, users_group_id, cache=False):
634 users_group = cls.query()
634 users_group = cls.query()
635 if cache:
635 if cache:
636 users_group = users_group.options(FromCache("sql_cache_short",
636 users_group = users_group.options(FromCache("sql_cache_short",
637 "get_users_group_%s" % users_group_id))
637 "get_users_group_%s" % users_group_id))
638 return users_group.get(users_group_id)
638 return users_group.get(users_group_id)
639
639
640 def get_api_data(self):
640 def get_api_data(self):
641 users_group = self
641 users_group = self
642
642
643 data = dict(
643 data = dict(
644 users_group_id=users_group.users_group_id,
644 users_group_id=users_group.users_group_id,
645 group_name=users_group.users_group_name,
645 group_name=users_group.users_group_name,
646 active=users_group.users_group_active,
646 active=users_group.users_group_active,
647 )
647 )
648
648
649 return data
649 return data
650
650
651
651
652 class UsersGroupMember(Base, BaseModel):
652 class UsersGroupMember(Base, BaseModel):
653 __tablename__ = 'users_groups_members'
653 __tablename__ = 'users_groups_members'
654 __table_args__ = (
654 __table_args__ = (
655 {'extend_existing': True, 'mysql_engine': 'InnoDB',
655 {'extend_existing': True, 'mysql_engine': 'InnoDB',
656 'mysql_charset': 'utf8'},
656 'mysql_charset': 'utf8'},
657 )
657 )
658
658
659 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
659 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
660 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
660 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
661 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
661 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
662
662
663 user = relationship('User', lazy='joined')
663 user = relationship('User', lazy='joined')
664 users_group = relationship('UsersGroup')
664 users_group = relationship('UsersGroup')
665
665
666 def __init__(self, gr_id='', u_id=''):
666 def __init__(self, gr_id='', u_id=''):
667 self.users_group_id = gr_id
667 self.users_group_id = gr_id
668 self.user_id = u_id
668 self.user_id = u_id
669
669
670
670
671 class Repository(Base, BaseModel):
671 class Repository(Base, BaseModel):
672 __tablename__ = 'repositories'
672 __tablename__ = 'repositories'
673 __table_args__ = (
673 __table_args__ = (
674 UniqueConstraint('repo_name'),
674 UniqueConstraint('repo_name'),
675 Index('r_repo_name_idx', 'repo_name'),
675 Index('r_repo_name_idx', 'repo_name'),
676 {'extend_existing': True, 'mysql_engine': 'InnoDB',
676 {'extend_existing': True, 'mysql_engine': 'InnoDB',
677 'mysql_charset': 'utf8'},
677 'mysql_charset': 'utf8'},
678 )
678 )
679
679
680 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
680 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
681 repo_name = Column("repo_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
681 repo_name = Column("repo_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
682 clone_uri = Column("clone_uri", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
682 clone_uri = Column("clone_uri", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
683 repo_type = Column("repo_type", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
683 repo_type = Column("repo_type", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
684 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
684 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
685 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
685 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
686 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
686 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
687 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
687 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
688 description = Column("description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
688 description = Column("description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
689 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
689 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
690 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
690 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
691 landing_rev = Column("landing_revision", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
691 landing_rev = Column("landing_revision", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
692 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
692 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
693 _locked = Column("locked", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
693 _locked = Column("locked", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
694 _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) #JSON data
694 _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) #JSON data
695
695
696 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
696 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
697 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
697 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
698
698
699 user = relationship('User')
699 user = relationship('User')
700 fork = relationship('Repository', remote_side=repo_id)
700 fork = relationship('Repository', remote_side=repo_id)
701 group = relationship('RepoGroup')
701 group = relationship('RepoGroup')
702 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
702 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
703 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
703 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
704 stats = relationship('Statistics', cascade='all', uselist=False)
704 stats = relationship('Statistics', cascade='all', uselist=False)
705
705
706 followers = relationship('UserFollowing',
706 followers = relationship('UserFollowing',
707 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
707 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
708 cascade='all')
708 cascade='all')
709
709
710 logs = relationship('UserLog')
710 logs = relationship('UserLog')
711 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
711 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
712
712
713 pull_requests_org = relationship('PullRequest',
713 pull_requests_org = relationship('PullRequest',
714 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
714 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
715 cascade="all, delete, delete-orphan")
715 cascade="all, delete, delete-orphan")
716
716
717 pull_requests_other = relationship('PullRequest',
717 pull_requests_other = relationship('PullRequest',
718 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
718 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
719 cascade="all, delete, delete-orphan")
719 cascade="all, delete, delete-orphan")
720
720
721 def __unicode__(self):
721 def __unicode__(self):
722 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
722 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
723 self.repo_name)
723 self.repo_name)
724
724
725 @hybrid_property
725 @hybrid_property
726 def locked(self):
726 def locked(self):
727 # always should return [user_id, timelocked]
727 # always should return [user_id, timelocked]
728 if self._locked:
728 if self._locked:
729 _lock_info = self._locked.split(':')
729 _lock_info = self._locked.split(':')
730 return int(_lock_info[0]), _lock_info[1]
730 return int(_lock_info[0]), _lock_info[1]
731 return [None, None]
731 return [None, None]
732
732
733 @locked.setter
733 @locked.setter
734 def locked(self, val):
734 def locked(self, val):
735 if val and isinstance(val, (list, tuple)):
735 if val and isinstance(val, (list, tuple)):
736 self._locked = ':'.join(map(str, val))
736 self._locked = ':'.join(map(str, val))
737 else:
737 else:
738 self._locked = None
738 self._locked = None
739
739
740 @hybrid_property
740 @hybrid_property
741 def changeset_cache(self):
741 def changeset_cache(self):
742 from rhodecode.lib.vcs.backends.base import EmptyChangeset
742 from rhodecode.lib.vcs.backends.base import EmptyChangeset
743 dummy = EmptyChangeset().__json__()
743 dummy = EmptyChangeset().__json__()
744 if not self._changeset_cache:
744 if not self._changeset_cache:
745 return dummy
745 return dummy
746 try:
746 try:
747 return json.loads(self._changeset_cache)
747 return json.loads(self._changeset_cache)
748 except TypeError:
748 except TypeError:
749 return dummy
749 return dummy
750
750
751 @changeset_cache.setter
751 @changeset_cache.setter
752 def changeset_cache(self, val):
752 def changeset_cache(self, val):
753 try:
753 try:
754 self._changeset_cache = json.dumps(val)
754 self._changeset_cache = json.dumps(val)
755 except:
755 except:
756 log.error(traceback.format_exc())
756 log.error(traceback.format_exc())
757
757
758 @classmethod
758 @classmethod
759 def url_sep(cls):
759 def url_sep(cls):
760 return URL_SEP
760 return URL_SEP
761
761
762 @classmethod
762 @classmethod
763 def normalize_repo_name(cls, repo_name):
763 def normalize_repo_name(cls, repo_name):
764 """
764 """
765 Normalizes os specific repo_name to the format internally stored inside
765 Normalizes os specific repo_name to the format internally stored inside
766 dabatabase using URL_SEP
766 dabatabase using URL_SEP
767
767
768 :param cls:
768 :param cls:
769 :param repo_name:
769 :param repo_name:
770 """
770 """
771 return cls.url_sep().join(repo_name.split(os.sep))
771 return cls.url_sep().join(repo_name.split(os.sep))
772
772
773 @classmethod
773 @classmethod
774 def get_by_repo_name(cls, repo_name):
774 def get_by_repo_name(cls, repo_name):
775 q = Session().query(cls).filter(cls.repo_name == repo_name)
775 q = Session().query(cls).filter(cls.repo_name == repo_name)
776 q = q.options(joinedload(Repository.fork))\
776 q = q.options(joinedload(Repository.fork))\
777 .options(joinedload(Repository.user))\
777 .options(joinedload(Repository.user))\
778 .options(joinedload(Repository.group))
778 .options(joinedload(Repository.group))
779 return q.scalar()
779 return q.scalar()
780
780
781 @classmethod
781 @classmethod
782 def get_by_full_path(cls, repo_full_path):
782 def get_by_full_path(cls, repo_full_path):
783 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
783 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
784 repo_name = cls.normalize_repo_name(repo_name)
784 repo_name = cls.normalize_repo_name(repo_name)
785 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
785 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
786
786
787 @classmethod
787 @classmethod
788 def get_repo_forks(cls, repo_id):
788 def get_repo_forks(cls, repo_id):
789 return cls.query().filter(Repository.fork_id == repo_id)
789 return cls.query().filter(Repository.fork_id == repo_id)
790
790
791 @classmethod
791 @classmethod
792 def base_path(cls):
792 def base_path(cls):
793 """
793 """
794 Returns base path when all repos are stored
794 Returns base path when all repos are stored
795
795
796 :param cls:
796 :param cls:
797 """
797 """
798 q = Session().query(RhodeCodeUi)\
798 q = Session().query(RhodeCodeUi)\
799 .filter(RhodeCodeUi.ui_key == cls.url_sep())
799 .filter(RhodeCodeUi.ui_key == cls.url_sep())
800 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
800 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
801 return q.one().ui_value
801 return q.one().ui_value
802
802
803 @property
803 @property
804 def forks(self):
804 def forks(self):
805 """
805 """
806 Return forks of this repo
806 Return forks of this repo
807 """
807 """
808 return Repository.get_repo_forks(self.repo_id)
808 return Repository.get_repo_forks(self.repo_id)
809
809
810 @property
810 @property
811 def parent(self):
811 def parent(self):
812 """
812 """
813 Returns fork parent
813 Returns fork parent
814 """
814 """
815 return self.fork
815 return self.fork
816
816
817 @property
817 @property
818 def just_name(self):
818 def just_name(self):
819 return self.repo_name.split(Repository.url_sep())[-1]
819 return self.repo_name.split(Repository.url_sep())[-1]
820
820
821 @property
821 @property
822 def groups_with_parents(self):
822 def groups_with_parents(self):
823 groups = []
823 groups = []
824 if self.group is None:
824 if self.group is None:
825 return groups
825 return groups
826
826
827 cur_gr = self.group
827 cur_gr = self.group
828 groups.insert(0, cur_gr)
828 groups.insert(0, cur_gr)
829 while 1:
829 while 1:
830 gr = getattr(cur_gr, 'parent_group', None)
830 gr = getattr(cur_gr, 'parent_group', None)
831 cur_gr = cur_gr.parent_group
831 cur_gr = cur_gr.parent_group
832 if gr is None:
832 if gr is None:
833 break
833 break
834 groups.insert(0, gr)
834 groups.insert(0, gr)
835
835
836 return groups
836 return groups
837
837
838 @property
838 @property
839 def groups_and_repo(self):
839 def groups_and_repo(self):
840 return self.groups_with_parents, self.just_name
840 return self.groups_with_parents, self.just_name
841
841
842 @LazyProperty
842 @LazyProperty
843 def repo_path(self):
843 def repo_path(self):
844 """
844 """
845 Returns base full path for that repository means where it actually
845 Returns base full path for that repository means where it actually
846 exists on a filesystem
846 exists on a filesystem
847 """
847 """
848 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
848 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
849 Repository.url_sep())
849 Repository.url_sep())
850 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
850 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
851 return q.one().ui_value
851 return q.one().ui_value
852
852
853 @property
853 @property
854 def repo_full_path(self):
854 def repo_full_path(self):
855 p = [self.repo_path]
855 p = [self.repo_path]
856 # we need to split the name by / since this is how we store the
856 # we need to split the name by / since this is how we store the
857 # names in the database, but that eventually needs to be converted
857 # names in the database, but that eventually needs to be converted
858 # into a valid system path
858 # into a valid system path
859 p += self.repo_name.split(Repository.url_sep())
859 p += self.repo_name.split(Repository.url_sep())
860 return os.path.join(*p)
860 return os.path.join(*p)
861
861
862 @property
862 @property
863 def cache_keys(self):
863 def cache_keys(self):
864 """
864 """
865 Returns associated cache keys for that repo
865 Returns associated cache keys for that repo
866 """
866 """
867 return CacheInvalidation.query()\
867 return CacheInvalidation.query()\
868 .filter(CacheInvalidation.cache_args == self.repo_name)\
868 .filter(CacheInvalidation.cache_args == self.repo_name)\
869 .order_by(CacheInvalidation.cache_key)\
869 .order_by(CacheInvalidation.cache_key)\
870 .all()
870 .all()
871
871
872 def get_new_name(self, repo_name):
872 def get_new_name(self, repo_name):
873 """
873 """
874 returns new full repository name based on assigned group and new new
874 returns new full repository name based on assigned group and new new
875
875
876 :param group_name:
876 :param group_name:
877 """
877 """
878 path_prefix = self.group.full_path_splitted if self.group else []
878 path_prefix = self.group.full_path_splitted if self.group else []
879 return Repository.url_sep().join(path_prefix + [repo_name])
879 return Repository.url_sep().join(path_prefix + [repo_name])
880
880
881 @property
881 @property
882 def _ui(self):
882 def _ui(self):
883 """
883 """
884 Creates an db based ui object for this repository
884 Creates an db based ui object for this repository
885 """
885 """
886 from rhodecode.lib.utils import make_ui
886 from rhodecode.lib.utils import make_ui
887 return make_ui('db', clear_session=False)
887 return make_ui('db', clear_session=False)
888
888
889 @classmethod
889 @classmethod
890 def inject_ui(cls, repo, extras={}):
890 def inject_ui(cls, repo, extras={}):
891 from rhodecode.lib.vcs.backends.hg import MercurialRepository
891 from rhodecode.lib.vcs.backends.hg import MercurialRepository
892 from rhodecode.lib.vcs.backends.git import GitRepository
892 from rhodecode.lib.vcs.backends.git import GitRepository
893 required = (MercurialRepository, GitRepository)
893 required = (MercurialRepository, GitRepository)
894 if not isinstance(repo, required):
894 if not isinstance(repo, required):
895 raise Exception('repo must be instance of %s' % required)
895 raise Exception('repo must be instance of %s' % required)
896
896
897 # inject ui extra param to log this action via push logger
897 # inject ui extra param to log this action via push logger
898 for k, v in extras.items():
898 for k, v in extras.items():
899 repo._repo.ui.setconfig('rhodecode_extras', k, v)
899 repo._repo.ui.setconfig('rhodecode_extras', k, v)
900
900
901 @classmethod
901 @classmethod
902 def is_valid(cls, repo_name):
902 def is_valid(cls, repo_name):
903 """
903 """
904 returns True if given repo name is a valid filesystem repository
904 returns True if given repo name is a valid filesystem repository
905
905
906 :param cls:
906 :param cls:
907 :param repo_name:
907 :param repo_name:
908 """
908 """
909 from rhodecode.lib.utils import is_valid_repo
909 from rhodecode.lib.utils import is_valid_repo
910
910
911 return is_valid_repo(repo_name, cls.base_path())
911 return is_valid_repo(repo_name, cls.base_path())
912
912
913 def get_api_data(self):
913 def get_api_data(self):
914 """
914 """
915 Common function for generating repo api data
915 Common function for generating repo api data
916
916
917 """
917 """
918 repo = self
918 repo = self
919 data = dict(
919 data = dict(
920 repo_id=repo.repo_id,
920 repo_id=repo.repo_id,
921 repo_name=repo.repo_name,
921 repo_name=repo.repo_name,
922 repo_type=repo.repo_type,
922 repo_type=repo.repo_type,
923 clone_uri=repo.clone_uri,
923 clone_uri=repo.clone_uri,
924 private=repo.private,
924 private=repo.private,
925 created_on=repo.created_on,
925 created_on=repo.created_on,
926 description=repo.description,
926 description=repo.description,
927 landing_rev=repo.landing_rev,
927 landing_rev=repo.landing_rev,
928 owner=repo.user.username,
928 owner=repo.user.username,
929 fork_of=repo.fork.repo_name if repo.fork else None,
929 fork_of=repo.fork.repo_name if repo.fork else None,
930 enable_statistics=repo.enable_statistics,
930 enable_statistics=repo.enable_statistics,
931 enable_locking=repo.enable_locking,
931 enable_locking=repo.enable_locking,
932 enable_downloads=repo.enable_downloads,
932 enable_downloads=repo.enable_downloads,
933 last_changeset=repo.changeset_cache
933 last_changeset=repo.changeset_cache
934 )
934 )
935
935
936 return data
936 return data
937
937
938 @classmethod
938 @classmethod
939 def lock(cls, repo, user_id):
939 def lock(cls, repo, user_id):
940 repo.locked = [user_id, time.time()]
940 repo.locked = [user_id, time.time()]
941 Session().add(repo)
941 Session().add(repo)
942 Session().commit()
942 Session().commit()
943
943
944 @classmethod
944 @classmethod
945 def unlock(cls, repo):
945 def unlock(cls, repo):
946 repo.locked = None
946 repo.locked = None
947 Session().add(repo)
947 Session().add(repo)
948 Session().commit()
948 Session().commit()
949
949
950 @property
950 @property
951 def last_db_change(self):
951 def last_db_change(self):
952 return self.updated_on
952 return self.updated_on
953
953
954 def clone_url(self, **override):
954 def clone_url(self, **override):
955 from pylons import url
955 from pylons import url
956 from urlparse import urlparse
956 from urlparse import urlparse
957 import urllib
957 import urllib
958 parsed_url = urlparse(url('home', qualified=True))
958 parsed_url = urlparse(url('home', qualified=True))
959 default_clone_uri = '%(scheme)s://%(user)s%(pass)s%(netloc)s%(prefix)s%(path)s'
959 default_clone_uri = '%(scheme)s://%(user)s%(pass)s%(netloc)s%(prefix)s%(path)s'
960 decoded_path = safe_unicode(urllib.unquote(parsed_url.path))
960 decoded_path = safe_unicode(urllib.unquote(parsed_url.path))
961 args = {
961 args = {
962 'user': '',
962 'user': '',
963 'pass': '',
963 'pass': '',
964 'scheme': parsed_url.scheme,
964 'scheme': parsed_url.scheme,
965 'netloc': parsed_url.netloc,
965 'netloc': parsed_url.netloc,
966 'prefix': decoded_path,
966 'prefix': decoded_path,
967 'path': self.repo_name
967 'path': self.repo_name
968 }
968 }
969
969
970 args.update(override)
970 args.update(override)
971 return default_clone_uri % args
971 return default_clone_uri % args
972
972
973 #==========================================================================
973 #==========================================================================
974 # SCM PROPERTIES
974 # SCM PROPERTIES
975 #==========================================================================
975 #==========================================================================
976
976
977 def get_changeset(self, rev=None):
977 def get_changeset(self, rev=None):
978 return get_changeset_safe(self.scm_instance, rev)
978 return get_changeset_safe(self.scm_instance, rev)
979
979
980 def get_landing_changeset(self):
980 def get_landing_changeset(self):
981 """
981 """
982 Returns landing changeset, or if that doesn't exist returns the tip
982 Returns landing changeset, or if that doesn't exist returns the tip
983 """
983 """
984 cs = self.get_changeset(self.landing_rev) or self.get_changeset()
984 cs = self.get_changeset(self.landing_rev) or self.get_changeset()
985 return cs
985 return cs
986
986
987 def update_changeset_cache(self, cs_cache=None):
987 def update_changeset_cache(self, cs_cache=None):
988 """
988 """
989 Update cache of last changeset for repository, keys should be::
989 Update cache of last changeset for repository, keys should be::
990
990
991 short_id
991 short_id
992 raw_id
992 raw_id
993 revision
993 revision
994 message
994 message
995 date
995 date
996 author
996 author
997
997
998 :param cs_cache:
998 :param cs_cache:
999 """
999 """
1000 from rhodecode.lib.vcs.backends.base import BaseChangeset
1000 from rhodecode.lib.vcs.backends.base import BaseChangeset
1001 if cs_cache is None:
1001 if cs_cache is None:
1002 cs_cache = self.get_changeset()
1002 cs_cache = self.get_changeset()
1003 if isinstance(cs_cache, BaseChangeset):
1003 if isinstance(cs_cache, BaseChangeset):
1004 cs_cache = cs_cache.__json__()
1004 cs_cache = cs_cache.__json__()
1005
1005
1006 if (cs_cache != self.changeset_cache
1006 if (cs_cache != self.changeset_cache
1007 or not self.last_change
1007 or not self.last_change
1008 or not self.changeset_cache):
1008 or not self.changeset_cache):
1009 _default = datetime.datetime.fromtimestamp(0)
1009 _default = datetime.datetime.fromtimestamp(0)
1010 last_change = cs_cache.get('date') or self.last_change or _default
1010 last_change = cs_cache.get('date') or self.last_change or _default
1011 log.debug('updated repo %s with new cs cache %s' % (self, cs_cache))
1011 log.debug('updated repo %s with new cs cache %s' % (self, cs_cache))
1012 self.updated_on = last_change
1012 self.updated_on = last_change
1013 self.changeset_cache = cs_cache
1013 self.changeset_cache = cs_cache
1014 Session().add(self)
1014 Session().add(self)
1015 Session().commit()
1015 Session().commit()
1016 else:
1016 else:
1017 log.debug('Skipping repo:%s already with latest changes' % self)
1017 log.debug('Skipping repo:%s already with latest changes' % self)
1018
1018
1019 @property
1019 @property
1020 def tip(self):
1020 def tip(self):
1021 return self.get_changeset('tip')
1021 return self.get_changeset('tip')
1022
1022
1023 @property
1023 @property
1024 def author(self):
1024 def author(self):
1025 return self.tip.author
1025 return self.tip.author
1026
1026
1027 @property
1027 @property
1028 def last_change(self):
1028 def last_change(self):
1029 return self.scm_instance.last_change
1029 return self.scm_instance.last_change
1030
1030
1031 def get_comments(self, revisions=None):
1031 def get_comments(self, revisions=None):
1032 """
1032 """
1033 Returns comments for this repository grouped by revisions
1033 Returns comments for this repository grouped by revisions
1034
1034
1035 :param revisions: filter query by revisions only
1035 :param revisions: filter query by revisions only
1036 """
1036 """
1037 cmts = ChangesetComment.query()\
1037 cmts = ChangesetComment.query()\
1038 .filter(ChangesetComment.repo == self)
1038 .filter(ChangesetComment.repo == self)
1039 if revisions:
1039 if revisions:
1040 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
1040 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
1041 grouped = defaultdict(list)
1041 grouped = defaultdict(list)
1042 for cmt in cmts.all():
1042 for cmt in cmts.all():
1043 grouped[cmt.revision].append(cmt)
1043 grouped[cmt.revision].append(cmt)
1044 return grouped
1044 return grouped
1045
1045
1046 def statuses(self, revisions=None):
1046 def statuses(self, revisions=None):
1047 """
1047 """
1048 Returns statuses for this repository
1048 Returns statuses for this repository
1049
1049
1050 :param revisions: list of revisions to get statuses for
1050 :param revisions: list of revisions to get statuses for
1051 :type revisions: list
1051 :type revisions: list
1052 """
1052 """
1053
1053
1054 statuses = ChangesetStatus.query()\
1054 statuses = ChangesetStatus.query()\
1055 .filter(ChangesetStatus.repo == self)\
1055 .filter(ChangesetStatus.repo == self)\
1056 .filter(ChangesetStatus.version == 0)
1056 .filter(ChangesetStatus.version == 0)
1057 if revisions:
1057 if revisions:
1058 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
1058 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
1059 grouped = {}
1059 grouped = {}
1060
1060
1061 #maybe we have open new pullrequest without a status ?
1061 #maybe we have open new pullrequest without a status ?
1062 stat = ChangesetStatus.STATUS_UNDER_REVIEW
1062 stat = ChangesetStatus.STATUS_UNDER_REVIEW
1063 status_lbl = ChangesetStatus.get_status_lbl(stat)
1063 status_lbl = ChangesetStatus.get_status_lbl(stat)
1064 for pr in PullRequest.query().filter(PullRequest.org_repo == self).all():
1064 for pr in PullRequest.query().filter(PullRequest.org_repo == self).all():
1065 for rev in pr.revisions:
1065 for rev in pr.revisions:
1066 pr_id = pr.pull_request_id
1066 pr_id = pr.pull_request_id
1067 pr_repo = pr.other_repo.repo_name
1067 pr_repo = pr.other_repo.repo_name
1068 grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
1068 grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
1069
1069
1070 for stat in statuses.all():
1070 for stat in statuses.all():
1071 pr_id = pr_repo = None
1071 pr_id = pr_repo = None
1072 if stat.pull_request:
1072 if stat.pull_request:
1073 pr_id = stat.pull_request.pull_request_id
1073 pr_id = stat.pull_request.pull_request_id
1074 pr_repo = stat.pull_request.other_repo.repo_name
1074 pr_repo = stat.pull_request.other_repo.repo_name
1075 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
1075 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
1076 pr_id, pr_repo]
1076 pr_id, pr_repo]
1077 return grouped
1077 return grouped
1078
1078
1079 #==========================================================================
1079 #==========================================================================
1080 # SCM CACHE INSTANCE
1080 # SCM CACHE INSTANCE
1081 #==========================================================================
1081 #==========================================================================
1082
1082
1083 @property
1083 @property
1084 def invalidate(self):
1084 def invalidate(self):
1085 return CacheInvalidation.invalidate(self.repo_name)
1085 return CacheInvalidation.invalidate(self.repo_name)
1086
1086
1087 def set_invalidate(self):
1087 def set_invalidate(self):
1088 """
1088 """
1089 set a cache for invalidation for this instance
1089 set a cache for invalidation for this instance
1090 """
1090 """
1091 CacheInvalidation.set_invalidate(repo_name=self.repo_name)
1091 CacheInvalidation.set_invalidate(repo_name=self.repo_name)
1092
1092
1093 @LazyProperty
1093 @LazyProperty
1094 def scm_instance(self):
1094 def scm_instance(self):
1095 import rhodecode
1095 import rhodecode
1096 full_cache = str2bool(rhodecode.CONFIG.get('vcs_full_cache'))
1096 full_cache = str2bool(rhodecode.CONFIG.get('vcs_full_cache'))
1097 if full_cache:
1097 if full_cache:
1098 return self.scm_instance_cached()
1098 return self.scm_instance_cached()
1099 return self.__get_instance()
1099 return self.__get_instance()
1100
1100
1101 def scm_instance_cached(self, cache_map=None):
1101 def scm_instance_cached(self, cache_map=None):
1102 @cache_region('long_term')
1102 @cache_region('long_term')
1103 def _c(repo_name):
1103 def _c(repo_name):
1104 return self.__get_instance()
1104 return self.__get_instance()
1105 rn = self.repo_name
1105 rn = self.repo_name
1106 log.debug('Getting cached instance of repo')
1106 log.debug('Getting cached instance of repo')
1107
1107
1108 if cache_map:
1108 if cache_map:
1109 # get using prefilled cache_map
1109 # get using prefilled cache_map
1110 invalidate_repo = cache_map[self.repo_name]
1110 invalidate_repo = cache_map[self.repo_name]
1111 if invalidate_repo:
1111 if invalidate_repo:
1112 invalidate_repo = (None if invalidate_repo.cache_active
1112 invalidate_repo = (None if invalidate_repo.cache_active
1113 else invalidate_repo)
1113 else invalidate_repo)
1114 else:
1114 else:
1115 # get from invalidate
1115 # get from invalidate
1116 invalidate_repo = self.invalidate
1116 invalidate_repo = self.invalidate
1117
1117
1118 if invalidate_repo is not None:
1118 if invalidate_repo is not None:
1119 region_invalidate(_c, None, rn)
1119 region_invalidate(_c, None, rn)
1120 # update our cache
1120 # update our cache
1121 CacheInvalidation.set_valid(invalidate_repo.cache_key)
1121 CacheInvalidation.set_valid(invalidate_repo.cache_key)
1122 return _c(rn)
1122 return _c(rn)
1123
1123
1124 def __get_instance(self):
1124 def __get_instance(self):
1125 repo_full_path = self.repo_full_path
1125 repo_full_path = self.repo_full_path
1126 try:
1126 try:
1127 alias = get_scm(repo_full_path)[0]
1127 alias = get_scm(repo_full_path)[0]
1128 log.debug('Creating instance of %s repository' % alias)
1128 log.debug('Creating instance of %s repository' % alias)
1129 backend = get_backend(alias)
1129 backend = get_backend(alias)
1130 except VCSError:
1130 except VCSError:
1131 log.error(traceback.format_exc())
1131 log.error(traceback.format_exc())
1132 log.error('Perhaps this repository is in db and not in '
1132 log.error('Perhaps this repository is in db and not in '
1133 'filesystem run rescan repositories with '
1133 'filesystem run rescan repositories with '
1134 '"destroy old data " option from admin panel')
1134 '"destroy old data " option from admin panel')
1135 return
1135 return
1136
1136
1137 if alias == 'hg':
1137 if alias == 'hg':
1138
1138
1139 repo = backend(safe_str(repo_full_path), create=False,
1139 repo = backend(safe_str(repo_full_path), create=False,
1140 baseui=self._ui)
1140 baseui=self._ui)
1141 # skip hidden web repository
1141 # skip hidden web repository
1142 if repo._get_hidden():
1142 if repo._get_hidden():
1143 return
1143 return
1144 else:
1144 else:
1145 repo = backend(repo_full_path, create=False)
1145 repo = backend(repo_full_path, create=False)
1146
1146
1147 return repo
1147 return repo
1148
1148
1149
1149
1150 class RepoGroup(Base, BaseModel):
1150 class RepoGroup(Base, BaseModel):
1151 __tablename__ = 'groups'
1151 __tablename__ = 'groups'
1152 __table_args__ = (
1152 __table_args__ = (
1153 UniqueConstraint('group_name', 'group_parent_id'),
1153 UniqueConstraint('group_name', 'group_parent_id'),
1154 CheckConstraint('group_id != group_parent_id'),
1154 CheckConstraint('group_id != group_parent_id'),
1155 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1155 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1156 'mysql_charset': 'utf8'},
1156 'mysql_charset': 'utf8'},
1157 )
1157 )
1158 __mapper_args__ = {'order_by': 'group_name'}
1158 __mapper_args__ = {'order_by': 'group_name'}
1159
1159
1160 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1160 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1161 group_name = Column("group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
1161 group_name = Column("group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
1162 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
1162 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
1163 group_description = Column("group_description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1163 group_description = Column("group_description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1164 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
1164 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
1165
1165
1166 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
1166 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
1167 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
1167 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
1168
1168
1169 parent_group = relationship('RepoGroup', remote_side=group_id)
1169 parent_group = relationship('RepoGroup', remote_side=group_id)
1170
1170
1171 def __init__(self, group_name='', parent_group=None):
1171 def __init__(self, group_name='', parent_group=None):
1172 self.group_name = group_name
1172 self.group_name = group_name
1173 self.parent_group = parent_group
1173 self.parent_group = parent_group
1174
1174
1175 def __unicode__(self):
1175 def __unicode__(self):
1176 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
1176 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
1177 self.group_name)
1177 self.group_name)
1178
1178
1179 @classmethod
1179 @classmethod
1180 def groups_choices(cls, groups=None, check_perms=False, show_empty_group=True):
1180 def groups_choices(cls, groups=None, show_empty_group=True):
1181 from webhelpers.html import literal as _literal
1181 from webhelpers.html import literal as _literal
1182 from rhodecode.model.scm import ScmModel
1183 if not groups:
1182 if not groups:
1184 groups = cls.query().all()
1183 groups = cls.query().all()
1185 if check_perms:
1184
1186 #filter group user have access to, it's done
1187 #magically inside ScmModel based on current user
1188 groups = ScmModel().get_repos_groups(groups)
1189 repo_groups = []
1185 repo_groups = []
1190 if show_empty_group:
1186 if show_empty_group:
1191 repo_groups = [('-1', '-- no parent --')]
1187 repo_groups = [('-1', '-- no parent --')]
1192 sep = ' &raquo; '
1188 sep = ' &raquo; '
1193 _name = lambda k: _literal(sep.join(k))
1189 _name = lambda k: _literal(sep.join(k))
1194
1190
1195 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
1191 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
1196 for x in groups])
1192 for x in groups])
1197
1193
1198 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
1194 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
1199 return repo_groups
1195 return repo_groups
1200
1196
1201 @classmethod
1197 @classmethod
1202 def url_sep(cls):
1198 def url_sep(cls):
1203 return URL_SEP
1199 return URL_SEP
1204
1200
1205 @classmethod
1201 @classmethod
1206 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
1202 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
1207 if case_insensitive:
1203 if case_insensitive:
1208 gr = cls.query()\
1204 gr = cls.query()\
1209 .filter(cls.group_name.ilike(group_name))
1205 .filter(cls.group_name.ilike(group_name))
1210 else:
1206 else:
1211 gr = cls.query()\
1207 gr = cls.query()\
1212 .filter(cls.group_name == group_name)
1208 .filter(cls.group_name == group_name)
1213 if cache:
1209 if cache:
1214 gr = gr.options(FromCache(
1210 gr = gr.options(FromCache(
1215 "sql_cache_short",
1211 "sql_cache_short",
1216 "get_group_%s" % _hash_key(group_name)
1212 "get_group_%s" % _hash_key(group_name)
1217 )
1213 )
1218 )
1214 )
1219 return gr.scalar()
1215 return gr.scalar()
1220
1216
1221 @property
1217 @property
1222 def parents(self):
1218 def parents(self):
1223 parents_recursion_limit = 5
1219 parents_recursion_limit = 5
1224 groups = []
1220 groups = []
1225 if self.parent_group is None:
1221 if self.parent_group is None:
1226 return groups
1222 return groups
1227 cur_gr = self.parent_group
1223 cur_gr = self.parent_group
1228 groups.insert(0, cur_gr)
1224 groups.insert(0, cur_gr)
1229 cnt = 0
1225 cnt = 0
1230 while 1:
1226 while 1:
1231 cnt += 1
1227 cnt += 1
1232 gr = getattr(cur_gr, 'parent_group', None)
1228 gr = getattr(cur_gr, 'parent_group', None)
1233 cur_gr = cur_gr.parent_group
1229 cur_gr = cur_gr.parent_group
1234 if gr is None:
1230 if gr is None:
1235 break
1231 break
1236 if cnt == parents_recursion_limit:
1232 if cnt == parents_recursion_limit:
1237 # this will prevent accidental infinit loops
1233 # this will prevent accidental infinit loops
1238 log.error('group nested more than %s' %
1234 log.error('group nested more than %s' %
1239 parents_recursion_limit)
1235 parents_recursion_limit)
1240 break
1236 break
1241
1237
1242 groups.insert(0, gr)
1238 groups.insert(0, gr)
1243 return groups
1239 return groups
1244
1240
1245 @property
1241 @property
1246 def children(self):
1242 def children(self):
1247 return RepoGroup.query().filter(RepoGroup.parent_group == self)
1243 return RepoGroup.query().filter(RepoGroup.parent_group == self)
1248
1244
1249 @property
1245 @property
1250 def name(self):
1246 def name(self):
1251 return self.group_name.split(RepoGroup.url_sep())[-1]
1247 return self.group_name.split(RepoGroup.url_sep())[-1]
1252
1248
1253 @property
1249 @property
1254 def full_path(self):
1250 def full_path(self):
1255 return self.group_name
1251 return self.group_name
1256
1252
1257 @property
1253 @property
1258 def full_path_splitted(self):
1254 def full_path_splitted(self):
1259 return self.group_name.split(RepoGroup.url_sep())
1255 return self.group_name.split(RepoGroup.url_sep())
1260
1256
1261 @property
1257 @property
1262 def repositories(self):
1258 def repositories(self):
1263 return Repository.query()\
1259 return Repository.query()\
1264 .filter(Repository.group == self)\
1260 .filter(Repository.group == self)\
1265 .order_by(Repository.repo_name)
1261 .order_by(Repository.repo_name)
1266
1262
1267 @property
1263 @property
1268 def repositories_recursive_count(self):
1264 def repositories_recursive_count(self):
1269 cnt = self.repositories.count()
1265 cnt = self.repositories.count()
1270
1266
1271 def children_count(group):
1267 def children_count(group):
1272 cnt = 0
1268 cnt = 0
1273 for child in group.children:
1269 for child in group.children:
1274 cnt += child.repositories.count()
1270 cnt += child.repositories.count()
1275 cnt += children_count(child)
1271 cnt += children_count(child)
1276 return cnt
1272 return cnt
1277
1273
1278 return cnt + children_count(self)
1274 return cnt + children_count(self)
1279
1275
1280 def recursive_groups_and_repos(self):
1276 def recursive_groups_and_repos(self):
1281 """
1277 """
1282 Recursive return all groups, with repositories in those groups
1278 Recursive return all groups, with repositories in those groups
1283 """
1279 """
1284 all_ = []
1280 all_ = []
1285
1281
1286 def _get_members(root_gr):
1282 def _get_members(root_gr):
1287 for r in root_gr.repositories:
1283 for r in root_gr.repositories:
1288 all_.append(r)
1284 all_.append(r)
1289 childs = root_gr.children.all()
1285 childs = root_gr.children.all()
1290 if childs:
1286 if childs:
1291 for gr in childs:
1287 for gr in childs:
1292 all_.append(gr)
1288 all_.append(gr)
1293 _get_members(gr)
1289 _get_members(gr)
1294
1290
1295 _get_members(self)
1291 _get_members(self)
1296 return [self] + all_
1292 return [self] + all_
1297
1293
1298 def get_new_name(self, group_name):
1294 def get_new_name(self, group_name):
1299 """
1295 """
1300 returns new full group name based on parent and new name
1296 returns new full group name based on parent and new name
1301
1297
1302 :param group_name:
1298 :param group_name:
1303 """
1299 """
1304 path_prefix = (self.parent_group.full_path_splitted if
1300 path_prefix = (self.parent_group.full_path_splitted if
1305 self.parent_group else [])
1301 self.parent_group else [])
1306 return RepoGroup.url_sep().join(path_prefix + [group_name])
1302 return RepoGroup.url_sep().join(path_prefix + [group_name])
1307
1303
1308
1304
1309 class Permission(Base, BaseModel):
1305 class Permission(Base, BaseModel):
1310 __tablename__ = 'permissions'
1306 __tablename__ = 'permissions'
1311 __table_args__ = (
1307 __table_args__ = (
1312 Index('p_perm_name_idx', 'permission_name'),
1308 Index('p_perm_name_idx', 'permission_name'),
1313 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1309 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1314 'mysql_charset': 'utf8'},
1310 'mysql_charset': 'utf8'},
1315 )
1311 )
1316 PERMS = [
1312 PERMS = [
1317 ('repository.none', _('Repository no access')),
1313 ('repository.none', _('Repository no access')),
1318 ('repository.read', _('Repository read access')),
1314 ('repository.read', _('Repository read access')),
1319 ('repository.write', _('Repository write access')),
1315 ('repository.write', _('Repository write access')),
1320 ('repository.admin', _('Repository admin access')),
1316 ('repository.admin', _('Repository admin access')),
1321
1317
1322 ('group.none', _('Repositories Group no access')),
1318 ('group.none', _('Repositories Group no access')),
1323 ('group.read', _('Repositories Group read access')),
1319 ('group.read', _('Repositories Group read access')),
1324 ('group.write', _('Repositories Group write access')),
1320 ('group.write', _('Repositories Group write access')),
1325 ('group.admin', _('Repositories Group admin access')),
1321 ('group.admin', _('Repositories Group admin access')),
1326
1322
1327 ('hg.admin', _('RhodeCode Administrator')),
1323 ('hg.admin', _('RhodeCode Administrator')),
1328 ('hg.create.none', _('Repository creation disabled')),
1324 ('hg.create.none', _('Repository creation disabled')),
1329 ('hg.create.repository', _('Repository creation enabled')),
1325 ('hg.create.repository', _('Repository creation enabled')),
1330 ('hg.fork.none', _('Repository forking disabled')),
1326 ('hg.fork.none', _('Repository forking disabled')),
1331 ('hg.fork.repository', _('Repository forking enabled')),
1327 ('hg.fork.repository', _('Repository forking enabled')),
1332 ('hg.register.none', _('Register disabled')),
1328 ('hg.register.none', _('Register disabled')),
1333 ('hg.register.manual_activate', _('Register new user with RhodeCode '
1329 ('hg.register.manual_activate', _('Register new user with RhodeCode '
1334 'with manual activation')),
1330 'with manual activation')),
1335
1331
1336 ('hg.register.auto_activate', _('Register new user with RhodeCode '
1332 ('hg.register.auto_activate', _('Register new user with RhodeCode '
1337 'with auto activation')),
1333 'with auto activation')),
1338 ]
1334 ]
1339
1335
1340 # defines which permissions are more important higher the more important
1336 # defines which permissions are more important higher the more important
1341 PERM_WEIGHTS = {
1337 PERM_WEIGHTS = {
1342 'repository.none': 0,
1338 'repository.none': 0,
1343 'repository.read': 1,
1339 'repository.read': 1,
1344 'repository.write': 3,
1340 'repository.write': 3,
1345 'repository.admin': 4,
1341 'repository.admin': 4,
1346
1342
1347 'group.none': 0,
1343 'group.none': 0,
1348 'group.read': 1,
1344 'group.read': 1,
1349 'group.write': 3,
1345 'group.write': 3,
1350 'group.admin': 4,
1346 'group.admin': 4,
1351
1347
1352 'hg.fork.none': 0,
1348 'hg.fork.none': 0,
1353 'hg.fork.repository': 1,
1349 'hg.fork.repository': 1,
1354 'hg.create.none': 0,
1350 'hg.create.none': 0,
1355 'hg.create.repository':1
1351 'hg.create.repository':1
1356 }
1352 }
1357
1353
1358 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1354 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1359 permission_name = Column("permission_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1355 permission_name = Column("permission_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1360 permission_longname = Column("permission_longname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1356 permission_longname = Column("permission_longname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1361
1357
1362 def __unicode__(self):
1358 def __unicode__(self):
1363 return u"<%s('%s:%s')>" % (
1359 return u"<%s('%s:%s')>" % (
1364 self.__class__.__name__, self.permission_id, self.permission_name
1360 self.__class__.__name__, self.permission_id, self.permission_name
1365 )
1361 )
1366
1362
1367 @classmethod
1363 @classmethod
1368 def get_by_key(cls, key):
1364 def get_by_key(cls, key):
1369 return cls.query().filter(cls.permission_name == key).scalar()
1365 return cls.query().filter(cls.permission_name == key).scalar()
1370
1366
1371 @classmethod
1367 @classmethod
1372 def get_default_perms(cls, default_user_id):
1368 def get_default_perms(cls, default_user_id):
1373 q = Session().query(UserRepoToPerm, Repository, cls)\
1369 q = Session().query(UserRepoToPerm, Repository, cls)\
1374 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
1370 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
1375 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
1371 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
1376 .filter(UserRepoToPerm.user_id == default_user_id)
1372 .filter(UserRepoToPerm.user_id == default_user_id)
1377
1373
1378 return q.all()
1374 return q.all()
1379
1375
1380 @classmethod
1376 @classmethod
1381 def get_default_group_perms(cls, default_user_id):
1377 def get_default_group_perms(cls, default_user_id):
1382 q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
1378 q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
1383 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
1379 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
1384 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
1380 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
1385 .filter(UserRepoGroupToPerm.user_id == default_user_id)
1381 .filter(UserRepoGroupToPerm.user_id == default_user_id)
1386
1382
1387 return q.all()
1383 return q.all()
1388
1384
1389
1385
1390 class UserRepoToPerm(Base, BaseModel):
1386 class UserRepoToPerm(Base, BaseModel):
1391 __tablename__ = 'repo_to_perm'
1387 __tablename__ = 'repo_to_perm'
1392 __table_args__ = (
1388 __table_args__ = (
1393 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
1389 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
1394 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1390 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1395 'mysql_charset': 'utf8'}
1391 'mysql_charset': 'utf8'}
1396 )
1392 )
1397 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1393 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1398 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1394 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1399 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1395 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1400 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1396 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1401
1397
1402 user = relationship('User')
1398 user = relationship('User')
1403 repository = relationship('Repository')
1399 repository = relationship('Repository')
1404 permission = relationship('Permission')
1400 permission = relationship('Permission')
1405
1401
1406 @classmethod
1402 @classmethod
1407 def create(cls, user, repository, permission):
1403 def create(cls, user, repository, permission):
1408 n = cls()
1404 n = cls()
1409 n.user = user
1405 n.user = user
1410 n.repository = repository
1406 n.repository = repository
1411 n.permission = permission
1407 n.permission = permission
1412 Session().add(n)
1408 Session().add(n)
1413 return n
1409 return n
1414
1410
1415 def __unicode__(self):
1411 def __unicode__(self):
1416 return u'<user:%s => %s >' % (self.user, self.repository)
1412 return u'<user:%s => %s >' % (self.user, self.repository)
1417
1413
1418
1414
1419 class UserToPerm(Base, BaseModel):
1415 class UserToPerm(Base, BaseModel):
1420 __tablename__ = 'user_to_perm'
1416 __tablename__ = 'user_to_perm'
1421 __table_args__ = (
1417 __table_args__ = (
1422 UniqueConstraint('user_id', 'permission_id'),
1418 UniqueConstraint('user_id', 'permission_id'),
1423 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1419 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1424 'mysql_charset': 'utf8'}
1420 'mysql_charset': 'utf8'}
1425 )
1421 )
1426 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1422 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1427 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1423 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1428 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1424 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1429
1425
1430 user = relationship('User')
1426 user = relationship('User')
1431 permission = relationship('Permission', lazy='joined')
1427 permission = relationship('Permission', lazy='joined')
1432
1428
1433
1429
1434 class UsersGroupRepoToPerm(Base, BaseModel):
1430 class UsersGroupRepoToPerm(Base, BaseModel):
1435 __tablename__ = 'users_group_repo_to_perm'
1431 __tablename__ = 'users_group_repo_to_perm'
1436 __table_args__ = (
1432 __table_args__ = (
1437 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1433 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1438 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1434 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1439 'mysql_charset': 'utf8'}
1435 'mysql_charset': 'utf8'}
1440 )
1436 )
1441 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1437 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1442 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1438 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1443 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1439 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1444 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1440 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1445
1441
1446 users_group = relationship('UsersGroup')
1442 users_group = relationship('UsersGroup')
1447 permission = relationship('Permission')
1443 permission = relationship('Permission')
1448 repository = relationship('Repository')
1444 repository = relationship('Repository')
1449
1445
1450 @classmethod
1446 @classmethod
1451 def create(cls, users_group, repository, permission):
1447 def create(cls, users_group, repository, permission):
1452 n = cls()
1448 n = cls()
1453 n.users_group = users_group
1449 n.users_group = users_group
1454 n.repository = repository
1450 n.repository = repository
1455 n.permission = permission
1451 n.permission = permission
1456 Session().add(n)
1452 Session().add(n)
1457 return n
1453 return n
1458
1454
1459 def __unicode__(self):
1455 def __unicode__(self):
1460 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
1456 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
1461
1457
1462
1458
1463 class UsersGroupToPerm(Base, BaseModel):
1459 class UsersGroupToPerm(Base, BaseModel):
1464 __tablename__ = 'users_group_to_perm'
1460 __tablename__ = 'users_group_to_perm'
1465 __table_args__ = (
1461 __table_args__ = (
1466 UniqueConstraint('users_group_id', 'permission_id',),
1462 UniqueConstraint('users_group_id', 'permission_id',),
1467 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1463 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1468 'mysql_charset': 'utf8'}
1464 'mysql_charset': 'utf8'}
1469 )
1465 )
1470 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1466 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1471 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1467 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1472 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1468 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1473
1469
1474 users_group = relationship('UsersGroup')
1470 users_group = relationship('UsersGroup')
1475 permission = relationship('Permission')
1471 permission = relationship('Permission')
1476
1472
1477
1473
1478 class UserRepoGroupToPerm(Base, BaseModel):
1474 class UserRepoGroupToPerm(Base, BaseModel):
1479 __tablename__ = 'user_repo_group_to_perm'
1475 __tablename__ = 'user_repo_group_to_perm'
1480 __table_args__ = (
1476 __table_args__ = (
1481 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1477 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1482 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1478 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1483 'mysql_charset': 'utf8'}
1479 'mysql_charset': 'utf8'}
1484 )
1480 )
1485
1481
1486 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1482 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1487 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1483 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1488 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1484 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1489 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1485 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1490
1486
1491 user = relationship('User')
1487 user = relationship('User')
1492 group = relationship('RepoGroup')
1488 group = relationship('RepoGroup')
1493 permission = relationship('Permission')
1489 permission = relationship('Permission')
1494
1490
1495
1491
1496 class UsersGroupRepoGroupToPerm(Base, BaseModel):
1492 class UsersGroupRepoGroupToPerm(Base, BaseModel):
1497 __tablename__ = 'users_group_repo_group_to_perm'
1493 __tablename__ = 'users_group_repo_group_to_perm'
1498 __table_args__ = (
1494 __table_args__ = (
1499 UniqueConstraint('users_group_id', 'group_id'),
1495 UniqueConstraint('users_group_id', 'group_id'),
1500 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1496 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1501 'mysql_charset': 'utf8'}
1497 'mysql_charset': 'utf8'}
1502 )
1498 )
1503
1499
1504 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)
1500 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)
1505 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1501 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1506 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1502 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1507 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1503 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1508
1504
1509 users_group = relationship('UsersGroup')
1505 users_group = relationship('UsersGroup')
1510 permission = relationship('Permission')
1506 permission = relationship('Permission')
1511 group = relationship('RepoGroup')
1507 group = relationship('RepoGroup')
1512
1508
1513
1509
1514 class Statistics(Base, BaseModel):
1510 class Statistics(Base, BaseModel):
1515 __tablename__ = 'statistics'
1511 __tablename__ = 'statistics'
1516 __table_args__ = (
1512 __table_args__ = (
1517 UniqueConstraint('repository_id'),
1513 UniqueConstraint('repository_id'),
1518 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1514 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1519 'mysql_charset': 'utf8'}
1515 'mysql_charset': 'utf8'}
1520 )
1516 )
1521 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1517 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1522 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1518 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1523 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1519 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1524 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1520 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1525 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1521 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1526 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1522 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1527
1523
1528 repository = relationship('Repository', single_parent=True)
1524 repository = relationship('Repository', single_parent=True)
1529
1525
1530
1526
1531 class UserFollowing(Base, BaseModel):
1527 class UserFollowing(Base, BaseModel):
1532 __tablename__ = 'user_followings'
1528 __tablename__ = 'user_followings'
1533 __table_args__ = (
1529 __table_args__ = (
1534 UniqueConstraint('user_id', 'follows_repository_id'),
1530 UniqueConstraint('user_id', 'follows_repository_id'),
1535 UniqueConstraint('user_id', 'follows_user_id'),
1531 UniqueConstraint('user_id', 'follows_user_id'),
1536 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1532 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1537 'mysql_charset': 'utf8'}
1533 'mysql_charset': 'utf8'}
1538 )
1534 )
1539
1535
1540 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1536 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1541 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1537 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1542 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1538 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1543 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1539 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1544 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1540 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1545
1541
1546 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1542 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1547
1543
1548 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1544 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1549 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1545 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1550
1546
1551 @classmethod
1547 @classmethod
1552 def get_repo_followers(cls, repo_id):
1548 def get_repo_followers(cls, repo_id):
1553 return cls.query().filter(cls.follows_repo_id == repo_id)
1549 return cls.query().filter(cls.follows_repo_id == repo_id)
1554
1550
1555
1551
1556 class CacheInvalidation(Base, BaseModel):
1552 class CacheInvalidation(Base, BaseModel):
1557 __tablename__ = 'cache_invalidation'
1553 __tablename__ = 'cache_invalidation'
1558 __table_args__ = (
1554 __table_args__ = (
1559 UniqueConstraint('cache_key'),
1555 UniqueConstraint('cache_key'),
1560 Index('key_idx', 'cache_key'),
1556 Index('key_idx', 'cache_key'),
1561 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1557 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1562 'mysql_charset': 'utf8'},
1558 'mysql_charset': 'utf8'},
1563 )
1559 )
1564 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1560 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1565 cache_key = Column("cache_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1561 cache_key = Column("cache_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1566 cache_args = Column("cache_args", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1562 cache_args = Column("cache_args", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1567 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1563 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1568
1564
1569 def __init__(self, cache_key, cache_args=''):
1565 def __init__(self, cache_key, cache_args=''):
1570 self.cache_key = cache_key
1566 self.cache_key = cache_key
1571 self.cache_args = cache_args
1567 self.cache_args = cache_args
1572 self.cache_active = False
1568 self.cache_active = False
1573
1569
1574 def __unicode__(self):
1570 def __unicode__(self):
1575 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1571 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1576 self.cache_id, self.cache_key)
1572 self.cache_id, self.cache_key)
1577
1573
1578 @property
1574 @property
1579 def prefix(self):
1575 def prefix(self):
1580 _split = self.cache_key.split(self.cache_args, 1)
1576 _split = self.cache_key.split(self.cache_args, 1)
1581 if _split and len(_split) == 2:
1577 if _split and len(_split) == 2:
1582 return _split[0]
1578 return _split[0]
1583 return ''
1579 return ''
1584
1580
1585 @classmethod
1581 @classmethod
1586 def clear_cache(cls):
1582 def clear_cache(cls):
1587 cls.query().delete()
1583 cls.query().delete()
1588
1584
1589 @classmethod
1585 @classmethod
1590 def _get_key(cls, key):
1586 def _get_key(cls, key):
1591 """
1587 """
1592 Wrapper for generating a key, together with a prefix
1588 Wrapper for generating a key, together with a prefix
1593
1589
1594 :param key:
1590 :param key:
1595 """
1591 """
1596 import rhodecode
1592 import rhodecode
1597 prefix = ''
1593 prefix = ''
1598 org_key = key
1594 org_key = key
1599 iid = rhodecode.CONFIG.get('instance_id')
1595 iid = rhodecode.CONFIG.get('instance_id')
1600 if iid:
1596 if iid:
1601 prefix = iid
1597 prefix = iid
1602
1598
1603 return "%s%s" % (prefix, key), prefix, org_key
1599 return "%s%s" % (prefix, key), prefix, org_key
1604
1600
1605 @classmethod
1601 @classmethod
1606 def get_by_key(cls, key):
1602 def get_by_key(cls, key):
1607 return cls.query().filter(cls.cache_key == key).scalar()
1603 return cls.query().filter(cls.cache_key == key).scalar()
1608
1604
1609 @classmethod
1605 @classmethod
1610 def get_by_repo_name(cls, repo_name):
1606 def get_by_repo_name(cls, repo_name):
1611 return cls.query().filter(cls.cache_args == repo_name).all()
1607 return cls.query().filter(cls.cache_args == repo_name).all()
1612
1608
1613 @classmethod
1609 @classmethod
1614 def _get_or_create_key(cls, key, repo_name, commit=True):
1610 def _get_or_create_key(cls, key, repo_name, commit=True):
1615 inv_obj = Session().query(cls).filter(cls.cache_key == key).scalar()
1611 inv_obj = Session().query(cls).filter(cls.cache_key == key).scalar()
1616 if not inv_obj:
1612 if not inv_obj:
1617 try:
1613 try:
1618 inv_obj = CacheInvalidation(key, repo_name)
1614 inv_obj = CacheInvalidation(key, repo_name)
1619 Session().add(inv_obj)
1615 Session().add(inv_obj)
1620 if commit:
1616 if commit:
1621 Session().commit()
1617 Session().commit()
1622 except Exception:
1618 except Exception:
1623 log.error(traceback.format_exc())
1619 log.error(traceback.format_exc())
1624 Session().rollback()
1620 Session().rollback()
1625 return inv_obj
1621 return inv_obj
1626
1622
1627 @classmethod
1623 @classmethod
1628 def invalidate(cls, key):
1624 def invalidate(cls, key):
1629 """
1625 """
1630 Returns Invalidation object if this given key should be invalidated
1626 Returns Invalidation object if this given key should be invalidated
1631 None otherwise. `cache_active = False` means that this cache
1627 None otherwise. `cache_active = False` means that this cache
1632 state is not valid and needs to be invalidated
1628 state is not valid and needs to be invalidated
1633
1629
1634 :param key:
1630 :param key:
1635 """
1631 """
1636 repo_name = key
1632 repo_name = key
1637 repo_name = remove_suffix(repo_name, '_README')
1633 repo_name = remove_suffix(repo_name, '_README')
1638 repo_name = remove_suffix(repo_name, '_RSS')
1634 repo_name = remove_suffix(repo_name, '_RSS')
1639 repo_name = remove_suffix(repo_name, '_ATOM')
1635 repo_name = remove_suffix(repo_name, '_ATOM')
1640
1636
1641 # adds instance prefix
1637 # adds instance prefix
1642 key, _prefix, _org_key = cls._get_key(key)
1638 key, _prefix, _org_key = cls._get_key(key)
1643 inv = cls._get_or_create_key(key, repo_name)
1639 inv = cls._get_or_create_key(key, repo_name)
1644
1640
1645 if inv and inv.cache_active is False:
1641 if inv and inv.cache_active is False:
1646 return inv
1642 return inv
1647
1643
1648 @classmethod
1644 @classmethod
1649 def set_invalidate(cls, key=None, repo_name=None):
1645 def set_invalidate(cls, key=None, repo_name=None):
1650 """
1646 """
1651 Mark this Cache key for invalidation, either by key or whole
1647 Mark this Cache key for invalidation, either by key or whole
1652 cache sets based on repo_name
1648 cache sets based on repo_name
1653
1649
1654 :param key:
1650 :param key:
1655 """
1651 """
1656 invalidated_keys = []
1652 invalidated_keys = []
1657 if key:
1653 if key:
1658 key, _prefix, _org_key = cls._get_key(key)
1654 key, _prefix, _org_key = cls._get_key(key)
1659 inv_objs = Session().query(cls).filter(cls.cache_key == key).all()
1655 inv_objs = Session().query(cls).filter(cls.cache_key == key).all()
1660 elif repo_name:
1656 elif repo_name:
1661 inv_objs = Session().query(cls).filter(cls.cache_args == repo_name).all()
1657 inv_objs = Session().query(cls).filter(cls.cache_args == repo_name).all()
1662
1658
1663 try:
1659 try:
1664 for inv_obj in inv_objs:
1660 for inv_obj in inv_objs:
1665 inv_obj.cache_active = False
1661 inv_obj.cache_active = False
1666 log.debug('marking %s key for invalidation based on key=%s,repo_name=%s'
1662 log.debug('marking %s key for invalidation based on key=%s,repo_name=%s'
1667 % (inv_obj, key, repo_name))
1663 % (inv_obj, key, repo_name))
1668 invalidated_keys.append(inv_obj.cache_key)
1664 invalidated_keys.append(inv_obj.cache_key)
1669 Session().add(inv_obj)
1665 Session().add(inv_obj)
1670 Session().commit()
1666 Session().commit()
1671 except Exception:
1667 except Exception:
1672 log.error(traceback.format_exc())
1668 log.error(traceback.format_exc())
1673 Session().rollback()
1669 Session().rollback()
1674 return invalidated_keys
1670 return invalidated_keys
1675
1671
1676 @classmethod
1672 @classmethod
1677 def set_valid(cls, key):
1673 def set_valid(cls, key):
1678 """
1674 """
1679 Mark this cache key as active and currently cached
1675 Mark this cache key as active and currently cached
1680
1676
1681 :param key:
1677 :param key:
1682 """
1678 """
1683 inv_obj = cls.get_by_key(key)
1679 inv_obj = cls.get_by_key(key)
1684 inv_obj.cache_active = True
1680 inv_obj.cache_active = True
1685 Session().add(inv_obj)
1681 Session().add(inv_obj)
1686 Session().commit()
1682 Session().commit()
1687
1683
1688 @classmethod
1684 @classmethod
1689 def get_cache_map(cls):
1685 def get_cache_map(cls):
1690
1686
1691 class cachemapdict(dict):
1687 class cachemapdict(dict):
1692
1688
1693 def __init__(self, *args, **kwargs):
1689 def __init__(self, *args, **kwargs):
1694 fixkey = kwargs.get('fixkey')
1690 fixkey = kwargs.get('fixkey')
1695 if fixkey:
1691 if fixkey:
1696 del kwargs['fixkey']
1692 del kwargs['fixkey']
1697 self.fixkey = fixkey
1693 self.fixkey = fixkey
1698 super(cachemapdict, self).__init__(*args, **kwargs)
1694 super(cachemapdict, self).__init__(*args, **kwargs)
1699
1695
1700 def __getattr__(self, name):
1696 def __getattr__(self, name):
1701 key = name
1697 key = name
1702 if self.fixkey:
1698 if self.fixkey:
1703 key, _prefix, _org_key = cls._get_key(key)
1699 key, _prefix, _org_key = cls._get_key(key)
1704 if key in self.__dict__:
1700 if key in self.__dict__:
1705 return self.__dict__[key]
1701 return self.__dict__[key]
1706 else:
1702 else:
1707 return self[key]
1703 return self[key]
1708
1704
1709 def __getitem__(self, key):
1705 def __getitem__(self, key):
1710 if self.fixkey:
1706 if self.fixkey:
1711 key, _prefix, _org_key = cls._get_key(key)
1707 key, _prefix, _org_key = cls._get_key(key)
1712 try:
1708 try:
1713 return super(cachemapdict, self).__getitem__(key)
1709 return super(cachemapdict, self).__getitem__(key)
1714 except KeyError:
1710 except KeyError:
1715 return
1711 return
1716
1712
1717 cache_map = cachemapdict(fixkey=True)
1713 cache_map = cachemapdict(fixkey=True)
1718 for obj in cls.query().all():
1714 for obj in cls.query().all():
1719 cache_map[obj.cache_key] = cachemapdict(obj.get_dict())
1715 cache_map[obj.cache_key] = cachemapdict(obj.get_dict())
1720 return cache_map
1716 return cache_map
1721
1717
1722
1718
1723 class ChangesetComment(Base, BaseModel):
1719 class ChangesetComment(Base, BaseModel):
1724 __tablename__ = 'changeset_comments'
1720 __tablename__ = 'changeset_comments'
1725 __table_args__ = (
1721 __table_args__ = (
1726 Index('cc_revision_idx', 'revision'),
1722 Index('cc_revision_idx', 'revision'),
1727 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1723 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1728 'mysql_charset': 'utf8'},
1724 'mysql_charset': 'utf8'},
1729 )
1725 )
1730 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1726 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1731 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1727 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1732 revision = Column('revision', String(40), nullable=True)
1728 revision = Column('revision', String(40), nullable=True)
1733 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1729 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1734 line_no = Column('line_no', Unicode(10), nullable=True)
1730 line_no = Column('line_no', Unicode(10), nullable=True)
1735 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
1731 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
1736 f_path = Column('f_path', Unicode(1000), nullable=True)
1732 f_path = Column('f_path', Unicode(1000), nullable=True)
1737 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1733 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1738 text = Column('text', UnicodeText(25000), nullable=False)
1734 text = Column('text', UnicodeText(25000), nullable=False)
1739 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1735 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1740 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1736 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1741
1737
1742 author = relationship('User', lazy='joined')
1738 author = relationship('User', lazy='joined')
1743 repo = relationship('Repository')
1739 repo = relationship('Repository')
1744 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1740 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1745 pull_request = relationship('PullRequest', lazy='joined')
1741 pull_request = relationship('PullRequest', lazy='joined')
1746
1742
1747 @classmethod
1743 @classmethod
1748 def get_users(cls, revision=None, pull_request_id=None):
1744 def get_users(cls, revision=None, pull_request_id=None):
1749 """
1745 """
1750 Returns user associated with this ChangesetComment. ie those
1746 Returns user associated with this ChangesetComment. ie those
1751 who actually commented
1747 who actually commented
1752
1748
1753 :param cls:
1749 :param cls:
1754 :param revision:
1750 :param revision:
1755 """
1751 """
1756 q = Session().query(User)\
1752 q = Session().query(User)\
1757 .join(ChangesetComment.author)
1753 .join(ChangesetComment.author)
1758 if revision:
1754 if revision:
1759 q = q.filter(cls.revision == revision)
1755 q = q.filter(cls.revision == revision)
1760 elif pull_request_id:
1756 elif pull_request_id:
1761 q = q.filter(cls.pull_request_id == pull_request_id)
1757 q = q.filter(cls.pull_request_id == pull_request_id)
1762 return q.all()
1758 return q.all()
1763
1759
1764
1760
1765 class ChangesetStatus(Base, BaseModel):
1761 class ChangesetStatus(Base, BaseModel):
1766 __tablename__ = 'changeset_statuses'
1762 __tablename__ = 'changeset_statuses'
1767 __table_args__ = (
1763 __table_args__ = (
1768 Index('cs_revision_idx', 'revision'),
1764 Index('cs_revision_idx', 'revision'),
1769 Index('cs_version_idx', 'version'),
1765 Index('cs_version_idx', 'version'),
1770 UniqueConstraint('repo_id', 'revision', 'version'),
1766 UniqueConstraint('repo_id', 'revision', 'version'),
1771 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1767 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1772 'mysql_charset': 'utf8'}
1768 'mysql_charset': 'utf8'}
1773 )
1769 )
1774 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1770 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1775 STATUS_APPROVED = 'approved'
1771 STATUS_APPROVED = 'approved'
1776 STATUS_REJECTED = 'rejected'
1772 STATUS_REJECTED = 'rejected'
1777 STATUS_UNDER_REVIEW = 'under_review'
1773 STATUS_UNDER_REVIEW = 'under_review'
1778
1774
1779 STATUSES = [
1775 STATUSES = [
1780 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1776 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1781 (STATUS_APPROVED, _("Approved")),
1777 (STATUS_APPROVED, _("Approved")),
1782 (STATUS_REJECTED, _("Rejected")),
1778 (STATUS_REJECTED, _("Rejected")),
1783 (STATUS_UNDER_REVIEW, _("Under Review")),
1779 (STATUS_UNDER_REVIEW, _("Under Review")),
1784 ]
1780 ]
1785
1781
1786 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1782 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1787 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1783 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1788 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1784 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1789 revision = Column('revision', String(40), nullable=False)
1785 revision = Column('revision', String(40), nullable=False)
1790 status = Column('status', String(128), nullable=False, default=DEFAULT)
1786 status = Column('status', String(128), nullable=False, default=DEFAULT)
1791 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1787 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1792 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1788 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1793 version = Column('version', Integer(), nullable=False, default=0)
1789 version = Column('version', Integer(), nullable=False, default=0)
1794 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1790 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1795
1791
1796 author = relationship('User', lazy='joined')
1792 author = relationship('User', lazy='joined')
1797 repo = relationship('Repository')
1793 repo = relationship('Repository')
1798 comment = relationship('ChangesetComment', lazy='joined')
1794 comment = relationship('ChangesetComment', lazy='joined')
1799 pull_request = relationship('PullRequest', lazy='joined')
1795 pull_request = relationship('PullRequest', lazy='joined')
1800
1796
1801 def __unicode__(self):
1797 def __unicode__(self):
1802 return u"<%s('%s:%s')>" % (
1798 return u"<%s('%s:%s')>" % (
1803 self.__class__.__name__,
1799 self.__class__.__name__,
1804 self.status, self.author
1800 self.status, self.author
1805 )
1801 )
1806
1802
1807 @classmethod
1803 @classmethod
1808 def get_status_lbl(cls, value):
1804 def get_status_lbl(cls, value):
1809 return dict(cls.STATUSES).get(value)
1805 return dict(cls.STATUSES).get(value)
1810
1806
1811 @property
1807 @property
1812 def status_lbl(self):
1808 def status_lbl(self):
1813 return ChangesetStatus.get_status_lbl(self.status)
1809 return ChangesetStatus.get_status_lbl(self.status)
1814
1810
1815
1811
1816 class PullRequest(Base, BaseModel):
1812 class PullRequest(Base, BaseModel):
1817 __tablename__ = 'pull_requests'
1813 __tablename__ = 'pull_requests'
1818 __table_args__ = (
1814 __table_args__ = (
1819 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1815 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1820 'mysql_charset': 'utf8'},
1816 'mysql_charset': 'utf8'},
1821 )
1817 )
1822
1818
1823 STATUS_NEW = u'new'
1819 STATUS_NEW = u'new'
1824 STATUS_OPEN = u'open'
1820 STATUS_OPEN = u'open'
1825 STATUS_CLOSED = u'closed'
1821 STATUS_CLOSED = u'closed'
1826
1822
1827 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1823 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1828 title = Column('title', Unicode(256), nullable=True)
1824 title = Column('title', Unicode(256), nullable=True)
1829 description = Column('description', UnicodeText(10240), nullable=True)
1825 description = Column('description', UnicodeText(10240), nullable=True)
1830 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1826 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1831 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1827 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1832 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1828 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1833 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1829 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1834 _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
1830 _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
1835 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1831 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1836 org_ref = Column('org_ref', Unicode(256), nullable=False)
1832 org_ref = Column('org_ref', Unicode(256), nullable=False)
1837 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1833 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1838 other_ref = Column('other_ref', Unicode(256), nullable=False)
1834 other_ref = Column('other_ref', Unicode(256), nullable=False)
1839
1835
1840 @hybrid_property
1836 @hybrid_property
1841 def revisions(self):
1837 def revisions(self):
1842 return self._revisions.split(':')
1838 return self._revisions.split(':')
1843
1839
1844 @revisions.setter
1840 @revisions.setter
1845 def revisions(self, val):
1841 def revisions(self, val):
1846 self._revisions = ':'.join(val)
1842 self._revisions = ':'.join(val)
1847
1843
1848 @property
1844 @property
1849 def org_ref_parts(self):
1845 def org_ref_parts(self):
1850 return self.org_ref.split(':')
1846 return self.org_ref.split(':')
1851
1847
1852 @property
1848 @property
1853 def other_ref_parts(self):
1849 def other_ref_parts(self):
1854 return self.other_ref.split(':')
1850 return self.other_ref.split(':')
1855
1851
1856 author = relationship('User', lazy='joined')
1852 author = relationship('User', lazy='joined')
1857 reviewers = relationship('PullRequestReviewers',
1853 reviewers = relationship('PullRequestReviewers',
1858 cascade="all, delete, delete-orphan")
1854 cascade="all, delete, delete-orphan")
1859 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1855 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1860 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1856 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1861 statuses = relationship('ChangesetStatus')
1857 statuses = relationship('ChangesetStatus')
1862 comments = relationship('ChangesetComment',
1858 comments = relationship('ChangesetComment',
1863 cascade="all, delete, delete-orphan")
1859 cascade="all, delete, delete-orphan")
1864
1860
1865 def is_closed(self):
1861 def is_closed(self):
1866 return self.status == self.STATUS_CLOSED
1862 return self.status == self.STATUS_CLOSED
1867
1863
1868 def __json__(self):
1864 def __json__(self):
1869 return dict(
1865 return dict(
1870 revisions=self.revisions
1866 revisions=self.revisions
1871 )
1867 )
1872
1868
1873
1869
1874 class PullRequestReviewers(Base, BaseModel):
1870 class PullRequestReviewers(Base, BaseModel):
1875 __tablename__ = 'pull_request_reviewers'
1871 __tablename__ = 'pull_request_reviewers'
1876 __table_args__ = (
1872 __table_args__ = (
1877 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1873 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1878 'mysql_charset': 'utf8'},
1874 'mysql_charset': 'utf8'},
1879 )
1875 )
1880
1876
1881 def __init__(self, user=None, pull_request=None):
1877 def __init__(self, user=None, pull_request=None):
1882 self.user = user
1878 self.user = user
1883 self.pull_request = pull_request
1879 self.pull_request = pull_request
1884
1880
1885 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1881 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1886 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1882 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1887 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1883 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1888
1884
1889 user = relationship('User')
1885 user = relationship('User')
1890 pull_request = relationship('PullRequest')
1886 pull_request = relationship('PullRequest')
1891
1887
1892
1888
1893 class Notification(Base, BaseModel):
1889 class Notification(Base, BaseModel):
1894 __tablename__ = 'notifications'
1890 __tablename__ = 'notifications'
1895 __table_args__ = (
1891 __table_args__ = (
1896 Index('notification_type_idx', 'type'),
1892 Index('notification_type_idx', 'type'),
1897 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1893 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1898 'mysql_charset': 'utf8'},
1894 'mysql_charset': 'utf8'},
1899 )
1895 )
1900
1896
1901 TYPE_CHANGESET_COMMENT = u'cs_comment'
1897 TYPE_CHANGESET_COMMENT = u'cs_comment'
1902 TYPE_MESSAGE = u'message'
1898 TYPE_MESSAGE = u'message'
1903 TYPE_MENTION = u'mention'
1899 TYPE_MENTION = u'mention'
1904 TYPE_REGISTRATION = u'registration'
1900 TYPE_REGISTRATION = u'registration'
1905 TYPE_PULL_REQUEST = u'pull_request'
1901 TYPE_PULL_REQUEST = u'pull_request'
1906 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1902 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1907
1903
1908 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1904 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1909 subject = Column('subject', Unicode(512), nullable=True)
1905 subject = Column('subject', Unicode(512), nullable=True)
1910 body = Column('body', UnicodeText(50000), nullable=True)
1906 body = Column('body', UnicodeText(50000), nullable=True)
1911 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1907 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1912 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1908 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1913 type_ = Column('type', Unicode(256))
1909 type_ = Column('type', Unicode(256))
1914
1910
1915 created_by_user = relationship('User')
1911 created_by_user = relationship('User')
1916 notifications_to_users = relationship('UserNotification', lazy='joined',
1912 notifications_to_users = relationship('UserNotification', lazy='joined',
1917 cascade="all, delete, delete-orphan")
1913 cascade="all, delete, delete-orphan")
1918
1914
1919 @property
1915 @property
1920 def recipients(self):
1916 def recipients(self):
1921 return [x.user for x in UserNotification.query()\
1917 return [x.user for x in UserNotification.query()\
1922 .filter(UserNotification.notification == self)\
1918 .filter(UserNotification.notification == self)\
1923 .order_by(UserNotification.user_id.asc()).all()]
1919 .order_by(UserNotification.user_id.asc()).all()]
1924
1920
1925 @classmethod
1921 @classmethod
1926 def create(cls, created_by, subject, body, recipients, type_=None):
1922 def create(cls, created_by, subject, body, recipients, type_=None):
1927 if type_ is None:
1923 if type_ is None:
1928 type_ = Notification.TYPE_MESSAGE
1924 type_ = Notification.TYPE_MESSAGE
1929
1925
1930 notification = cls()
1926 notification = cls()
1931 notification.created_by_user = created_by
1927 notification.created_by_user = created_by
1932 notification.subject = subject
1928 notification.subject = subject
1933 notification.body = body
1929 notification.body = body
1934 notification.type_ = type_
1930 notification.type_ = type_
1935 notification.created_on = datetime.datetime.now()
1931 notification.created_on = datetime.datetime.now()
1936
1932
1937 for u in recipients:
1933 for u in recipients:
1938 assoc = UserNotification()
1934 assoc = UserNotification()
1939 assoc.notification = notification
1935 assoc.notification = notification
1940 u.notifications.append(assoc)
1936 u.notifications.append(assoc)
1941 Session().add(notification)
1937 Session().add(notification)
1942 return notification
1938 return notification
1943
1939
1944 @property
1940 @property
1945 def description(self):
1941 def description(self):
1946 from rhodecode.model.notification import NotificationModel
1942 from rhodecode.model.notification import NotificationModel
1947 return NotificationModel().make_description(self)
1943 return NotificationModel().make_description(self)
1948
1944
1949
1945
1950 class UserNotification(Base, BaseModel):
1946 class UserNotification(Base, BaseModel):
1951 __tablename__ = 'user_to_notification'
1947 __tablename__ = 'user_to_notification'
1952 __table_args__ = (
1948 __table_args__ = (
1953 UniqueConstraint('user_id', 'notification_id'),
1949 UniqueConstraint('user_id', 'notification_id'),
1954 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1950 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1955 'mysql_charset': 'utf8'}
1951 'mysql_charset': 'utf8'}
1956 )
1952 )
1957 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1953 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1958 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1954 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1959 read = Column('read', Boolean, default=False)
1955 read = Column('read', Boolean, default=False)
1960 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1956 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1961
1957
1962 user = relationship('User', lazy="joined")
1958 user = relationship('User', lazy="joined")
1963 notification = relationship('Notification', lazy="joined",
1959 notification = relationship('Notification', lazy="joined",
1964 order_by=lambda: Notification.created_on.desc(),)
1960 order_by=lambda: Notification.created_on.desc(),)
1965
1961
1966 def mark_as_read(self):
1962 def mark_as_read(self):
1967 self.read = True
1963 self.read = True
1968 Session().add(self)
1964 Session().add(self)
1969
1965
1970
1966
1971 class DbMigrateVersion(Base, BaseModel):
1967 class DbMigrateVersion(Base, BaseModel):
1972 __tablename__ = 'db_migrate_version'
1968 __tablename__ = 'db_migrate_version'
1973 __table_args__ = (
1969 __table_args__ = (
1974 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1970 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1975 'mysql_charset': 'utf8'},
1971 'mysql_charset': 'utf8'},
1976 )
1972 )
1977 repository_id = Column('repository_id', String(250), primary_key=True)
1973 repository_id = Column('repository_id', String(250), primary_key=True)
1978 repository_path = Column('repository_path', Text)
1974 repository_path = Column('repository_path', Text)
1979 version = Column('version', Integer)
1975 version = Column('version', Integer)
General Comments 0
You need to be logged in to leave comments. Login now