##// END OF EJS Templates
Renamed name to firstname in forms...
marcink -
r2544:6ce3387b beta
parent child Browse files
Show More
@@ -1,427 +1,426 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
44 set_rhodecode_config, repo_name_slug
45 from rhodecode.model.db import RhodeCodeUi, Repository, RepoGroup, \
45 from rhodecode.model.db import RhodeCodeUi, Repository, RepoGroup, \
46 RhodeCodeSetting
46 RhodeCodeSetting
47 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
47 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
48 ApplicationUiSettingsForm
48 ApplicationUiSettingsForm
49 from rhodecode.model.scm import ScmModel
49 from rhodecode.model.scm import ScmModel
50 from rhodecode.model.user import UserModel
50 from rhodecode.model.user import UserModel
51 from rhodecode.model.db import User
51 from rhodecode.model.db import User
52 from rhodecode.model.notification import EmailNotificationModel
52 from rhodecode.model.notification import EmailNotificationModel
53 from rhodecode.model.meta import Session
53 from rhodecode.model.meta import Session
54
54
55 log = logging.getLogger(__name__)
55 log = logging.getLogger(__name__)
56
56
57
57
58 class SettingsController(BaseController):
58 class SettingsController(BaseController):
59 """REST Controller styled on the Atom Publishing Protocol"""
59 """REST Controller styled on the Atom Publishing Protocol"""
60 # To properly map this controller, ensure your config/routing.py
60 # To properly map this controller, ensure your config/routing.py
61 # file has a resource setup:
61 # file has a resource setup:
62 # map.resource('setting', 'settings', controller='admin/settings',
62 # map.resource('setting', 'settings', controller='admin/settings',
63 # path_prefix='/admin', name_prefix='admin_')
63 # path_prefix='/admin', name_prefix='admin_')
64
64
65 @LoginRequired()
65 @LoginRequired()
66 def __before__(self):
66 def __before__(self):
67 c.admin_user = session.get('admin_user')
67 c.admin_user = session.get('admin_user')
68 c.admin_username = session.get('admin_username')
68 c.admin_username = session.get('admin_username')
69 c.modules = sorted([(p.project_name, p.version)
69 c.modules = sorted([(p.project_name, p.version)
70 for p in pkg_resources.working_set],
70 for p in pkg_resources.working_set],
71 key=lambda k: k[0].lower())
71 key=lambda k: k[0].lower())
72 c.py_version = platform.python_version()
72 c.py_version = platform.python_version()
73 c.platform = platform.platform()
73 c.platform = platform.platform()
74 super(SettingsController, self).__before__()
74 super(SettingsController, self).__before__()
75
75
76 @HasPermissionAllDecorator('hg.admin')
76 @HasPermissionAllDecorator('hg.admin')
77 def index(self, format='html'):
77 def index(self, format='html'):
78 """GET /admin/settings: All items in the collection"""
78 """GET /admin/settings: All items in the collection"""
79 # url('admin_settings')
79 # url('admin_settings')
80
80
81 defaults = RhodeCodeSetting.get_app_settings()
81 defaults = RhodeCodeSetting.get_app_settings()
82 defaults.update(self.get_hg_ui_settings())
82 defaults.update(self.get_hg_ui_settings())
83
83
84 return htmlfill.render(
84 return htmlfill.render(
85 render('admin/settings/settings.html'),
85 render('admin/settings/settings.html'),
86 defaults=defaults,
86 defaults=defaults,
87 encoding="UTF-8",
87 encoding="UTF-8",
88 force_defaults=False
88 force_defaults=False
89 )
89 )
90
90
91 @HasPermissionAllDecorator('hg.admin')
91 @HasPermissionAllDecorator('hg.admin')
92 def create(self):
92 def create(self):
93 """POST /admin/settings: Create a new item"""
93 """POST /admin/settings: Create a new item"""
94 # url('admin_settings')
94 # url('admin_settings')
95
95
96 @HasPermissionAllDecorator('hg.admin')
96 @HasPermissionAllDecorator('hg.admin')
97 def new(self, format='html'):
97 def new(self, format='html'):
98 """GET /admin/settings/new: Form to create a new item"""
98 """GET /admin/settings/new: Form to create a new item"""
99 # url('admin_new_setting')
99 # url('admin_new_setting')
100
100
101 @HasPermissionAllDecorator('hg.admin')
101 @HasPermissionAllDecorator('hg.admin')
102 def update(self, setting_id):
102 def update(self, setting_id):
103 """PUT /admin/settings/setting_id: Update an existing item"""
103 """PUT /admin/settings/setting_id: Update an existing item"""
104 # Forms posted to this method should contain a hidden field:
104 # Forms posted to this method should contain a hidden field:
105 # <input type="hidden" name="_method" value="PUT" />
105 # <input type="hidden" name="_method" value="PUT" />
106 # Or using helpers:
106 # Or using helpers:
107 # h.form(url('admin_setting', setting_id=ID),
107 # h.form(url('admin_setting', setting_id=ID),
108 # method='put')
108 # method='put')
109 # url('admin_setting', setting_id=ID)
109 # url('admin_setting', setting_id=ID)
110 if setting_id == 'mapping':
110 if setting_id == 'mapping':
111 rm_obsolete = request.POST.get('destroy', False)
111 rm_obsolete = request.POST.get('destroy', False)
112 log.debug('Rescanning directories with destroy=%s' % rm_obsolete)
112 log.debug('Rescanning directories with destroy=%s' % rm_obsolete)
113 initial = ScmModel().repo_scan()
113 initial = ScmModel().repo_scan()
114 log.debug('invalidating all repositories')
114 log.debug('invalidating all repositories')
115 for repo_name in initial.keys():
115 for repo_name in initial.keys():
116 invalidate_cache('get_repo_cached_%s' % repo_name)
116 invalidate_cache('get_repo_cached_%s' % repo_name)
117
117
118 added, removed = repo2db_mapper(initial, rm_obsolete)
118 added, removed = repo2db_mapper(initial, rm_obsolete)
119
119
120 h.flash(_('Repositories successfully'
120 h.flash(_('Repositories successfully'
121 ' rescanned added: %s,removed: %s') % (added, removed),
121 ' rescanned added: %s,removed: %s') % (added, removed),
122 category='success')
122 category='success')
123
123
124 if setting_id == 'whoosh':
124 if setting_id == 'whoosh':
125 repo_location = self.get_hg_ui_settings()['paths_root_path']
125 repo_location = self.get_hg_ui_settings()['paths_root_path']
126 full_index = request.POST.get('full_index', False)
126 full_index = request.POST.get('full_index', False)
127 run_task(tasks.whoosh_index, repo_location, full_index)
127 run_task(tasks.whoosh_index, repo_location, full_index)
128
128
129 h.flash(_('Whoosh reindex task scheduled'), category='success')
129 h.flash(_('Whoosh reindex task scheduled'), category='success')
130 if setting_id == 'global':
130 if setting_id == 'global':
131
131
132 application_form = ApplicationSettingsForm()()
132 application_form = ApplicationSettingsForm()()
133 try:
133 try:
134 form_result = application_form.to_python(dict(request.POST))
134 form_result = application_form.to_python(dict(request.POST))
135
135
136 try:
136 try:
137 hgsettings1 = RhodeCodeSetting.get_by_name('title')
137 hgsettings1 = RhodeCodeSetting.get_by_name('title')
138 hgsettings1.app_settings_value = \
138 hgsettings1.app_settings_value = \
139 form_result['rhodecode_title']
139 form_result['rhodecode_title']
140
140
141 hgsettings2 = RhodeCodeSetting.get_by_name('realm')
141 hgsettings2 = RhodeCodeSetting.get_by_name('realm')
142 hgsettings2.app_settings_value = \
142 hgsettings2.app_settings_value = \
143 form_result['rhodecode_realm']
143 form_result['rhodecode_realm']
144
144
145 hgsettings3 = RhodeCodeSetting.get_by_name('ga_code')
145 hgsettings3 = RhodeCodeSetting.get_by_name('ga_code')
146 hgsettings3.app_settings_value = \
146 hgsettings3.app_settings_value = \
147 form_result['rhodecode_ga_code']
147 form_result['rhodecode_ga_code']
148
148
149 self.sa.add(hgsettings1)
149 self.sa.add(hgsettings1)
150 self.sa.add(hgsettings2)
150 self.sa.add(hgsettings2)
151 self.sa.add(hgsettings3)
151 self.sa.add(hgsettings3)
152 self.sa.commit()
152 self.sa.commit()
153 set_rhodecode_config(config)
153 set_rhodecode_config(config)
154 h.flash(_('Updated application settings'),
154 h.flash(_('Updated application settings'),
155 category='success')
155 category='success')
156
156
157 except Exception:
157 except Exception:
158 log.error(traceback.format_exc())
158 log.error(traceback.format_exc())
159 h.flash(_('error occurred during updating '
159 h.flash(_('error occurred during updating '
160 'application settings'),
160 'application settings'),
161 category='error')
161 category='error')
162
162
163 self.sa.rollback()
163 self.sa.rollback()
164
164
165 except formencode.Invalid, errors:
165 except formencode.Invalid, errors:
166 return htmlfill.render(
166 return htmlfill.render(
167 render('admin/settings/settings.html'),
167 render('admin/settings/settings.html'),
168 defaults=errors.value,
168 defaults=errors.value,
169 errors=errors.error_dict or {},
169 errors=errors.error_dict or {},
170 prefix_error=False,
170 prefix_error=False,
171 encoding="UTF-8")
171 encoding="UTF-8")
172
172
173 if setting_id == 'mercurial':
173 if setting_id == 'mercurial':
174 application_form = ApplicationUiSettingsForm()()
174 application_form = ApplicationUiSettingsForm()()
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 # fix namespaces for hooks
177 # fix namespaces for hooks
178 _f = lambda s: s.replace('.', '_')
178 _f = lambda s: s.replace('.', '_')
179 try:
179 try:
180
180
181 hgsettings1 = self.sa.query(RhodeCodeUi)\
181 hgsettings1 = self.sa.query(RhodeCodeUi)\
182 .filter(RhodeCodeUi.ui_key == 'push_ssl').one()
182 .filter(RhodeCodeUi.ui_key == 'push_ssl').one()
183 hgsettings1.ui_value = form_result['web_push_ssl']
183 hgsettings1.ui_value = form_result['web_push_ssl']
184
184
185 hgsettings2 = self.sa.query(RhodeCodeUi)\
185 hgsettings2 = self.sa.query(RhodeCodeUi)\
186 .filter(RhodeCodeUi.ui_key == '/').one()
186 .filter(RhodeCodeUi.ui_key == '/').one()
187 hgsettings2.ui_value = form_result['paths_root_path']
187 hgsettings2.ui_value = form_result['paths_root_path']
188
188
189 #HOOKS
189 #HOOKS
190 hgsettings3 = self.sa.query(RhodeCodeUi)\
190 hgsettings3 = self.sa.query(RhodeCodeUi)\
191 .filter(RhodeCodeUi.ui_key == RhodeCodeUi.HOOK_UPDATE)\
191 .filter(RhodeCodeUi.ui_key == RhodeCodeUi.HOOK_UPDATE)\
192 .one()
192 .one()
193 hgsettings3.ui_active = bool(form_result[_f('hooks_%s' %
193 hgsettings3.ui_active = bool(form_result[_f('hooks_%s' %
194 RhodeCodeUi.HOOK_UPDATE)])
194 RhodeCodeUi.HOOK_UPDATE)])
195
195
196 hgsettings4 = self.sa.query(RhodeCodeUi)\
196 hgsettings4 = self.sa.query(RhodeCodeUi)\
197 .filter(RhodeCodeUi.ui_key == RhodeCodeUi.HOOK_REPO_SIZE)\
197 .filter(RhodeCodeUi.ui_key == RhodeCodeUi.HOOK_REPO_SIZE)\
198 .one()
198 .one()
199 hgsettings4.ui_active = bool(form_result[_f('hooks_%s' %
199 hgsettings4.ui_active = bool(form_result[_f('hooks_%s' %
200 RhodeCodeUi.HOOK_REPO_SIZE)])
200 RhodeCodeUi.HOOK_REPO_SIZE)])
201
201
202 hgsettings5 = self.sa.query(RhodeCodeUi)\
202 hgsettings5 = self.sa.query(RhodeCodeUi)\
203 .filter(RhodeCodeUi.ui_key == RhodeCodeUi.HOOK_PUSH)\
203 .filter(RhodeCodeUi.ui_key == RhodeCodeUi.HOOK_PUSH)\
204 .one()
204 .one()
205 hgsettings5.ui_active = bool(form_result[_f('hooks_%s' %
205 hgsettings5.ui_active = bool(form_result[_f('hooks_%s' %
206 RhodeCodeUi.HOOK_PUSH)])
206 RhodeCodeUi.HOOK_PUSH)])
207
207
208 hgsettings6 = self.sa.query(RhodeCodeUi)\
208 hgsettings6 = self.sa.query(RhodeCodeUi)\
209 .filter(RhodeCodeUi.ui_key == RhodeCodeUi.HOOK_PULL)\
209 .filter(RhodeCodeUi.ui_key == RhodeCodeUi.HOOK_PULL)\
210 .one()
210 .one()
211 hgsettings6.ui_active = bool(form_result[_f('hooks_%s' %
211 hgsettings6.ui_active = bool(form_result[_f('hooks_%s' %
212 RhodeCodeUi.HOOK_PULL)])
212 RhodeCodeUi.HOOK_PULL)])
213
213
214 self.sa.add(hgsettings1)
214 self.sa.add(hgsettings1)
215 self.sa.add(hgsettings2)
215 self.sa.add(hgsettings2)
216 self.sa.add(hgsettings3)
216 self.sa.add(hgsettings3)
217 self.sa.add(hgsettings4)
217 self.sa.add(hgsettings4)
218 self.sa.add(hgsettings5)
218 self.sa.add(hgsettings5)
219 self.sa.add(hgsettings6)
219 self.sa.add(hgsettings6)
220 self.sa.commit()
220 self.sa.commit()
221
221
222 h.flash(_('Updated mercurial settings'),
222 h.flash(_('Updated mercurial settings'),
223 category='success')
223 category='success')
224
224
225 except:
225 except:
226 log.error(traceback.format_exc())
226 log.error(traceback.format_exc())
227 h.flash(_('error occurred during updating '
227 h.flash(_('error occurred during updating '
228 'application settings'), category='error')
228 'application settings'), category='error')
229
229
230 self.sa.rollback()
230 self.sa.rollback()
231
231
232 except formencode.Invalid, errors:
232 except formencode.Invalid, errors:
233 return htmlfill.render(
233 return htmlfill.render(
234 render('admin/settings/settings.html'),
234 render('admin/settings/settings.html'),
235 defaults=errors.value,
235 defaults=errors.value,
236 errors=errors.error_dict or {},
236 errors=errors.error_dict or {},
237 prefix_error=False,
237 prefix_error=False,
238 encoding="UTF-8")
238 encoding="UTF-8")
239
239
240 if setting_id == 'hooks':
240 if setting_id == 'hooks':
241 ui_key = request.POST.get('new_hook_ui_key')
241 ui_key = request.POST.get('new_hook_ui_key')
242 ui_value = request.POST.get('new_hook_ui_value')
242 ui_value = request.POST.get('new_hook_ui_value')
243 try:
243 try:
244
244
245 if ui_value and ui_key:
245 if ui_value and ui_key:
246 RhodeCodeUi.create_or_update_hook(ui_key, ui_value)
246 RhodeCodeUi.create_or_update_hook(ui_key, ui_value)
247 h.flash(_('Added new hook'),
247 h.flash(_('Added new hook'),
248 category='success')
248 category='success')
249
249
250 # check for edits
250 # check for edits
251 update = False
251 update = False
252 _d = request.POST.dict_of_lists()
252 _d = request.POST.dict_of_lists()
253 for k, v in zip(_d.get('hook_ui_key', []),
253 for k, v in zip(_d.get('hook_ui_key', []),
254 _d.get('hook_ui_value_new', [])):
254 _d.get('hook_ui_value_new', [])):
255 RhodeCodeUi.create_or_update_hook(k, v)
255 RhodeCodeUi.create_or_update_hook(k, v)
256 update = True
256 update = True
257
257
258 if update:
258 if update:
259 h.flash(_('Updated hooks'), category='success')
259 h.flash(_('Updated hooks'), category='success')
260 self.sa.commit()
260 self.sa.commit()
261 except:
261 except:
262 log.error(traceback.format_exc())
262 log.error(traceback.format_exc())
263 h.flash(_('error occurred during hook creation'),
263 h.flash(_('error occurred during hook creation'),
264 category='error')
264 category='error')
265
265
266 return redirect(url('admin_edit_setting', setting_id='hooks'))
266 return redirect(url('admin_edit_setting', setting_id='hooks'))
267
267
268 if setting_id == 'email':
268 if setting_id == 'email':
269 test_email = request.POST.get('test_email')
269 test_email = request.POST.get('test_email')
270 test_email_subj = 'RhodeCode TestEmail'
270 test_email_subj = 'RhodeCode TestEmail'
271 test_email_body = 'RhodeCode Email test'
271 test_email_body = 'RhodeCode Email test'
272
272
273 test_email_html_body = EmailNotificationModel()\
273 test_email_html_body = EmailNotificationModel()\
274 .get_email_tmpl(EmailNotificationModel.TYPE_DEFAULT,
274 .get_email_tmpl(EmailNotificationModel.TYPE_DEFAULT,
275 body=test_email_body)
275 body=test_email_body)
276
276
277 recipients = [test_email] if [test_email] else None
277 recipients = [test_email] if [test_email] else None
278
278
279 run_task(tasks.send_email, recipients, test_email_subj,
279 run_task(tasks.send_email, recipients, test_email_subj,
280 test_email_body, test_email_html_body)
280 test_email_body, test_email_html_body)
281
281
282 h.flash(_('Email task created'), category='success')
282 h.flash(_('Email task created'), category='success')
283 return redirect(url('admin_settings'))
283 return redirect(url('admin_settings'))
284
284
285 @HasPermissionAllDecorator('hg.admin')
285 @HasPermissionAllDecorator('hg.admin')
286 def delete(self, setting_id):
286 def delete(self, setting_id):
287 """DELETE /admin/settings/setting_id: Delete an existing item"""
287 """DELETE /admin/settings/setting_id: Delete an existing item"""
288 # Forms posted to this method should contain a hidden field:
288 # Forms posted to this method should contain a hidden field:
289 # <input type="hidden" name="_method" value="DELETE" />
289 # <input type="hidden" name="_method" value="DELETE" />
290 # Or using helpers:
290 # Or using helpers:
291 # h.form(url('admin_setting', setting_id=ID),
291 # h.form(url('admin_setting', setting_id=ID),
292 # method='delete')
292 # method='delete')
293 # url('admin_setting', setting_id=ID)
293 # url('admin_setting', setting_id=ID)
294 if setting_id == 'hooks':
294 if setting_id == 'hooks':
295 hook_id = request.POST.get('hook_id')
295 hook_id = request.POST.get('hook_id')
296 RhodeCodeUi.delete(hook_id)
296 RhodeCodeUi.delete(hook_id)
297 self.sa.commit()
297 self.sa.commit()
298
298
299 @HasPermissionAllDecorator('hg.admin')
299 @HasPermissionAllDecorator('hg.admin')
300 def show(self, setting_id, format='html'):
300 def show(self, setting_id, format='html'):
301 """
301 """
302 GET /admin/settings/setting_id: Show a specific item"""
302 GET /admin/settings/setting_id: Show a specific item"""
303 # url('admin_setting', setting_id=ID)
303 # url('admin_setting', setting_id=ID)
304
304
305 @HasPermissionAllDecorator('hg.admin')
305 @HasPermissionAllDecorator('hg.admin')
306 def edit(self, setting_id, format='html'):
306 def edit(self, setting_id, format='html'):
307 """
307 """
308 GET /admin/settings/setting_id/edit: Form to
308 GET /admin/settings/setting_id/edit: Form to
309 edit an existing item"""
309 edit an existing item"""
310 # url('admin_edit_setting', setting_id=ID)
310 # url('admin_edit_setting', setting_id=ID)
311
311
312 c.hooks = RhodeCodeUi.get_builtin_hooks()
312 c.hooks = RhodeCodeUi.get_builtin_hooks()
313 c.custom_hooks = RhodeCodeUi.get_custom_hooks()
313 c.custom_hooks = RhodeCodeUi.get_custom_hooks()
314
314
315 return htmlfill.render(
315 return htmlfill.render(
316 render('admin/settings/hooks.html'),
316 render('admin/settings/hooks.html'),
317 defaults={},
317 defaults={},
318 encoding="UTF-8",
318 encoding="UTF-8",
319 force_defaults=False
319 force_defaults=False
320 )
320 )
321
321
322 @NotAnonymous()
322 @NotAnonymous()
323 def my_account(self):
323 def my_account(self):
324 """
324 """
325 GET /_admin/my_account Displays info about my account
325 GET /_admin/my_account Displays info about my account
326 """
326 """
327 # url('admin_settings_my_account')
327 # url('admin_settings_my_account')
328
328
329 c.user = User.get(self.rhodecode_user.user_id)
329 c.user = User.get(self.rhodecode_user.user_id)
330 all_repos = self.sa.query(Repository)\
330 all_repos = self.sa.query(Repository)\
331 .filter(Repository.user_id == c.user.user_id)\
331 .filter(Repository.user_id == c.user.user_id)\
332 .order_by(func.lower(Repository.repo_name)).all()
332 .order_by(func.lower(Repository.repo_name)).all()
333
333
334 c.user_repos = ScmModel().get_repos(all_repos)
334 c.user_repos = ScmModel().get_repos(all_repos)
335
335
336 if c.user.username == 'default':
336 if c.user.username == 'default':
337 h.flash(_("You can't edit this user since it's"
337 h.flash(_("You can't edit this user since it's"
338 " crucial for entire application"), category='warning')
338 " crucial for entire application"), category='warning')
339 return redirect(url('users'))
339 return redirect(url('users'))
340
340
341 defaults = c.user.get_dict()
341 defaults = c.user.get_dict()
342
342
343 c.form = htmlfill.render(
343 c.form = htmlfill.render(
344 render('admin/users/user_edit_my_account_form.html'),
344 render('admin/users/user_edit_my_account_form.html'),
345 defaults=defaults,
345 defaults=defaults,
346 encoding="UTF-8",
346 encoding="UTF-8",
347 force_defaults=False
347 force_defaults=False
348 )
348 )
349 return render('admin/users/user_edit_my_account.html')
349 return render('admin/users/user_edit_my_account.html')
350
350
351 def my_account_update(self):
351 def my_account_update(self):
352 """PUT /_admin/my_account_update: Update an existing item"""
352 """PUT /_admin/my_account_update: Update an existing item"""
353 # Forms posted to this method should contain a hidden field:
353 # Forms posted to this method should contain a hidden field:
354 # <input type="hidden" name="_method" value="PUT" />
354 # <input type="hidden" name="_method" value="PUT" />
355 # Or using helpers:
355 # Or using helpers:
356 # h.form(url('admin_settings_my_account_update'),
356 # h.form(url('admin_settings_my_account_update'),
357 # method='put')
357 # method='put')
358 # url('admin_settings_my_account_update', id=ID)
358 # url('admin_settings_my_account_update', id=ID)
359 user_model = UserModel()
360 uid = self.rhodecode_user.user_id
359 uid = self.rhodecode_user.user_id
360 email = self.rhodecode_user.email
361 _form = UserForm(edit=True,
361 _form = UserForm(edit=True,
362 old_data={'user_id': uid,
362 old_data={'user_id': uid, 'email': email})()
363 'email': self.rhodecode_user.email})()
364 form_result = {}
363 form_result = {}
365 try:
364 try:
366 form_result = _form.to_python(dict(request.POST))
365 form_result = _form.to_python(dict(request.POST))
367 user_model.update_my_account(uid, form_result)
366 UserModel().update_my_account(uid, form_result)
368 h.flash(_('Your account was updated successfully'),
367 h.flash(_('Your account was updated successfully'),
369 category='success')
368 category='success')
370 Session.commit()
369 Session.commit()
371 except formencode.Invalid, errors:
370 except formencode.Invalid, errors:
372 c.user = User.get(self.rhodecode_user.user_id)
371 c.user = User.get(self.rhodecode_user.user_id)
373 all_repos = self.sa.query(Repository)\
372 all_repos = self.sa.query(Repository)\
374 .filter(Repository.user_id == c.user.user_id)\
373 .filter(Repository.user_id == c.user.user_id)\
375 .order_by(func.lower(Repository.repo_name))\
374 .order_by(func.lower(Repository.repo_name))\
376 .all()
375 .all()
377 c.user_repos = ScmModel().get_repos(all_repos)
376 c.user_repos = ScmModel().get_repos(all_repos)
378
377
379 c.form = htmlfill.render(
378 c.form = htmlfill.render(
380 render('admin/users/user_edit_my_account_form.html'),
379 render('admin/users/user_edit_my_account_form.html'),
381 defaults=errors.value,
380 defaults=errors.value,
382 errors=errors.error_dict or {},
381 errors=errors.error_dict or {},
383 prefix_error=False,
382 prefix_error=False,
384 encoding="UTF-8")
383 encoding="UTF-8")
385 return render('admin/users/user_edit_my_account.html')
384 return render('admin/users/user_edit_my_account.html')
386 except Exception:
385 except Exception:
387 log.error(traceback.format_exc())
386 log.error(traceback.format_exc())
388 h.flash(_('error occurred during update of user %s') \
387 h.flash(_('error occurred during update of user %s') \
389 % form_result.get('username'), category='error')
388 % form_result.get('username'), category='error')
390
389
391 return redirect(url('my_account'))
390 return redirect(url('my_account'))
392
391
393 @NotAnonymous()
392 @NotAnonymous()
394 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
393 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
395 def create_repository(self):
394 def create_repository(self):
396 """GET /_admin/create_repository: Form to create a new item"""
395 """GET /_admin/create_repository: Form to create a new item"""
397
396
398 c.repo_groups = RepoGroup.groups_choices()
397 c.repo_groups = RepoGroup.groups_choices()
399 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
398 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
400 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
399 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
401
400
402 new_repo = request.GET.get('repo', '')
401 new_repo = request.GET.get('repo', '')
403 c.new_repo = repo_name_slug(new_repo)
402 c.new_repo = repo_name_slug(new_repo)
404
403
405 return render('admin/repos/repo_add_create_repository.html')
404 return render('admin/repos/repo_add_create_repository.html')
406
405
407 def get_hg_ui_settings(self):
406 def get_hg_ui_settings(self):
408 ret = self.sa.query(RhodeCodeUi).all()
407 ret = self.sa.query(RhodeCodeUi).all()
409
408
410 if not ret:
409 if not ret:
411 raise Exception('Could not get application ui settings !')
410 raise Exception('Could not get application ui settings !')
412 settings = {}
411 settings = {}
413 for each in ret:
412 for each in ret:
414 k = each.ui_key
413 k = each.ui_key
415 v = each.ui_value
414 v = each.ui_value
416 if k == '/':
415 if k == '/':
417 k = 'root_path'
416 k = 'root_path'
418
417
419 if k.find('.') != -1:
418 if k.find('.') != -1:
420 k = k.replace('.', '_')
419 k = k.replace('.', '_')
421
420
422 if each.ui_section == 'hooks':
421 if each.ui_section == 'hooks':
423 v = each.ui_active
422 v = each.ui_active
424
423
425 settings[each.ui_section + '_' + k] = v
424 settings[each.ui_section + '_' + k] = v
426
425
427 return settings
426 return settings
@@ -1,1667 +1,1665 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 from collections import defaultdict
31 from collections import defaultdict
32
32
33 from sqlalchemy import *
33 from sqlalchemy import *
34 from sqlalchemy.ext.hybrid import hybrid_property
34 from sqlalchemy.ext.hybrid import hybrid_property
35 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
35 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
36 from sqlalchemy.exc import DatabaseError
36 from sqlalchemy.exc import DatabaseError
37 from beaker.cache import cache_region, region_invalidate
37 from beaker.cache import cache_region, region_invalidate
38 from webob.exc import HTTPNotFound
38 from webob.exc import HTTPNotFound
39
39
40 from pylons.i18n.translation import lazy_ugettext as _
40 from pylons.i18n.translation import lazy_ugettext as _
41
41
42 from rhodecode.lib.vcs import get_backend
42 from rhodecode.lib.vcs import get_backend
43 from rhodecode.lib.vcs.utils.helpers import get_scm
43 from rhodecode.lib.vcs.utils.helpers import get_scm
44 from rhodecode.lib.vcs.exceptions import VCSError
44 from rhodecode.lib.vcs.exceptions import VCSError
45 from rhodecode.lib.vcs.utils.lazy import LazyProperty
45 from rhodecode.lib.vcs.utils.lazy import LazyProperty
46
46
47 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
47 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
48 safe_unicode
48 safe_unicode
49 from rhodecode.lib.compat import json
49 from rhodecode.lib.compat import json
50 from rhodecode.lib.caching_query import FromCache
50 from rhodecode.lib.caching_query import FromCache
51
51
52 from rhodecode.model.meta import Base, Session
52 from rhodecode.model.meta import Base, Session
53
53
54 URL_SEP = '/'
54 URL_SEP = '/'
55 log = logging.getLogger(__name__)
55 log = logging.getLogger(__name__)
56
56
57 #==============================================================================
57 #==============================================================================
58 # BASE CLASSES
58 # BASE CLASSES
59 #==============================================================================
59 #==============================================================================
60
60
61 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
61 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
62
62
63
63
64 class ModelSerializer(json.JSONEncoder):
64 class ModelSerializer(json.JSONEncoder):
65 """
65 """
66 Simple Serializer for JSON,
66 Simple Serializer for JSON,
67
67
68 usage::
68 usage::
69
69
70 to make object customized for serialization implement a __json__
70 to make object customized for serialization implement a __json__
71 method that will return a dict for serialization into json
71 method that will return a dict for serialization into json
72
72
73 example::
73 example::
74
74
75 class Task(object):
75 class Task(object):
76
76
77 def __init__(self, name, value):
77 def __init__(self, name, value):
78 self.name = name
78 self.name = name
79 self.value = value
79 self.value = value
80
80
81 def __json__(self):
81 def __json__(self):
82 return dict(name=self.name,
82 return dict(name=self.name,
83 value=self.value)
83 value=self.value)
84
84
85 """
85 """
86
86
87 def default(self, obj):
87 def default(self, obj):
88
88
89 if hasattr(obj, '__json__'):
89 if hasattr(obj, '__json__'):
90 return obj.__json__()
90 return obj.__json__()
91 else:
91 else:
92 return json.JSONEncoder.default(self, obj)
92 return json.JSONEncoder.default(self, obj)
93
93
94
94
95 class BaseModel(object):
95 class BaseModel(object):
96 """
96 """
97 Base Model for all classess
97 Base Model for all classess
98 """
98 """
99
99
100 @classmethod
100 @classmethod
101 def _get_keys(cls):
101 def _get_keys(cls):
102 """return column names for this model """
102 """return column names for this model """
103 return class_mapper(cls).c.keys()
103 return class_mapper(cls).c.keys()
104
104
105 def get_dict(self):
105 def get_dict(self):
106 """
106 """
107 return dict with keys and values corresponding
107 return dict with keys and values corresponding
108 to this model data """
108 to this model data """
109
109
110 d = {}
110 d = {}
111 for k in self._get_keys():
111 for k in self._get_keys():
112 d[k] = getattr(self, k)
112 d[k] = getattr(self, k)
113
113
114 # also use __json__() if present to get additional fields
114 # also use __json__() if present to get additional fields
115 for k, val in getattr(self, '__json__', lambda: {})().iteritems():
115 for k, val in getattr(self, '__json__', lambda: {})().iteritems():
116 d[k] = val
116 d[k] = val
117 return d
117 return d
118
118
119 def get_appstruct(self):
119 def get_appstruct(self):
120 """return list with keys and values tupples corresponding
120 """return list with keys and values tupples corresponding
121 to this model data """
121 to this model data """
122
122
123 l = []
123 l = []
124 for k in self._get_keys():
124 for k in self._get_keys():
125 l.append((k, getattr(self, k),))
125 l.append((k, getattr(self, k),))
126 return l
126 return l
127
127
128 def populate_obj(self, populate_dict):
128 def populate_obj(self, populate_dict):
129 """populate model with data from given populate_dict"""
129 """populate model with data from given populate_dict"""
130
130
131 for k in self._get_keys():
131 for k in self._get_keys():
132 if k in populate_dict:
132 if k in populate_dict:
133 setattr(self, k, populate_dict[k])
133 setattr(self, k, populate_dict[k])
134
134
135 @classmethod
135 @classmethod
136 def query(cls):
136 def query(cls):
137 return Session().query(cls)
137 return Session().query(cls)
138
138
139 @classmethod
139 @classmethod
140 def get(cls, id_):
140 def get(cls, id_):
141 if id_:
141 if id_:
142 return cls.query().get(id_)
142 return cls.query().get(id_)
143
143
144 @classmethod
144 @classmethod
145 def get_or_404(cls, id_):
145 def get_or_404(cls, id_):
146 if id_:
146 if id_:
147 res = cls.query().get(id_)
147 res = cls.query().get(id_)
148 if not res:
148 if not res:
149 raise HTTPNotFound
149 raise HTTPNotFound
150 return res
150 return res
151
151
152 @classmethod
152 @classmethod
153 def getAll(cls):
153 def getAll(cls):
154 return cls.query().all()
154 return cls.query().all()
155
155
156 @classmethod
156 @classmethod
157 def delete(cls, id_):
157 def delete(cls, id_):
158 obj = cls.query().get(id_)
158 obj = cls.query().get(id_)
159 Session().delete(obj)
159 Session().delete(obj)
160
160
161 def __repr__(self):
161 def __repr__(self):
162 if hasattr(self, '__unicode__'):
162 if hasattr(self, '__unicode__'):
163 # python repr needs to return str
163 # python repr needs to return str
164 return safe_str(self.__unicode__())
164 return safe_str(self.__unicode__())
165 return '<DB:%s>' % (self.__class__.__name__)
165 return '<DB:%s>' % (self.__class__.__name__)
166
166
167
167
168 class RhodeCodeSetting(Base, BaseModel):
168 class RhodeCodeSetting(Base, BaseModel):
169 __tablename__ = 'rhodecode_settings'
169 __tablename__ = 'rhodecode_settings'
170 __table_args__ = (
170 __table_args__ = (
171 UniqueConstraint('app_settings_name'),
171 UniqueConstraint('app_settings_name'),
172 {'extend_existing': True, 'mysql_engine': 'InnoDB',
172 {'extend_existing': True, 'mysql_engine': 'InnoDB',
173 'mysql_charset': 'utf8'}
173 'mysql_charset': 'utf8'}
174 )
174 )
175 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
175 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
176 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
176 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
177 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
177 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
178
178
179 def __init__(self, k='', v=''):
179 def __init__(self, k='', v=''):
180 self.app_settings_name = k
180 self.app_settings_name = k
181 self.app_settings_value = v
181 self.app_settings_value = v
182
182
183 @validates('_app_settings_value')
183 @validates('_app_settings_value')
184 def validate_settings_value(self, key, val):
184 def validate_settings_value(self, key, val):
185 assert type(val) == unicode
185 assert type(val) == unicode
186 return val
186 return val
187
187
188 @hybrid_property
188 @hybrid_property
189 def app_settings_value(self):
189 def app_settings_value(self):
190 v = self._app_settings_value
190 v = self._app_settings_value
191 if self.app_settings_name == 'ldap_active':
191 if self.app_settings_name == 'ldap_active':
192 v = str2bool(v)
192 v = str2bool(v)
193 return v
193 return v
194
194
195 @app_settings_value.setter
195 @app_settings_value.setter
196 def app_settings_value(self, val):
196 def app_settings_value(self, val):
197 """
197 """
198 Setter that will always make sure we use unicode in app_settings_value
198 Setter that will always make sure we use unicode in app_settings_value
199
199
200 :param val:
200 :param val:
201 """
201 """
202 self._app_settings_value = safe_unicode(val)
202 self._app_settings_value = safe_unicode(val)
203
203
204 def __unicode__(self):
204 def __unicode__(self):
205 return u"<%s('%s:%s')>" % (
205 return u"<%s('%s:%s')>" % (
206 self.__class__.__name__,
206 self.__class__.__name__,
207 self.app_settings_name, self.app_settings_value
207 self.app_settings_name, self.app_settings_value
208 )
208 )
209
209
210 @classmethod
210 @classmethod
211 def get_by_name(cls, ldap_key):
211 def get_by_name(cls, ldap_key):
212 return cls.query()\
212 return cls.query()\
213 .filter(cls.app_settings_name == ldap_key).scalar()
213 .filter(cls.app_settings_name == ldap_key).scalar()
214
214
215 @classmethod
215 @classmethod
216 def get_app_settings(cls, cache=False):
216 def get_app_settings(cls, cache=False):
217
217
218 ret = cls.query()
218 ret = cls.query()
219
219
220 if cache:
220 if cache:
221 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
221 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
222
222
223 if not ret:
223 if not ret:
224 raise Exception('Could not get application settings !')
224 raise Exception('Could not get application settings !')
225 settings = {}
225 settings = {}
226 for each in ret:
226 for each in ret:
227 settings['rhodecode_' + each.app_settings_name] = \
227 settings['rhodecode_' + each.app_settings_name] = \
228 each.app_settings_value
228 each.app_settings_value
229
229
230 return settings
230 return settings
231
231
232 @classmethod
232 @classmethod
233 def get_ldap_settings(cls, cache=False):
233 def get_ldap_settings(cls, cache=False):
234 ret = cls.query()\
234 ret = cls.query()\
235 .filter(cls.app_settings_name.startswith('ldap_')).all()
235 .filter(cls.app_settings_name.startswith('ldap_')).all()
236 fd = {}
236 fd = {}
237 for row in ret:
237 for row in ret:
238 fd.update({row.app_settings_name: row.app_settings_value})
238 fd.update({row.app_settings_name: row.app_settings_value})
239
239
240 return fd
240 return fd
241
241
242
242
243 class RhodeCodeUi(Base, BaseModel):
243 class RhodeCodeUi(Base, BaseModel):
244 __tablename__ = 'rhodecode_ui'
244 __tablename__ = 'rhodecode_ui'
245 __table_args__ = (
245 __table_args__ = (
246 UniqueConstraint('ui_key'),
246 UniqueConstraint('ui_key'),
247 {'extend_existing': True, 'mysql_engine': 'InnoDB',
247 {'extend_existing': True, 'mysql_engine': 'InnoDB',
248 'mysql_charset': 'utf8'}
248 'mysql_charset': 'utf8'}
249 )
249 )
250
250
251 HOOK_UPDATE = 'changegroup.update'
251 HOOK_UPDATE = 'changegroup.update'
252 HOOK_REPO_SIZE = 'changegroup.repo_size'
252 HOOK_REPO_SIZE = 'changegroup.repo_size'
253 HOOK_PUSH = 'changegroup.push_logger'
253 HOOK_PUSH = 'changegroup.push_logger'
254 HOOK_PULL = 'preoutgoing.pull_logger'
254 HOOK_PULL = 'preoutgoing.pull_logger'
255
255
256 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
256 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
257 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
257 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
258 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
258 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
259 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
259 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
260 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
260 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
261
261
262 @classmethod
262 @classmethod
263 def get_by_key(cls, key):
263 def get_by_key(cls, key):
264 return cls.query().filter(cls.ui_key == key)
264 return cls.query().filter(cls.ui_key == key)
265
265
266 @classmethod
266 @classmethod
267 def get_builtin_hooks(cls):
267 def get_builtin_hooks(cls):
268 q = cls.query()
268 q = cls.query()
269 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
269 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
270 cls.HOOK_REPO_SIZE,
270 cls.HOOK_REPO_SIZE,
271 cls.HOOK_PUSH, cls.HOOK_PULL]))
271 cls.HOOK_PUSH, cls.HOOK_PULL]))
272 return q.all()
272 return q.all()
273
273
274 @classmethod
274 @classmethod
275 def get_custom_hooks(cls):
275 def get_custom_hooks(cls):
276 q = cls.query()
276 q = cls.query()
277 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
277 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
278 cls.HOOK_REPO_SIZE,
278 cls.HOOK_REPO_SIZE,
279 cls.HOOK_PUSH, cls.HOOK_PULL]))
279 cls.HOOK_PUSH, cls.HOOK_PULL]))
280 q = q.filter(cls.ui_section == 'hooks')
280 q = q.filter(cls.ui_section == 'hooks')
281 return q.all()
281 return q.all()
282
282
283 @classmethod
283 @classmethod
284 def get_repos_location(cls):
284 def get_repos_location(cls):
285 return cls.get_by_key('/').one().ui_value
285 return cls.get_by_key('/').one().ui_value
286
286
287 @classmethod
287 @classmethod
288 def create_or_update_hook(cls, key, val):
288 def create_or_update_hook(cls, key, val):
289 new_ui = cls.get_by_key(key).scalar() or cls()
289 new_ui = cls.get_by_key(key).scalar() or cls()
290 new_ui.ui_section = 'hooks'
290 new_ui.ui_section = 'hooks'
291 new_ui.ui_active = True
291 new_ui.ui_active = True
292 new_ui.ui_key = key
292 new_ui.ui_key = key
293 new_ui.ui_value = val
293 new_ui.ui_value = val
294
294
295 Session().add(new_ui)
295 Session().add(new_ui)
296
296
297
297
298 class User(Base, BaseModel):
298 class User(Base, BaseModel):
299 __tablename__ = 'users'
299 __tablename__ = 'users'
300 __table_args__ = (
300 __table_args__ = (
301 UniqueConstraint('username'), UniqueConstraint('email'),
301 UniqueConstraint('username'), UniqueConstraint('email'),
302 {'extend_existing': True, 'mysql_engine': 'InnoDB',
302 {'extend_existing': True, 'mysql_engine': 'InnoDB',
303 'mysql_charset': 'utf8'}
303 'mysql_charset': 'utf8'}
304 )
304 )
305 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
305 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
306 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
306 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
307 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
307 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
308 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
308 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
309 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
309 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
310 name = Column("firstname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
310 name = Column("firstname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
311 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
311 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
312 _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
312 _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
313 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
313 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
314 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
314 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
315 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
315 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
316
316
317 user_log = relationship('UserLog', cascade='all')
317 user_log = relationship('UserLog', cascade='all')
318 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
318 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
319
319
320 repositories = relationship('Repository')
320 repositories = relationship('Repository')
321 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
321 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
322 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
322 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
323 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
323 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
324
324
325 group_member = relationship('UsersGroupMember', cascade='all')
325 group_member = relationship('UsersGroupMember', cascade='all')
326
326
327 notifications = relationship('UserNotification', cascade='all')
327 notifications = relationship('UserNotification', cascade='all')
328 # notifications assigned to this user
328 # notifications assigned to this user
329 user_created_notifications = relationship('Notification', cascade='all')
329 user_created_notifications = relationship('Notification', cascade='all')
330 # comments created by this user
330 # comments created by this user
331 user_comments = relationship('ChangesetComment', cascade='all')
331 user_comments = relationship('ChangesetComment', cascade='all')
332
332
333 @hybrid_property
333 @hybrid_property
334 def email(self):
334 def email(self):
335 return self._email
335 return self._email
336
336
337 @email.setter
337 @email.setter
338 def email(self, val):
338 def email(self, val):
339 self._email = val.lower() if val else None
339 self._email = val.lower() if val else None
340
340
341 @property
341 @property
342 def emails(self):
342 def emails(self):
343 other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
343 other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
344 return [self.email] + [x.email for x in other]
344 return [self.email] + [x.email for x in other]
345
345
346 @property
346 @property
347 def full_name(self):
347 def full_name(self):
348 return '%s %s' % (self.name, self.lastname)
348 return '%s %s' % (self.name, self.lastname)
349
349
350 @property
350 @property
351 def full_name_or_username(self):
351 def full_name_or_username(self):
352 return ('%s %s' % (self.name, self.lastname)
352 return ('%s %s' % (self.name, self.lastname)
353 if (self.name and self.lastname) else self.username)
353 if (self.name and self.lastname) else self.username)
354
354
355 @property
355 @property
356 def full_contact(self):
356 def full_contact(self):
357 return '%s %s <%s>' % (self.name, self.lastname, self.email)
357 return '%s %s <%s>' % (self.name, self.lastname, self.email)
358
358
359 @property
359 @property
360 def short_contact(self):
360 def short_contact(self):
361 return '%s %s' % (self.name, self.lastname)
361 return '%s %s' % (self.name, self.lastname)
362
362
363 @property
363 @property
364 def is_admin(self):
364 def is_admin(self):
365 return self.admin
365 return self.admin
366
366
367 def __unicode__(self):
367 def __unicode__(self):
368 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
368 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
369 self.user_id, self.username)
369 self.user_id, self.username)
370
370
371 @classmethod
371 @classmethod
372 def get_by_username(cls, username, case_insensitive=False, cache=False):
372 def get_by_username(cls, username, case_insensitive=False, cache=False):
373 if case_insensitive:
373 if case_insensitive:
374 q = cls.query().filter(cls.username.ilike(username))
374 q = cls.query().filter(cls.username.ilike(username))
375 else:
375 else:
376 q = cls.query().filter(cls.username == username)
376 q = cls.query().filter(cls.username == username)
377
377
378 if cache:
378 if cache:
379 q = q.options(FromCache(
379 q = q.options(FromCache(
380 "sql_cache_short",
380 "sql_cache_short",
381 "get_user_%s" % _hash_key(username)
381 "get_user_%s" % _hash_key(username)
382 )
382 )
383 )
383 )
384 return q.scalar()
384 return q.scalar()
385
385
386 @classmethod
386 @classmethod
387 def get_by_api_key(cls, api_key, cache=False):
387 def get_by_api_key(cls, api_key, cache=False):
388 q = cls.query().filter(cls.api_key == api_key)
388 q = cls.query().filter(cls.api_key == api_key)
389
389
390 if cache:
390 if cache:
391 q = q.options(FromCache("sql_cache_short",
391 q = q.options(FromCache("sql_cache_short",
392 "get_api_key_%s" % api_key))
392 "get_api_key_%s" % api_key))
393 return q.scalar()
393 return q.scalar()
394
394
395 @classmethod
395 @classmethod
396 def get_by_email(cls, email, case_insensitive=False, cache=False):
396 def get_by_email(cls, email, case_insensitive=False, cache=False):
397 if case_insensitive:
397 if case_insensitive:
398 q = cls.query().filter(cls.email.ilike(email))
398 q = cls.query().filter(cls.email.ilike(email))
399 else:
399 else:
400 q = cls.query().filter(cls.email == email)
400 q = cls.query().filter(cls.email == email)
401
401
402 if cache:
402 if cache:
403 q = q.options(FromCache("sql_cache_short",
403 q = q.options(FromCache("sql_cache_short",
404 "get_email_key_%s" % email))
404 "get_email_key_%s" % email))
405
405
406 ret = q.scalar()
406 ret = q.scalar()
407 if ret is None:
407 if ret is None:
408 q = UserEmailMap.query()
408 q = UserEmailMap.query()
409 # try fetching in alternate email map
409 # try fetching in alternate email map
410 if case_insensitive:
410 if case_insensitive:
411 q = q.filter(UserEmailMap.email.ilike(email))
411 q = q.filter(UserEmailMap.email.ilike(email))
412 else:
412 else:
413 q = q.filter(UserEmailMap.email == email)
413 q = q.filter(UserEmailMap.email == email)
414 q = q.options(joinedload(UserEmailMap.user))
414 q = q.options(joinedload(UserEmailMap.user))
415 if cache:
415 if cache:
416 q = q.options(FromCache("sql_cache_short",
416 q = q.options(FromCache("sql_cache_short",
417 "get_email_map_key_%s" % email))
417 "get_email_map_key_%s" % email))
418 ret = getattr(q.scalar(), 'user', None)
418 ret = getattr(q.scalar(), 'user', None)
419
419
420 return ret
420 return ret
421
421
422 def update_lastlogin(self):
422 def update_lastlogin(self):
423 """Update user lastlogin"""
423 """Update user lastlogin"""
424 self.last_login = datetime.datetime.now()
424 self.last_login = datetime.datetime.now()
425 Session().add(self)
425 Session().add(self)
426 log.debug('updated user %s lastlogin' % self.username)
426 log.debug('updated user %s lastlogin' % self.username)
427
427
428 def get_api_data(self):
428 def get_api_data(self):
429 """
429 """
430 Common function for generating user related data for API
430 Common function for generating user related data for API
431 """
431 """
432 user = self
432 user = self
433 data = dict(
433 data = dict(
434 user_id=user.user_id,
434 user_id=user.user_id,
435 username=user.username,
435 username=user.username,
436 firstname=user.name,
436 firstname=user.name,
437 lastname=user.lastname,
437 lastname=user.lastname,
438 email=user.email,
438 email=user.email,
439 emails=user.emails,
439 emails=user.emails,
440 api_key=user.api_key,
440 api_key=user.api_key,
441 active=user.active,
441 active=user.active,
442 admin=user.admin,
442 admin=user.admin,
443 ldap_dn=user.ldap_dn,
443 ldap_dn=user.ldap_dn,
444 last_login=user.last_login,
444 last_login=user.last_login,
445 )
445 )
446 return data
446 return data
447
447
448 def __json__(self):
448 def __json__(self):
449 return dict(
449 data = dict(
450 user_id=self.user_id,
451 first_name=self.name,
452 last_name=self.lastname,
453 email=self.email,
454 full_name=self.full_name,
450 full_name=self.full_name,
455 full_name_or_username=self.full_name_or_username,
451 full_name_or_username=self.full_name_or_username,
456 short_contact=self.short_contact,
452 short_contact=self.short_contact,
457 full_contact=self.full_contact
453 full_contact=self.full_contact
458 )
454 )
455 data.update(self.get_api_data())
456 return data
459
457
460
458
461 class UserEmailMap(Base, BaseModel):
459 class UserEmailMap(Base, BaseModel):
462 __tablename__ = 'user_email_map'
460 __tablename__ = 'user_email_map'
463 __table_args__ = (
461 __table_args__ = (
464 Index('uem_email_idx', 'email'),
462 Index('uem_email_idx', 'email'),
465 UniqueConstraint('email'),
463 UniqueConstraint('email'),
466 {'extend_existing': True, 'mysql_engine': 'InnoDB',
464 {'extend_existing': True, 'mysql_engine': 'InnoDB',
467 'mysql_charset': 'utf8'}
465 'mysql_charset': 'utf8'}
468 )
466 )
469 __mapper_args__ = {}
467 __mapper_args__ = {}
470
468
471 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
469 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
472 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
470 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
473 _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
471 _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
474
472
475 user = relationship('User', lazy='joined')
473 user = relationship('User', lazy='joined')
476
474
477 @validates('_email')
475 @validates('_email')
478 def validate_email(self, key, email):
476 def validate_email(self, key, email):
479 # check if this email is not main one
477 # check if this email is not main one
480 main_email = Session().query(User).filter(User.email == email).scalar()
478 main_email = Session().query(User).filter(User.email == email).scalar()
481 if main_email is not None:
479 if main_email is not None:
482 raise AttributeError('email %s is present is user table' % email)
480 raise AttributeError('email %s is present is user table' % email)
483 return email
481 return email
484
482
485 @hybrid_property
483 @hybrid_property
486 def email(self):
484 def email(self):
487 return self._email
485 return self._email
488
486
489 @email.setter
487 @email.setter
490 def email(self, val):
488 def email(self, val):
491 self._email = val.lower() if val else None
489 self._email = val.lower() if val else None
492
490
493
491
494 class UserLog(Base, BaseModel):
492 class UserLog(Base, BaseModel):
495 __tablename__ = 'user_logs'
493 __tablename__ = 'user_logs'
496 __table_args__ = (
494 __table_args__ = (
497 {'extend_existing': True, 'mysql_engine': 'InnoDB',
495 {'extend_existing': True, 'mysql_engine': 'InnoDB',
498 'mysql_charset': 'utf8'},
496 'mysql_charset': 'utf8'},
499 )
497 )
500 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
498 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
501 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
499 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
502 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
500 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
503 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
501 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
504 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
502 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
505 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
503 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
506 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
504 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
507
505
508 @property
506 @property
509 def action_as_day(self):
507 def action_as_day(self):
510 return datetime.date(*self.action_date.timetuple()[:3])
508 return datetime.date(*self.action_date.timetuple()[:3])
511
509
512 user = relationship('User')
510 user = relationship('User')
513 repository = relationship('Repository', cascade='')
511 repository = relationship('Repository', cascade='')
514
512
515
513
516 class UsersGroup(Base, BaseModel):
514 class UsersGroup(Base, BaseModel):
517 __tablename__ = 'users_groups'
515 __tablename__ = 'users_groups'
518 __table_args__ = (
516 __table_args__ = (
519 {'extend_existing': True, 'mysql_engine': 'InnoDB',
517 {'extend_existing': True, 'mysql_engine': 'InnoDB',
520 'mysql_charset': 'utf8'},
518 'mysql_charset': 'utf8'},
521 )
519 )
522
520
523 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
521 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
524 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
522 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
525 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
523 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
526
524
527 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
525 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
528 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
526 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
529 users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
527 users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
530
528
531 def __unicode__(self):
529 def __unicode__(self):
532 return u'<userGroup(%s)>' % (self.users_group_name)
530 return u'<userGroup(%s)>' % (self.users_group_name)
533
531
534 @classmethod
532 @classmethod
535 def get_by_group_name(cls, group_name, cache=False,
533 def get_by_group_name(cls, group_name, cache=False,
536 case_insensitive=False):
534 case_insensitive=False):
537 if case_insensitive:
535 if case_insensitive:
538 q = cls.query().filter(cls.users_group_name.ilike(group_name))
536 q = cls.query().filter(cls.users_group_name.ilike(group_name))
539 else:
537 else:
540 q = cls.query().filter(cls.users_group_name == group_name)
538 q = cls.query().filter(cls.users_group_name == group_name)
541 if cache:
539 if cache:
542 q = q.options(FromCache(
540 q = q.options(FromCache(
543 "sql_cache_short",
541 "sql_cache_short",
544 "get_user_%s" % _hash_key(group_name)
542 "get_user_%s" % _hash_key(group_name)
545 )
543 )
546 )
544 )
547 return q.scalar()
545 return q.scalar()
548
546
549 @classmethod
547 @classmethod
550 def get(cls, users_group_id, cache=False):
548 def get(cls, users_group_id, cache=False):
551 users_group = cls.query()
549 users_group = cls.query()
552 if cache:
550 if cache:
553 users_group = users_group.options(FromCache("sql_cache_short",
551 users_group = users_group.options(FromCache("sql_cache_short",
554 "get_users_group_%s" % users_group_id))
552 "get_users_group_%s" % users_group_id))
555 return users_group.get(users_group_id)
553 return users_group.get(users_group_id)
556
554
557 def get_api_data(self):
555 def get_api_data(self):
558 users_group = self
556 users_group = self
559
557
560 data = dict(
558 data = dict(
561 users_group_id=users_group.users_group_id,
559 users_group_id=users_group.users_group_id,
562 group_name=users_group.users_group_name,
560 group_name=users_group.users_group_name,
563 active=users_group.users_group_active,
561 active=users_group.users_group_active,
564 )
562 )
565
563
566 return data
564 return data
567
565
568
566
569 class UsersGroupMember(Base, BaseModel):
567 class UsersGroupMember(Base, BaseModel):
570 __tablename__ = 'users_groups_members'
568 __tablename__ = 'users_groups_members'
571 __table_args__ = (
569 __table_args__ = (
572 {'extend_existing': True, 'mysql_engine': 'InnoDB',
570 {'extend_existing': True, 'mysql_engine': 'InnoDB',
573 'mysql_charset': 'utf8'},
571 'mysql_charset': 'utf8'},
574 )
572 )
575
573
576 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
574 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
577 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
575 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
578 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
576 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
579
577
580 user = relationship('User', lazy='joined')
578 user = relationship('User', lazy='joined')
581 users_group = relationship('UsersGroup')
579 users_group = relationship('UsersGroup')
582
580
583 def __init__(self, gr_id='', u_id=''):
581 def __init__(self, gr_id='', u_id=''):
584 self.users_group_id = gr_id
582 self.users_group_id = gr_id
585 self.user_id = u_id
583 self.user_id = u_id
586
584
587
585
588 class Repository(Base, BaseModel):
586 class Repository(Base, BaseModel):
589 __tablename__ = 'repositories'
587 __tablename__ = 'repositories'
590 __table_args__ = (
588 __table_args__ = (
591 UniqueConstraint('repo_name'),
589 UniqueConstraint('repo_name'),
592 {'extend_existing': True, 'mysql_engine': 'InnoDB',
590 {'extend_existing': True, 'mysql_engine': 'InnoDB',
593 'mysql_charset': 'utf8'},
591 'mysql_charset': 'utf8'},
594 )
592 )
595
593
596 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
594 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
597 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
595 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
598 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
596 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
599 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
597 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
600 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
598 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
601 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
599 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
602 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
600 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
603 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
601 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
604 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
602 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
605 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
603 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
606 landing_rev = Column("landing_revision", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
604 landing_rev = Column("landing_revision", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
607
605
608 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
606 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
609 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
607 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
610
608
611 user = relationship('User')
609 user = relationship('User')
612 fork = relationship('Repository', remote_side=repo_id)
610 fork = relationship('Repository', remote_side=repo_id)
613 group = relationship('RepoGroup')
611 group = relationship('RepoGroup')
614 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
612 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
615 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
613 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
616 stats = relationship('Statistics', cascade='all', uselist=False)
614 stats = relationship('Statistics', cascade='all', uselist=False)
617
615
618 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
616 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
619
617
620 logs = relationship('UserLog')
618 logs = relationship('UserLog')
621 comments = relationship('ChangesetComment')
619 comments = relationship('ChangesetComment')
622
620
623 def __unicode__(self):
621 def __unicode__(self):
624 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
622 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
625 self.repo_name)
623 self.repo_name)
626
624
627 @classmethod
625 @classmethod
628 def url_sep(cls):
626 def url_sep(cls):
629 return URL_SEP
627 return URL_SEP
630
628
631 @classmethod
629 @classmethod
632 def get_by_repo_name(cls, repo_name):
630 def get_by_repo_name(cls, repo_name):
633 q = Session().query(cls).filter(cls.repo_name == repo_name)
631 q = Session().query(cls).filter(cls.repo_name == repo_name)
634 q = q.options(joinedload(Repository.fork))\
632 q = q.options(joinedload(Repository.fork))\
635 .options(joinedload(Repository.user))\
633 .options(joinedload(Repository.user))\
636 .options(joinedload(Repository.group))
634 .options(joinedload(Repository.group))
637 return q.scalar()
635 return q.scalar()
638
636
639 @classmethod
637 @classmethod
640 def get_by_full_path(cls, repo_full_path):
638 def get_by_full_path(cls, repo_full_path):
641 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
639 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
642 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
640 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
643
641
644 @classmethod
642 @classmethod
645 def get_repo_forks(cls, repo_id):
643 def get_repo_forks(cls, repo_id):
646 return cls.query().filter(Repository.fork_id == repo_id)
644 return cls.query().filter(Repository.fork_id == repo_id)
647
645
648 @classmethod
646 @classmethod
649 def base_path(cls):
647 def base_path(cls):
650 """
648 """
651 Returns base path when all repos are stored
649 Returns base path when all repos are stored
652
650
653 :param cls:
651 :param cls:
654 """
652 """
655 q = Session().query(RhodeCodeUi)\
653 q = Session().query(RhodeCodeUi)\
656 .filter(RhodeCodeUi.ui_key == cls.url_sep())
654 .filter(RhodeCodeUi.ui_key == cls.url_sep())
657 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
655 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
658 return q.one().ui_value
656 return q.one().ui_value
659
657
660 @property
658 @property
661 def forks(self):
659 def forks(self):
662 """
660 """
663 Return forks of this repo
661 Return forks of this repo
664 """
662 """
665 return Repository.get_repo_forks(self.repo_id)
663 return Repository.get_repo_forks(self.repo_id)
666
664
667 @property
665 @property
668 def parent(self):
666 def parent(self):
669 """
667 """
670 Returns fork parent
668 Returns fork parent
671 """
669 """
672 return self.fork
670 return self.fork
673
671
674 @property
672 @property
675 def just_name(self):
673 def just_name(self):
676 return self.repo_name.split(Repository.url_sep())[-1]
674 return self.repo_name.split(Repository.url_sep())[-1]
677
675
678 @property
676 @property
679 def groups_with_parents(self):
677 def groups_with_parents(self):
680 groups = []
678 groups = []
681 if self.group is None:
679 if self.group is None:
682 return groups
680 return groups
683
681
684 cur_gr = self.group
682 cur_gr = self.group
685 groups.insert(0, cur_gr)
683 groups.insert(0, cur_gr)
686 while 1:
684 while 1:
687 gr = getattr(cur_gr, 'parent_group', None)
685 gr = getattr(cur_gr, 'parent_group', None)
688 cur_gr = cur_gr.parent_group
686 cur_gr = cur_gr.parent_group
689 if gr is None:
687 if gr is None:
690 break
688 break
691 groups.insert(0, gr)
689 groups.insert(0, gr)
692
690
693 return groups
691 return groups
694
692
695 @property
693 @property
696 def groups_and_repo(self):
694 def groups_and_repo(self):
697 return self.groups_with_parents, self.just_name
695 return self.groups_with_parents, self.just_name
698
696
699 @LazyProperty
697 @LazyProperty
700 def repo_path(self):
698 def repo_path(self):
701 """
699 """
702 Returns base full path for that repository means where it actually
700 Returns base full path for that repository means where it actually
703 exists on a filesystem
701 exists on a filesystem
704 """
702 """
705 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
703 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
706 Repository.url_sep())
704 Repository.url_sep())
707 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
705 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
708 return q.one().ui_value
706 return q.one().ui_value
709
707
710 @property
708 @property
711 def repo_full_path(self):
709 def repo_full_path(self):
712 p = [self.repo_path]
710 p = [self.repo_path]
713 # we need to split the name by / since this is how we store the
711 # we need to split the name by / since this is how we store the
714 # names in the database, but that eventually needs to be converted
712 # names in the database, but that eventually needs to be converted
715 # into a valid system path
713 # into a valid system path
716 p += self.repo_name.split(Repository.url_sep())
714 p += self.repo_name.split(Repository.url_sep())
717 return os.path.join(*p)
715 return os.path.join(*p)
718
716
719 def get_new_name(self, repo_name):
717 def get_new_name(self, repo_name):
720 """
718 """
721 returns new full repository name based on assigned group and new new
719 returns new full repository name based on assigned group and new new
722
720
723 :param group_name:
721 :param group_name:
724 """
722 """
725 path_prefix = self.group.full_path_splitted if self.group else []
723 path_prefix = self.group.full_path_splitted if self.group else []
726 return Repository.url_sep().join(path_prefix + [repo_name])
724 return Repository.url_sep().join(path_prefix + [repo_name])
727
725
728 @property
726 @property
729 def _ui(self):
727 def _ui(self):
730 """
728 """
731 Creates an db based ui object for this repository
729 Creates an db based ui object for this repository
732 """
730 """
733 from mercurial import ui
731 from mercurial import ui
734 from mercurial import config
732 from mercurial import config
735 baseui = ui.ui()
733 baseui = ui.ui()
736
734
737 #clean the baseui object
735 #clean the baseui object
738 baseui._ocfg = config.config()
736 baseui._ocfg = config.config()
739 baseui._ucfg = config.config()
737 baseui._ucfg = config.config()
740 baseui._tcfg = config.config()
738 baseui._tcfg = config.config()
741
739
742 ret = RhodeCodeUi.query()\
740 ret = RhodeCodeUi.query()\
743 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
741 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
744
742
745 hg_ui = ret
743 hg_ui = ret
746 for ui_ in hg_ui:
744 for ui_ in hg_ui:
747 if ui_.ui_active:
745 if ui_.ui_active:
748 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
746 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
749 ui_.ui_key, ui_.ui_value)
747 ui_.ui_key, ui_.ui_value)
750 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
748 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
751
749
752 return baseui
750 return baseui
753
751
754 @classmethod
752 @classmethod
755 def inject_ui(cls, repo, extras={}):
753 def inject_ui(cls, repo, extras={}):
756 from rhodecode.lib.vcs.backends.hg import MercurialRepository
754 from rhodecode.lib.vcs.backends.hg import MercurialRepository
757 from rhodecode.lib.vcs.backends.git import GitRepository
755 from rhodecode.lib.vcs.backends.git import GitRepository
758 required = (MercurialRepository, GitRepository)
756 required = (MercurialRepository, GitRepository)
759 if not isinstance(repo, required):
757 if not isinstance(repo, required):
760 raise Exception('repo must be instance of %s' % required)
758 raise Exception('repo must be instance of %s' % required)
761
759
762 # inject ui extra param to log this action via push logger
760 # inject ui extra param to log this action via push logger
763 for k, v in extras.items():
761 for k, v in extras.items():
764 repo._repo.ui.setconfig('rhodecode_extras', k, v)
762 repo._repo.ui.setconfig('rhodecode_extras', k, v)
765
763
766 @classmethod
764 @classmethod
767 def is_valid(cls, repo_name):
765 def is_valid(cls, repo_name):
768 """
766 """
769 returns True if given repo name is a valid filesystem repository
767 returns True if given repo name is a valid filesystem repository
770
768
771 :param cls:
769 :param cls:
772 :param repo_name:
770 :param repo_name:
773 """
771 """
774 from rhodecode.lib.utils import is_valid_repo
772 from rhodecode.lib.utils import is_valid_repo
775
773
776 return is_valid_repo(repo_name, cls.base_path())
774 return is_valid_repo(repo_name, cls.base_path())
777
775
778 def get_api_data(self):
776 def get_api_data(self):
779 """
777 """
780 Common function for generating repo api data
778 Common function for generating repo api data
781
779
782 """
780 """
783 repo = self
781 repo = self
784 data = dict(
782 data = dict(
785 repo_id=repo.repo_id,
783 repo_id=repo.repo_id,
786 repo_name=repo.repo_name,
784 repo_name=repo.repo_name,
787 repo_type=repo.repo_type,
785 repo_type=repo.repo_type,
788 clone_uri=repo.clone_uri,
786 clone_uri=repo.clone_uri,
789 private=repo.private,
787 private=repo.private,
790 created_on=repo.created_on,
788 created_on=repo.created_on,
791 description=repo.description,
789 description=repo.description,
792 landing_rev=repo.landing_rev,
790 landing_rev=repo.landing_rev,
793 owner=repo.user.username,
791 owner=repo.user.username,
794 fork_of=repo.fork.repo_name if repo.fork else None
792 fork_of=repo.fork.repo_name if repo.fork else None
795 )
793 )
796
794
797 return data
795 return data
798
796
799 #==========================================================================
797 #==========================================================================
800 # SCM PROPERTIES
798 # SCM PROPERTIES
801 #==========================================================================
799 #==========================================================================
802
800
803 def get_changeset(self, rev=None):
801 def get_changeset(self, rev=None):
804 return get_changeset_safe(self.scm_instance, rev)
802 return get_changeset_safe(self.scm_instance, rev)
805
803
806 @property
804 @property
807 def tip(self):
805 def tip(self):
808 return self.get_changeset('tip')
806 return self.get_changeset('tip')
809
807
810 @property
808 @property
811 def author(self):
809 def author(self):
812 return self.tip.author
810 return self.tip.author
813
811
814 @property
812 @property
815 def last_change(self):
813 def last_change(self):
816 return self.scm_instance.last_change
814 return self.scm_instance.last_change
817
815
818 def get_comments(self, revisions=None):
816 def get_comments(self, revisions=None):
819 """
817 """
820 Returns comments for this repository grouped by revisions
818 Returns comments for this repository grouped by revisions
821
819
822 :param revisions: filter query by revisions only
820 :param revisions: filter query by revisions only
823 """
821 """
824 cmts = ChangesetComment.query()\
822 cmts = ChangesetComment.query()\
825 .filter(ChangesetComment.repo == self)
823 .filter(ChangesetComment.repo == self)
826 if revisions:
824 if revisions:
827 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
825 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
828 grouped = defaultdict(list)
826 grouped = defaultdict(list)
829 for cmt in cmts.all():
827 for cmt in cmts.all():
830 grouped[cmt.revision].append(cmt)
828 grouped[cmt.revision].append(cmt)
831 return grouped
829 return grouped
832
830
833 def statuses(self, revisions=None):
831 def statuses(self, revisions=None):
834 """
832 """
835 Returns statuses for this repository
833 Returns statuses for this repository
836
834
837 :param revisions: list of revisions to get statuses for
835 :param revisions: list of revisions to get statuses for
838 :type revisions: list
836 :type revisions: list
839 """
837 """
840
838
841 statuses = ChangesetStatus.query()\
839 statuses = ChangesetStatus.query()\
842 .filter(ChangesetStatus.repo == self)\
840 .filter(ChangesetStatus.repo == self)\
843 .filter(ChangesetStatus.version == 0)
841 .filter(ChangesetStatus.version == 0)
844 if revisions:
842 if revisions:
845 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
843 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
846 grouped = {}
844 grouped = {}
847
845
848 #maybe we have open new pullrequest without a status ?
846 #maybe we have open new pullrequest without a status ?
849 stat = ChangesetStatus.STATUS_UNDER_REVIEW
847 stat = ChangesetStatus.STATUS_UNDER_REVIEW
850 status_lbl = ChangesetStatus.get_status_lbl(stat)
848 status_lbl = ChangesetStatus.get_status_lbl(stat)
851 for pr in PullRequest.query().filter(PullRequest.org_repo == self).all():
849 for pr in PullRequest.query().filter(PullRequest.org_repo == self).all():
852 for rev in pr.revisions:
850 for rev in pr.revisions:
853 pr_id = pr.pull_request_id
851 pr_id = pr.pull_request_id
854 pr_repo = pr.other_repo.repo_name
852 pr_repo = pr.other_repo.repo_name
855 grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
853 grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
856
854
857 for stat in statuses.all():
855 for stat in statuses.all():
858 pr_id = pr_repo = None
856 pr_id = pr_repo = None
859 if stat.pull_request:
857 if stat.pull_request:
860 pr_id = stat.pull_request.pull_request_id
858 pr_id = stat.pull_request.pull_request_id
861 pr_repo = stat.pull_request.other_repo.repo_name
859 pr_repo = stat.pull_request.other_repo.repo_name
862 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
860 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
863 pr_id, pr_repo]
861 pr_id, pr_repo]
864 return grouped
862 return grouped
865
863
866 #==========================================================================
864 #==========================================================================
867 # SCM CACHE INSTANCE
865 # SCM CACHE INSTANCE
868 #==========================================================================
866 #==========================================================================
869
867
870 @property
868 @property
871 def invalidate(self):
869 def invalidate(self):
872 return CacheInvalidation.invalidate(self.repo_name)
870 return CacheInvalidation.invalidate(self.repo_name)
873
871
874 def set_invalidate(self):
872 def set_invalidate(self):
875 """
873 """
876 set a cache for invalidation for this instance
874 set a cache for invalidation for this instance
877 """
875 """
878 CacheInvalidation.set_invalidate(self.repo_name)
876 CacheInvalidation.set_invalidate(self.repo_name)
879
877
880 @LazyProperty
878 @LazyProperty
881 def scm_instance(self):
879 def scm_instance(self):
882 return self.__get_instance()
880 return self.__get_instance()
883
881
884 def scm_instance_cached(self, cache_map=None):
882 def scm_instance_cached(self, cache_map=None):
885 @cache_region('long_term')
883 @cache_region('long_term')
886 def _c(repo_name):
884 def _c(repo_name):
887 return self.__get_instance()
885 return self.__get_instance()
888 rn = self.repo_name
886 rn = self.repo_name
889 log.debug('Getting cached instance of repo')
887 log.debug('Getting cached instance of repo')
890
888
891 if cache_map:
889 if cache_map:
892 # get using prefilled cache_map
890 # get using prefilled cache_map
893 invalidate_repo = cache_map[self.repo_name]
891 invalidate_repo = cache_map[self.repo_name]
894 if invalidate_repo:
892 if invalidate_repo:
895 invalidate_repo = (None if invalidate_repo.cache_active
893 invalidate_repo = (None if invalidate_repo.cache_active
896 else invalidate_repo)
894 else invalidate_repo)
897 else:
895 else:
898 # get from invalidate
896 # get from invalidate
899 invalidate_repo = self.invalidate
897 invalidate_repo = self.invalidate
900
898
901 if invalidate_repo is not None:
899 if invalidate_repo is not None:
902 region_invalidate(_c, None, rn)
900 region_invalidate(_c, None, rn)
903 # update our cache
901 # update our cache
904 CacheInvalidation.set_valid(invalidate_repo.cache_key)
902 CacheInvalidation.set_valid(invalidate_repo.cache_key)
905 return _c(rn)
903 return _c(rn)
906
904
907 def __get_instance(self):
905 def __get_instance(self):
908 repo_full_path = self.repo_full_path
906 repo_full_path = self.repo_full_path
909 try:
907 try:
910 alias = get_scm(repo_full_path)[0]
908 alias = get_scm(repo_full_path)[0]
911 log.debug('Creating instance of %s repository' % alias)
909 log.debug('Creating instance of %s repository' % alias)
912 backend = get_backend(alias)
910 backend = get_backend(alias)
913 except VCSError:
911 except VCSError:
914 log.error(traceback.format_exc())
912 log.error(traceback.format_exc())
915 log.error('Perhaps this repository is in db and not in '
913 log.error('Perhaps this repository is in db and not in '
916 'filesystem run rescan repositories with '
914 'filesystem run rescan repositories with '
917 '"destroy old data " option from admin panel')
915 '"destroy old data " option from admin panel')
918 return
916 return
919
917
920 if alias == 'hg':
918 if alias == 'hg':
921
919
922 repo = backend(safe_str(repo_full_path), create=False,
920 repo = backend(safe_str(repo_full_path), create=False,
923 baseui=self._ui)
921 baseui=self._ui)
924 # skip hidden web repository
922 # skip hidden web repository
925 if repo._get_hidden():
923 if repo._get_hidden():
926 return
924 return
927 else:
925 else:
928 repo = backend(repo_full_path, create=False)
926 repo = backend(repo_full_path, create=False)
929
927
930 return repo
928 return repo
931
929
932
930
933 class RepoGroup(Base, BaseModel):
931 class RepoGroup(Base, BaseModel):
934 __tablename__ = 'groups'
932 __tablename__ = 'groups'
935 __table_args__ = (
933 __table_args__ = (
936 UniqueConstraint('group_name', 'group_parent_id'),
934 UniqueConstraint('group_name', 'group_parent_id'),
937 CheckConstraint('group_id != group_parent_id'),
935 CheckConstraint('group_id != group_parent_id'),
938 {'extend_existing': True, 'mysql_engine': 'InnoDB',
936 {'extend_existing': True, 'mysql_engine': 'InnoDB',
939 'mysql_charset': 'utf8'},
937 'mysql_charset': 'utf8'},
940 )
938 )
941 __mapper_args__ = {'order_by': 'group_name'}
939 __mapper_args__ = {'order_by': 'group_name'}
942
940
943 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
941 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
944 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
942 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
945 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
943 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
946 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
944 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
947
945
948 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
946 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
949 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
947 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
950
948
951 parent_group = relationship('RepoGroup', remote_side=group_id)
949 parent_group = relationship('RepoGroup', remote_side=group_id)
952
950
953 def __init__(self, group_name='', parent_group=None):
951 def __init__(self, group_name='', parent_group=None):
954 self.group_name = group_name
952 self.group_name = group_name
955 self.parent_group = parent_group
953 self.parent_group = parent_group
956
954
957 def __unicode__(self):
955 def __unicode__(self):
958 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
956 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
959 self.group_name)
957 self.group_name)
960
958
961 @classmethod
959 @classmethod
962 def groups_choices(cls):
960 def groups_choices(cls):
963 from webhelpers.html import literal as _literal
961 from webhelpers.html import literal as _literal
964 repo_groups = [('', '')]
962 repo_groups = [('', '')]
965 sep = ' &raquo; '
963 sep = ' &raquo; '
966 _name = lambda k: _literal(sep.join(k))
964 _name = lambda k: _literal(sep.join(k))
967
965
968 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
966 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
969 for x in cls.query().all()])
967 for x in cls.query().all()])
970
968
971 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
969 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
972 return repo_groups
970 return repo_groups
973
971
974 @classmethod
972 @classmethod
975 def url_sep(cls):
973 def url_sep(cls):
976 return URL_SEP
974 return URL_SEP
977
975
978 @classmethod
976 @classmethod
979 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
977 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
980 if case_insensitive:
978 if case_insensitive:
981 gr = cls.query()\
979 gr = cls.query()\
982 .filter(cls.group_name.ilike(group_name))
980 .filter(cls.group_name.ilike(group_name))
983 else:
981 else:
984 gr = cls.query()\
982 gr = cls.query()\
985 .filter(cls.group_name == group_name)
983 .filter(cls.group_name == group_name)
986 if cache:
984 if cache:
987 gr = gr.options(FromCache(
985 gr = gr.options(FromCache(
988 "sql_cache_short",
986 "sql_cache_short",
989 "get_group_%s" % _hash_key(group_name)
987 "get_group_%s" % _hash_key(group_name)
990 )
988 )
991 )
989 )
992 return gr.scalar()
990 return gr.scalar()
993
991
994 @property
992 @property
995 def parents(self):
993 def parents(self):
996 parents_recursion_limit = 5
994 parents_recursion_limit = 5
997 groups = []
995 groups = []
998 if self.parent_group is None:
996 if self.parent_group is None:
999 return groups
997 return groups
1000 cur_gr = self.parent_group
998 cur_gr = self.parent_group
1001 groups.insert(0, cur_gr)
999 groups.insert(0, cur_gr)
1002 cnt = 0
1000 cnt = 0
1003 while 1:
1001 while 1:
1004 cnt += 1
1002 cnt += 1
1005 gr = getattr(cur_gr, 'parent_group', None)
1003 gr = getattr(cur_gr, 'parent_group', None)
1006 cur_gr = cur_gr.parent_group
1004 cur_gr = cur_gr.parent_group
1007 if gr is None:
1005 if gr is None:
1008 break
1006 break
1009 if cnt == parents_recursion_limit:
1007 if cnt == parents_recursion_limit:
1010 # this will prevent accidental infinit loops
1008 # this will prevent accidental infinit loops
1011 log.error('group nested more than %s' %
1009 log.error('group nested more than %s' %
1012 parents_recursion_limit)
1010 parents_recursion_limit)
1013 break
1011 break
1014
1012
1015 groups.insert(0, gr)
1013 groups.insert(0, gr)
1016 return groups
1014 return groups
1017
1015
1018 @property
1016 @property
1019 def children(self):
1017 def children(self):
1020 return RepoGroup.query().filter(RepoGroup.parent_group == self)
1018 return RepoGroup.query().filter(RepoGroup.parent_group == self)
1021
1019
1022 @property
1020 @property
1023 def name(self):
1021 def name(self):
1024 return self.group_name.split(RepoGroup.url_sep())[-1]
1022 return self.group_name.split(RepoGroup.url_sep())[-1]
1025
1023
1026 @property
1024 @property
1027 def full_path(self):
1025 def full_path(self):
1028 return self.group_name
1026 return self.group_name
1029
1027
1030 @property
1028 @property
1031 def full_path_splitted(self):
1029 def full_path_splitted(self):
1032 return self.group_name.split(RepoGroup.url_sep())
1030 return self.group_name.split(RepoGroup.url_sep())
1033
1031
1034 @property
1032 @property
1035 def repositories(self):
1033 def repositories(self):
1036 return Repository.query()\
1034 return Repository.query()\
1037 .filter(Repository.group == self)\
1035 .filter(Repository.group == self)\
1038 .order_by(Repository.repo_name)
1036 .order_by(Repository.repo_name)
1039
1037
1040 @property
1038 @property
1041 def repositories_recursive_count(self):
1039 def repositories_recursive_count(self):
1042 cnt = self.repositories.count()
1040 cnt = self.repositories.count()
1043
1041
1044 def children_count(group):
1042 def children_count(group):
1045 cnt = 0
1043 cnt = 0
1046 for child in group.children:
1044 for child in group.children:
1047 cnt += child.repositories.count()
1045 cnt += child.repositories.count()
1048 cnt += children_count(child)
1046 cnt += children_count(child)
1049 return cnt
1047 return cnt
1050
1048
1051 return cnt + children_count(self)
1049 return cnt + children_count(self)
1052
1050
1053 def get_new_name(self, group_name):
1051 def get_new_name(self, group_name):
1054 """
1052 """
1055 returns new full group name based on parent and new name
1053 returns new full group name based on parent and new name
1056
1054
1057 :param group_name:
1055 :param group_name:
1058 """
1056 """
1059 path_prefix = (self.parent_group.full_path_splitted if
1057 path_prefix = (self.parent_group.full_path_splitted if
1060 self.parent_group else [])
1058 self.parent_group else [])
1061 return RepoGroup.url_sep().join(path_prefix + [group_name])
1059 return RepoGroup.url_sep().join(path_prefix + [group_name])
1062
1060
1063
1061
1064 class Permission(Base, BaseModel):
1062 class Permission(Base, BaseModel):
1065 __tablename__ = 'permissions'
1063 __tablename__ = 'permissions'
1066 __table_args__ = (
1064 __table_args__ = (
1067 Index('p_perm_name_idx', 'permission_name'),
1065 Index('p_perm_name_idx', 'permission_name'),
1068 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1066 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1069 'mysql_charset': 'utf8'},
1067 'mysql_charset': 'utf8'},
1070 )
1068 )
1071 PERMS = [
1069 PERMS = [
1072 ('repository.none', _('Repository no access')),
1070 ('repository.none', _('Repository no access')),
1073 ('repository.read', _('Repository read access')),
1071 ('repository.read', _('Repository read access')),
1074 ('repository.write', _('Repository write access')),
1072 ('repository.write', _('Repository write access')),
1075 ('repository.admin', _('Repository admin access')),
1073 ('repository.admin', _('Repository admin access')),
1076
1074
1077 ('group.none', _('Repositories Group no access')),
1075 ('group.none', _('Repositories Group no access')),
1078 ('group.read', _('Repositories Group read access')),
1076 ('group.read', _('Repositories Group read access')),
1079 ('group.write', _('Repositories Group write access')),
1077 ('group.write', _('Repositories Group write access')),
1080 ('group.admin', _('Repositories Group admin access')),
1078 ('group.admin', _('Repositories Group admin access')),
1081
1079
1082 ('hg.admin', _('RhodeCode Administrator')),
1080 ('hg.admin', _('RhodeCode Administrator')),
1083 ('hg.create.none', _('Repository creation disabled')),
1081 ('hg.create.none', _('Repository creation disabled')),
1084 ('hg.create.repository', _('Repository creation enabled')),
1082 ('hg.create.repository', _('Repository creation enabled')),
1085 ('hg.register.none', _('Register disabled')),
1083 ('hg.register.none', _('Register disabled')),
1086 ('hg.register.manual_activate', _('Register new user with RhodeCode '
1084 ('hg.register.manual_activate', _('Register new user with RhodeCode '
1087 'with manual activation')),
1085 'with manual activation')),
1088
1086
1089 ('hg.register.auto_activate', _('Register new user with RhodeCode '
1087 ('hg.register.auto_activate', _('Register new user with RhodeCode '
1090 'with auto activation')),
1088 'with auto activation')),
1091 ]
1089 ]
1092
1090
1093 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1091 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1094 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1092 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1095 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1093 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1096
1094
1097 def __unicode__(self):
1095 def __unicode__(self):
1098 return u"<%s('%s:%s')>" % (
1096 return u"<%s('%s:%s')>" % (
1099 self.__class__.__name__, self.permission_id, self.permission_name
1097 self.__class__.__name__, self.permission_id, self.permission_name
1100 )
1098 )
1101
1099
1102 @classmethod
1100 @classmethod
1103 def get_by_key(cls, key):
1101 def get_by_key(cls, key):
1104 return cls.query().filter(cls.permission_name == key).scalar()
1102 return cls.query().filter(cls.permission_name == key).scalar()
1105
1103
1106 @classmethod
1104 @classmethod
1107 def get_default_perms(cls, default_user_id):
1105 def get_default_perms(cls, default_user_id):
1108 q = Session().query(UserRepoToPerm, Repository, cls)\
1106 q = Session().query(UserRepoToPerm, Repository, cls)\
1109 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
1107 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
1110 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
1108 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
1111 .filter(UserRepoToPerm.user_id == default_user_id)
1109 .filter(UserRepoToPerm.user_id == default_user_id)
1112
1110
1113 return q.all()
1111 return q.all()
1114
1112
1115 @classmethod
1113 @classmethod
1116 def get_default_group_perms(cls, default_user_id):
1114 def get_default_group_perms(cls, default_user_id):
1117 q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
1115 q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
1118 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
1116 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
1119 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
1117 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
1120 .filter(UserRepoGroupToPerm.user_id == default_user_id)
1118 .filter(UserRepoGroupToPerm.user_id == default_user_id)
1121
1119
1122 return q.all()
1120 return q.all()
1123
1121
1124
1122
1125 class UserRepoToPerm(Base, BaseModel):
1123 class UserRepoToPerm(Base, BaseModel):
1126 __tablename__ = 'repo_to_perm'
1124 __tablename__ = 'repo_to_perm'
1127 __table_args__ = (
1125 __table_args__ = (
1128 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
1126 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
1129 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1127 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1130 'mysql_charset': 'utf8'}
1128 'mysql_charset': 'utf8'}
1131 )
1129 )
1132 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1130 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1133 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1131 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1134 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1132 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1135 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1133 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1136
1134
1137 user = relationship('User')
1135 user = relationship('User')
1138 repository = relationship('Repository')
1136 repository = relationship('Repository')
1139 permission = relationship('Permission')
1137 permission = relationship('Permission')
1140
1138
1141 @classmethod
1139 @classmethod
1142 def create(cls, user, repository, permission):
1140 def create(cls, user, repository, permission):
1143 n = cls()
1141 n = cls()
1144 n.user = user
1142 n.user = user
1145 n.repository = repository
1143 n.repository = repository
1146 n.permission = permission
1144 n.permission = permission
1147 Session().add(n)
1145 Session().add(n)
1148 return n
1146 return n
1149
1147
1150 def __unicode__(self):
1148 def __unicode__(self):
1151 return u'<user:%s => %s >' % (self.user, self.repository)
1149 return u'<user:%s => %s >' % (self.user, self.repository)
1152
1150
1153
1151
1154 class UserToPerm(Base, BaseModel):
1152 class UserToPerm(Base, BaseModel):
1155 __tablename__ = 'user_to_perm'
1153 __tablename__ = 'user_to_perm'
1156 __table_args__ = (
1154 __table_args__ = (
1157 UniqueConstraint('user_id', 'permission_id'),
1155 UniqueConstraint('user_id', 'permission_id'),
1158 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1156 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1159 'mysql_charset': 'utf8'}
1157 'mysql_charset': 'utf8'}
1160 )
1158 )
1161 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1159 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1162 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1160 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1163 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1161 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1164
1162
1165 user = relationship('User')
1163 user = relationship('User')
1166 permission = relationship('Permission', lazy='joined')
1164 permission = relationship('Permission', lazy='joined')
1167
1165
1168
1166
1169 class UsersGroupRepoToPerm(Base, BaseModel):
1167 class UsersGroupRepoToPerm(Base, BaseModel):
1170 __tablename__ = 'users_group_repo_to_perm'
1168 __tablename__ = 'users_group_repo_to_perm'
1171 __table_args__ = (
1169 __table_args__ = (
1172 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1170 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1173 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1171 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1174 'mysql_charset': 'utf8'}
1172 'mysql_charset': 'utf8'}
1175 )
1173 )
1176 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1174 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1177 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1175 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1178 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1176 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1179 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1177 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1180
1178
1181 users_group = relationship('UsersGroup')
1179 users_group = relationship('UsersGroup')
1182 permission = relationship('Permission')
1180 permission = relationship('Permission')
1183 repository = relationship('Repository')
1181 repository = relationship('Repository')
1184
1182
1185 @classmethod
1183 @classmethod
1186 def create(cls, users_group, repository, permission):
1184 def create(cls, users_group, repository, permission):
1187 n = cls()
1185 n = cls()
1188 n.users_group = users_group
1186 n.users_group = users_group
1189 n.repository = repository
1187 n.repository = repository
1190 n.permission = permission
1188 n.permission = permission
1191 Session().add(n)
1189 Session().add(n)
1192 return n
1190 return n
1193
1191
1194 def __unicode__(self):
1192 def __unicode__(self):
1195 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
1193 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
1196
1194
1197
1195
1198 class UsersGroupToPerm(Base, BaseModel):
1196 class UsersGroupToPerm(Base, BaseModel):
1199 __tablename__ = 'users_group_to_perm'
1197 __tablename__ = 'users_group_to_perm'
1200 __table_args__ = (
1198 __table_args__ = (
1201 UniqueConstraint('users_group_id', 'permission_id',),
1199 UniqueConstraint('users_group_id', 'permission_id',),
1202 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1200 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1203 'mysql_charset': 'utf8'}
1201 'mysql_charset': 'utf8'}
1204 )
1202 )
1205 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1203 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1206 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1204 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1207 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1205 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1208
1206
1209 users_group = relationship('UsersGroup')
1207 users_group = relationship('UsersGroup')
1210 permission = relationship('Permission')
1208 permission = relationship('Permission')
1211
1209
1212
1210
1213 class UserRepoGroupToPerm(Base, BaseModel):
1211 class UserRepoGroupToPerm(Base, BaseModel):
1214 __tablename__ = 'user_repo_group_to_perm'
1212 __tablename__ = 'user_repo_group_to_perm'
1215 __table_args__ = (
1213 __table_args__ = (
1216 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1214 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1217 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1215 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1218 'mysql_charset': 'utf8'}
1216 'mysql_charset': 'utf8'}
1219 )
1217 )
1220
1218
1221 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1219 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1222 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1220 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1223 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1221 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1224 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1222 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1225
1223
1226 user = relationship('User')
1224 user = relationship('User')
1227 group = relationship('RepoGroup')
1225 group = relationship('RepoGroup')
1228 permission = relationship('Permission')
1226 permission = relationship('Permission')
1229
1227
1230
1228
1231 class UsersGroupRepoGroupToPerm(Base, BaseModel):
1229 class UsersGroupRepoGroupToPerm(Base, BaseModel):
1232 __tablename__ = 'users_group_repo_group_to_perm'
1230 __tablename__ = 'users_group_repo_group_to_perm'
1233 __table_args__ = (
1231 __table_args__ = (
1234 UniqueConstraint('users_group_id', 'group_id'),
1232 UniqueConstraint('users_group_id', 'group_id'),
1235 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1233 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1236 'mysql_charset': 'utf8'}
1234 'mysql_charset': 'utf8'}
1237 )
1235 )
1238
1236
1239 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)
1237 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)
1240 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1238 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1241 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1239 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1242 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1240 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1243
1241
1244 users_group = relationship('UsersGroup')
1242 users_group = relationship('UsersGroup')
1245 permission = relationship('Permission')
1243 permission = relationship('Permission')
1246 group = relationship('RepoGroup')
1244 group = relationship('RepoGroup')
1247
1245
1248
1246
1249 class Statistics(Base, BaseModel):
1247 class Statistics(Base, BaseModel):
1250 __tablename__ = 'statistics'
1248 __tablename__ = 'statistics'
1251 __table_args__ = (
1249 __table_args__ = (
1252 UniqueConstraint('repository_id'),
1250 UniqueConstraint('repository_id'),
1253 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1251 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1254 'mysql_charset': 'utf8'}
1252 'mysql_charset': 'utf8'}
1255 )
1253 )
1256 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1254 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1257 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1255 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1258 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1256 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1259 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1257 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1260 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1258 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1261 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1259 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1262
1260
1263 repository = relationship('Repository', single_parent=True)
1261 repository = relationship('Repository', single_parent=True)
1264
1262
1265
1263
1266 class UserFollowing(Base, BaseModel):
1264 class UserFollowing(Base, BaseModel):
1267 __tablename__ = 'user_followings'
1265 __tablename__ = 'user_followings'
1268 __table_args__ = (
1266 __table_args__ = (
1269 UniqueConstraint('user_id', 'follows_repository_id'),
1267 UniqueConstraint('user_id', 'follows_repository_id'),
1270 UniqueConstraint('user_id', 'follows_user_id'),
1268 UniqueConstraint('user_id', 'follows_user_id'),
1271 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1269 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1272 'mysql_charset': 'utf8'}
1270 'mysql_charset': 'utf8'}
1273 )
1271 )
1274
1272
1275 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1273 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1276 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1274 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1277 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1275 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1278 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1276 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1279 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1277 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1280
1278
1281 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1279 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1282
1280
1283 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1281 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1284 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1282 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1285
1283
1286 @classmethod
1284 @classmethod
1287 def get_repo_followers(cls, repo_id):
1285 def get_repo_followers(cls, repo_id):
1288 return cls.query().filter(cls.follows_repo_id == repo_id)
1286 return cls.query().filter(cls.follows_repo_id == repo_id)
1289
1287
1290
1288
1291 class CacheInvalidation(Base, BaseModel):
1289 class CacheInvalidation(Base, BaseModel):
1292 __tablename__ = 'cache_invalidation'
1290 __tablename__ = 'cache_invalidation'
1293 __table_args__ = (
1291 __table_args__ = (
1294 UniqueConstraint('cache_key'),
1292 UniqueConstraint('cache_key'),
1295 Index('key_idx', 'cache_key'),
1293 Index('key_idx', 'cache_key'),
1296 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1294 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1297 'mysql_charset': 'utf8'},
1295 'mysql_charset': 'utf8'},
1298 )
1296 )
1299 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1297 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1300 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1298 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1301 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1299 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1302 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1300 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1303
1301
1304 def __init__(self, cache_key, cache_args=''):
1302 def __init__(self, cache_key, cache_args=''):
1305 self.cache_key = cache_key
1303 self.cache_key = cache_key
1306 self.cache_args = cache_args
1304 self.cache_args = cache_args
1307 self.cache_active = False
1305 self.cache_active = False
1308
1306
1309 def __unicode__(self):
1307 def __unicode__(self):
1310 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1308 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1311 self.cache_id, self.cache_key)
1309 self.cache_id, self.cache_key)
1312
1310
1313 @classmethod
1311 @classmethod
1314 def clear_cache(cls):
1312 def clear_cache(cls):
1315 cls.query().delete()
1313 cls.query().delete()
1316
1314
1317 @classmethod
1315 @classmethod
1318 def _get_key(cls, key):
1316 def _get_key(cls, key):
1319 """
1317 """
1320 Wrapper for generating a key, together with a prefix
1318 Wrapper for generating a key, together with a prefix
1321
1319
1322 :param key:
1320 :param key:
1323 """
1321 """
1324 import rhodecode
1322 import rhodecode
1325 prefix = ''
1323 prefix = ''
1326 iid = rhodecode.CONFIG.get('instance_id')
1324 iid = rhodecode.CONFIG.get('instance_id')
1327 if iid:
1325 if iid:
1328 prefix = iid
1326 prefix = iid
1329 return "%s%s" % (prefix, key), prefix, key.rstrip('_README')
1327 return "%s%s" % (prefix, key), prefix, key.rstrip('_README')
1330
1328
1331 @classmethod
1329 @classmethod
1332 def get_by_key(cls, key):
1330 def get_by_key(cls, key):
1333 return cls.query().filter(cls.cache_key == key).scalar()
1331 return cls.query().filter(cls.cache_key == key).scalar()
1334
1332
1335 @classmethod
1333 @classmethod
1336 def _get_or_create_key(cls, key, prefix, org_key):
1334 def _get_or_create_key(cls, key, prefix, org_key):
1337 inv_obj = Session().query(cls).filter(cls.cache_key == key).scalar()
1335 inv_obj = Session().query(cls).filter(cls.cache_key == key).scalar()
1338 if not inv_obj:
1336 if not inv_obj:
1339 try:
1337 try:
1340 inv_obj = CacheInvalidation(key, org_key)
1338 inv_obj = CacheInvalidation(key, org_key)
1341 Session().add(inv_obj)
1339 Session().add(inv_obj)
1342 Session().commit()
1340 Session().commit()
1343 except Exception:
1341 except Exception:
1344 log.error(traceback.format_exc())
1342 log.error(traceback.format_exc())
1345 Session().rollback()
1343 Session().rollback()
1346 return inv_obj
1344 return inv_obj
1347
1345
1348 @classmethod
1346 @classmethod
1349 def invalidate(cls, key):
1347 def invalidate(cls, key):
1350 """
1348 """
1351 Returns Invalidation object if this given key should be invalidated
1349 Returns Invalidation object if this given key should be invalidated
1352 None otherwise. `cache_active = False` means that this cache
1350 None otherwise. `cache_active = False` means that this cache
1353 state is not valid and needs to be invalidated
1351 state is not valid and needs to be invalidated
1354
1352
1355 :param key:
1353 :param key:
1356 """
1354 """
1357
1355
1358 key, _prefix, _org_key = cls._get_key(key)
1356 key, _prefix, _org_key = cls._get_key(key)
1359 inv = cls._get_or_create_key(key, _prefix, _org_key)
1357 inv = cls._get_or_create_key(key, _prefix, _org_key)
1360
1358
1361 if inv and inv.cache_active is False:
1359 if inv and inv.cache_active is False:
1362 return inv
1360 return inv
1363
1361
1364 @classmethod
1362 @classmethod
1365 def set_invalidate(cls, key):
1363 def set_invalidate(cls, key):
1366 """
1364 """
1367 Mark this Cache key for invalidation
1365 Mark this Cache key for invalidation
1368
1366
1369 :param key:
1367 :param key:
1370 """
1368 """
1371
1369
1372 key, _prefix, _org_key = cls._get_key(key)
1370 key, _prefix, _org_key = cls._get_key(key)
1373 inv_objs = Session().query(cls).filter(cls.cache_args == _org_key).all()
1371 inv_objs = Session().query(cls).filter(cls.cache_args == _org_key).all()
1374 log.debug('marking %s key[s] %s for invalidation' % (len(inv_objs),
1372 log.debug('marking %s key[s] %s for invalidation' % (len(inv_objs),
1375 _org_key))
1373 _org_key))
1376 try:
1374 try:
1377 for inv_obj in inv_objs:
1375 for inv_obj in inv_objs:
1378 if inv_obj:
1376 if inv_obj:
1379 inv_obj.cache_active = False
1377 inv_obj.cache_active = False
1380
1378
1381 Session().add(inv_obj)
1379 Session().add(inv_obj)
1382 Session().commit()
1380 Session().commit()
1383 except Exception:
1381 except Exception:
1384 log.error(traceback.format_exc())
1382 log.error(traceback.format_exc())
1385 Session().rollback()
1383 Session().rollback()
1386
1384
1387 @classmethod
1385 @classmethod
1388 def set_valid(cls, key):
1386 def set_valid(cls, key):
1389 """
1387 """
1390 Mark this cache key as active and currently cached
1388 Mark this cache key as active and currently cached
1391
1389
1392 :param key:
1390 :param key:
1393 """
1391 """
1394 inv_obj = cls.get_by_key(key)
1392 inv_obj = cls.get_by_key(key)
1395 inv_obj.cache_active = True
1393 inv_obj.cache_active = True
1396 Session().add(inv_obj)
1394 Session().add(inv_obj)
1397 Session().commit()
1395 Session().commit()
1398
1396
1399 @classmethod
1397 @classmethod
1400 def get_cache_map(cls):
1398 def get_cache_map(cls):
1401
1399
1402 class cachemapdict(dict):
1400 class cachemapdict(dict):
1403
1401
1404 def __init__(self, *args, **kwargs):
1402 def __init__(self, *args, **kwargs):
1405 fixkey = kwargs.get('fixkey')
1403 fixkey = kwargs.get('fixkey')
1406 if fixkey:
1404 if fixkey:
1407 del kwargs['fixkey']
1405 del kwargs['fixkey']
1408 self.fixkey = fixkey
1406 self.fixkey = fixkey
1409 super(cachemapdict, self).__init__(*args, **kwargs)
1407 super(cachemapdict, self).__init__(*args, **kwargs)
1410
1408
1411 def __getattr__(self, name):
1409 def __getattr__(self, name):
1412 key = name
1410 key = name
1413 if self.fixkey:
1411 if self.fixkey:
1414 key, _prefix, _org_key = cls._get_key(key)
1412 key, _prefix, _org_key = cls._get_key(key)
1415 if key in self.__dict__:
1413 if key in self.__dict__:
1416 return self.__dict__[key]
1414 return self.__dict__[key]
1417 else:
1415 else:
1418 return self[key]
1416 return self[key]
1419
1417
1420 def __getitem__(self, key):
1418 def __getitem__(self, key):
1421 if self.fixkey:
1419 if self.fixkey:
1422 key, _prefix, _org_key = cls._get_key(key)
1420 key, _prefix, _org_key = cls._get_key(key)
1423 try:
1421 try:
1424 return super(cachemapdict, self).__getitem__(key)
1422 return super(cachemapdict, self).__getitem__(key)
1425 except KeyError:
1423 except KeyError:
1426 return
1424 return
1427
1425
1428 cache_map = cachemapdict(fixkey=True)
1426 cache_map = cachemapdict(fixkey=True)
1429 for obj in cls.query().all():
1427 for obj in cls.query().all():
1430 cache_map[obj.cache_key] = cachemapdict(obj.get_dict())
1428 cache_map[obj.cache_key] = cachemapdict(obj.get_dict())
1431 return cache_map
1429 return cache_map
1432
1430
1433
1431
1434 class ChangesetComment(Base, BaseModel):
1432 class ChangesetComment(Base, BaseModel):
1435 __tablename__ = 'changeset_comments'
1433 __tablename__ = 'changeset_comments'
1436 __table_args__ = (
1434 __table_args__ = (
1437 Index('cc_revision_idx', 'revision'),
1435 Index('cc_revision_idx', 'revision'),
1438 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1436 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1439 'mysql_charset': 'utf8'},
1437 'mysql_charset': 'utf8'},
1440 )
1438 )
1441 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1439 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1442 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1440 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1443 revision = Column('revision', String(40), nullable=True)
1441 revision = Column('revision', String(40), nullable=True)
1444 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1442 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1445 line_no = Column('line_no', Unicode(10), nullable=True)
1443 line_no = Column('line_no', Unicode(10), nullable=True)
1446 f_path = Column('f_path', Unicode(1000), nullable=True)
1444 f_path = Column('f_path', Unicode(1000), nullable=True)
1447 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1445 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1448 text = Column('text', Unicode(25000), nullable=False)
1446 text = Column('text', Unicode(25000), nullable=False)
1449 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1447 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1450
1448
1451 author = relationship('User', lazy='joined')
1449 author = relationship('User', lazy='joined')
1452 repo = relationship('Repository')
1450 repo = relationship('Repository')
1453 status_change = relationship('ChangesetStatus', uselist=False)
1451 status_change = relationship('ChangesetStatus', uselist=False)
1454 pull_request = relationship('PullRequest', lazy='joined')
1452 pull_request = relationship('PullRequest', lazy='joined')
1455
1453
1456 @classmethod
1454 @classmethod
1457 def get_users(cls, revision=None, pull_request_id=None):
1455 def get_users(cls, revision=None, pull_request_id=None):
1458 """
1456 """
1459 Returns user associated with this ChangesetComment. ie those
1457 Returns user associated with this ChangesetComment. ie those
1460 who actually commented
1458 who actually commented
1461
1459
1462 :param cls:
1460 :param cls:
1463 :param revision:
1461 :param revision:
1464 """
1462 """
1465 q = Session().query(User)\
1463 q = Session().query(User)\
1466 .join(ChangesetComment.author)
1464 .join(ChangesetComment.author)
1467 if revision:
1465 if revision:
1468 q = q.filter(cls.revision == revision)
1466 q = q.filter(cls.revision == revision)
1469 elif pull_request_id:
1467 elif pull_request_id:
1470 q = q.filter(cls.pull_request_id == pull_request_id)
1468 q = q.filter(cls.pull_request_id == pull_request_id)
1471 return q.all()
1469 return q.all()
1472
1470
1473
1471
1474 class ChangesetStatus(Base, BaseModel):
1472 class ChangesetStatus(Base, BaseModel):
1475 __tablename__ = 'changeset_statuses'
1473 __tablename__ = 'changeset_statuses'
1476 __table_args__ = (
1474 __table_args__ = (
1477 Index('cs_revision_idx', 'revision'),
1475 Index('cs_revision_idx', 'revision'),
1478 Index('cs_version_idx', 'version'),
1476 Index('cs_version_idx', 'version'),
1479 UniqueConstraint('repo_id', 'revision', 'version'),
1477 UniqueConstraint('repo_id', 'revision', 'version'),
1480 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1478 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1481 'mysql_charset': 'utf8'}
1479 'mysql_charset': 'utf8'}
1482 )
1480 )
1483 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1481 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1484 STATUS_APPROVED = 'approved'
1482 STATUS_APPROVED = 'approved'
1485 STATUS_REJECTED = 'rejected'
1483 STATUS_REJECTED = 'rejected'
1486 STATUS_UNDER_REVIEW = 'under_review'
1484 STATUS_UNDER_REVIEW = 'under_review'
1487
1485
1488 STATUSES = [
1486 STATUSES = [
1489 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1487 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1490 (STATUS_APPROVED, _("Approved")),
1488 (STATUS_APPROVED, _("Approved")),
1491 (STATUS_REJECTED, _("Rejected")),
1489 (STATUS_REJECTED, _("Rejected")),
1492 (STATUS_UNDER_REVIEW, _("Under Review")),
1490 (STATUS_UNDER_REVIEW, _("Under Review")),
1493 ]
1491 ]
1494
1492
1495 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1493 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1496 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1494 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1497 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1495 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1498 revision = Column('revision', String(40), nullable=False)
1496 revision = Column('revision', String(40), nullable=False)
1499 status = Column('status', String(128), nullable=False, default=DEFAULT)
1497 status = Column('status', String(128), nullable=False, default=DEFAULT)
1500 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1498 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1501 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1499 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1502 version = Column('version', Integer(), nullable=False, default=0)
1500 version = Column('version', Integer(), nullable=False, default=0)
1503 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1501 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1504
1502
1505 author = relationship('User', lazy='joined')
1503 author = relationship('User', lazy='joined')
1506 repo = relationship('Repository')
1504 repo = relationship('Repository')
1507 comment = relationship('ChangesetComment', lazy='joined')
1505 comment = relationship('ChangesetComment', lazy='joined')
1508 pull_request = relationship('PullRequest', lazy='joined')
1506 pull_request = relationship('PullRequest', lazy='joined')
1509
1507
1510 def __unicode__(self):
1508 def __unicode__(self):
1511 return u"<%s('%s:%s')>" % (
1509 return u"<%s('%s:%s')>" % (
1512 self.__class__.__name__,
1510 self.__class__.__name__,
1513 self.status, self.author
1511 self.status, self.author
1514 )
1512 )
1515
1513
1516 @classmethod
1514 @classmethod
1517 def get_status_lbl(cls, value):
1515 def get_status_lbl(cls, value):
1518 return dict(cls.STATUSES).get(value)
1516 return dict(cls.STATUSES).get(value)
1519
1517
1520 @property
1518 @property
1521 def status_lbl(self):
1519 def status_lbl(self):
1522 return ChangesetStatus.get_status_lbl(self.status)
1520 return ChangesetStatus.get_status_lbl(self.status)
1523
1521
1524
1522
1525 class PullRequest(Base, BaseModel):
1523 class PullRequest(Base, BaseModel):
1526 __tablename__ = 'pull_requests'
1524 __tablename__ = 'pull_requests'
1527 __table_args__ = (
1525 __table_args__ = (
1528 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1526 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1529 'mysql_charset': 'utf8'},
1527 'mysql_charset': 'utf8'},
1530 )
1528 )
1531
1529
1532 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1530 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1533 title = Column('title', Unicode(256), nullable=True)
1531 title = Column('title', Unicode(256), nullable=True)
1534 description = Column('description', Unicode(10240), nullable=True)
1532 description = Column('description', Unicode(10240), nullable=True)
1535 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1533 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1536 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1534 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1537 _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
1535 _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
1538 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1536 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1539 org_ref = Column('org_ref', Unicode(256), nullable=False)
1537 org_ref = Column('org_ref', Unicode(256), nullable=False)
1540 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1538 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1541 other_ref = Column('other_ref', Unicode(256), nullable=False)
1539 other_ref = Column('other_ref', Unicode(256), nullable=False)
1542
1540
1543 @hybrid_property
1541 @hybrid_property
1544 def revisions(self):
1542 def revisions(self):
1545 return self._revisions.split(':')
1543 return self._revisions.split(':')
1546
1544
1547 @revisions.setter
1545 @revisions.setter
1548 def revisions(self, val):
1546 def revisions(self, val):
1549 self._revisions = ':'.join(val)
1547 self._revisions = ':'.join(val)
1550
1548
1551 author = relationship('User', lazy='joined')
1549 author = relationship('User', lazy='joined')
1552 reviewers = relationship('PullRequestReviewers')
1550 reviewers = relationship('PullRequestReviewers')
1553 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1551 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1554 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1552 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1555
1553
1556 def __json__(self):
1554 def __json__(self):
1557 return dict(
1555 return dict(
1558 revisions=self.revisions
1556 revisions=self.revisions
1559 )
1557 )
1560
1558
1561
1559
1562 class PullRequestReviewers(Base, BaseModel):
1560 class PullRequestReviewers(Base, BaseModel):
1563 __tablename__ = 'pull_request_reviewers'
1561 __tablename__ = 'pull_request_reviewers'
1564 __table_args__ = (
1562 __table_args__ = (
1565 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1563 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1566 'mysql_charset': 'utf8'},
1564 'mysql_charset': 'utf8'},
1567 )
1565 )
1568
1566
1569 def __init__(self, user=None, pull_request=None):
1567 def __init__(self, user=None, pull_request=None):
1570 self.user = user
1568 self.user = user
1571 self.pull_request = pull_request
1569 self.pull_request = pull_request
1572
1570
1573 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1571 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1574 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1572 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1575 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1573 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1576
1574
1577 user = relationship('User')
1575 user = relationship('User')
1578 pull_request = relationship('PullRequest')
1576 pull_request = relationship('PullRequest')
1579
1577
1580
1578
1581 class Notification(Base, BaseModel):
1579 class Notification(Base, BaseModel):
1582 __tablename__ = 'notifications'
1580 __tablename__ = 'notifications'
1583 __table_args__ = (
1581 __table_args__ = (
1584 Index('notification_type_idx', 'type'),
1582 Index('notification_type_idx', 'type'),
1585 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1583 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1586 'mysql_charset': 'utf8'},
1584 'mysql_charset': 'utf8'},
1587 )
1585 )
1588
1586
1589 TYPE_CHANGESET_COMMENT = u'cs_comment'
1587 TYPE_CHANGESET_COMMENT = u'cs_comment'
1590 TYPE_MESSAGE = u'message'
1588 TYPE_MESSAGE = u'message'
1591 TYPE_MENTION = u'mention'
1589 TYPE_MENTION = u'mention'
1592 TYPE_REGISTRATION = u'registration'
1590 TYPE_REGISTRATION = u'registration'
1593 TYPE_PULL_REQUEST = u'pull_request'
1591 TYPE_PULL_REQUEST = u'pull_request'
1594 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1592 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1595
1593
1596 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1594 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1597 subject = Column('subject', Unicode(512), nullable=True)
1595 subject = Column('subject', Unicode(512), nullable=True)
1598 body = Column('body', Unicode(50000), nullable=True)
1596 body = Column('body', Unicode(50000), nullable=True)
1599 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1597 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1600 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1598 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1601 type_ = Column('type', Unicode(256))
1599 type_ = Column('type', Unicode(256))
1602
1600
1603 created_by_user = relationship('User')
1601 created_by_user = relationship('User')
1604 notifications_to_users = relationship('UserNotification', lazy='joined',
1602 notifications_to_users = relationship('UserNotification', lazy='joined',
1605 cascade="all, delete, delete-orphan")
1603 cascade="all, delete, delete-orphan")
1606
1604
1607 @property
1605 @property
1608 def recipients(self):
1606 def recipients(self):
1609 return [x.user for x in UserNotification.query()\
1607 return [x.user for x in UserNotification.query()\
1610 .filter(UserNotification.notification == self)\
1608 .filter(UserNotification.notification == self)\
1611 .order_by(UserNotification.user).all()]
1609 .order_by(UserNotification.user).all()]
1612
1610
1613 @classmethod
1611 @classmethod
1614 def create(cls, created_by, subject, body, recipients, type_=None):
1612 def create(cls, created_by, subject, body, recipients, type_=None):
1615 if type_ is None:
1613 if type_ is None:
1616 type_ = Notification.TYPE_MESSAGE
1614 type_ = Notification.TYPE_MESSAGE
1617
1615
1618 notification = cls()
1616 notification = cls()
1619 notification.created_by_user = created_by
1617 notification.created_by_user = created_by
1620 notification.subject = subject
1618 notification.subject = subject
1621 notification.body = body
1619 notification.body = body
1622 notification.type_ = type_
1620 notification.type_ = type_
1623 notification.created_on = datetime.datetime.now()
1621 notification.created_on = datetime.datetime.now()
1624
1622
1625 for u in recipients:
1623 for u in recipients:
1626 assoc = UserNotification()
1624 assoc = UserNotification()
1627 assoc.notification = notification
1625 assoc.notification = notification
1628 u.notifications.append(assoc)
1626 u.notifications.append(assoc)
1629 Session().add(notification)
1627 Session().add(notification)
1630 return notification
1628 return notification
1631
1629
1632 @property
1630 @property
1633 def description(self):
1631 def description(self):
1634 from rhodecode.model.notification import NotificationModel
1632 from rhodecode.model.notification import NotificationModel
1635 return NotificationModel().make_description(self)
1633 return NotificationModel().make_description(self)
1636
1634
1637
1635
1638 class UserNotification(Base, BaseModel):
1636 class UserNotification(Base, BaseModel):
1639 __tablename__ = 'user_to_notification'
1637 __tablename__ = 'user_to_notification'
1640 __table_args__ = (
1638 __table_args__ = (
1641 UniqueConstraint('user_id', 'notification_id'),
1639 UniqueConstraint('user_id', 'notification_id'),
1642 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1640 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1643 'mysql_charset': 'utf8'}
1641 'mysql_charset': 'utf8'}
1644 )
1642 )
1645 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1643 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1646 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1644 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1647 read = Column('read', Boolean, default=False)
1645 read = Column('read', Boolean, default=False)
1648 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1646 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1649
1647
1650 user = relationship('User', lazy="joined")
1648 user = relationship('User', lazy="joined")
1651 notification = relationship('Notification', lazy="joined",
1649 notification = relationship('Notification', lazy="joined",
1652 order_by=lambda: Notification.created_on.desc(),)
1650 order_by=lambda: Notification.created_on.desc(),)
1653
1651
1654 def mark_as_read(self):
1652 def mark_as_read(self):
1655 self.read = True
1653 self.read = True
1656 Session().add(self)
1654 Session().add(self)
1657
1655
1658
1656
1659 class DbMigrateVersion(Base, BaseModel):
1657 class DbMigrateVersion(Base, BaseModel):
1660 __tablename__ = 'db_migrate_version'
1658 __tablename__ = 'db_migrate_version'
1661 __table_args__ = (
1659 __table_args__ = (
1662 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1660 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1663 'mysql_charset': 'utf8'},
1661 'mysql_charset': 'utf8'},
1664 )
1662 )
1665 repository_id = Column('repository_id', String(250), primary_key=True)
1663 repository_id = Column('repository_id', String(250), primary_key=True)
1666 repository_path = Column('repository_path', Text)
1664 repository_path = Column('repository_path', Text)
1667 version = Column('version', Integer)
1665 version = Column('version', Integer)
@@ -1,309 +1,310 b''
1 """ this is forms validation classes
1 """ this is forms validation classes
2 http://formencode.org/module-formencode.validators.html
2 http://formencode.org/module-formencode.validators.html
3 for list off all availible validators
3 for list off all availible validators
4
4
5 we can create our own validators
5 we can create our own validators
6
6
7 The table below outlines the options which can be used in a schema in addition to the validators themselves
7 The table below outlines the options which can be used in a schema in addition to the validators themselves
8 pre_validators [] These validators will be applied before the schema
8 pre_validators [] These validators will be applied before the schema
9 chained_validators [] These validators will be applied after the schema
9 chained_validators [] These validators will be applied after the schema
10 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
10 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
12 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
12 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
13 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
13 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
14
14
15
15
16 <name> = formencode.validators.<name of validator>
16 <name> = formencode.validators.<name of validator>
17 <name> must equal form name
17 <name> must equal form name
18 list=[1,2,3,4,5]
18 list=[1,2,3,4,5]
19 for SELECT use formencode.All(OneOf(list), Int())
19 for SELECT use formencode.All(OneOf(list), Int())
20
20
21 """
21 """
22 import logging
22 import logging
23
23
24 import formencode
24 import formencode
25 from formencode import All
25 from formencode import All
26
26
27 from pylons.i18n.translation import _
27 from pylons.i18n.translation import _
28
28
29 from rhodecode.model import validators as v
29 from rhodecode.model import validators as v
30 from rhodecode import BACKENDS
30 from rhodecode import BACKENDS
31
31
32 log = logging.getLogger(__name__)
32 log = logging.getLogger(__name__)
33
33
34
34
35 class LoginForm(formencode.Schema):
35 class LoginForm(formencode.Schema):
36 allow_extra_fields = True
36 allow_extra_fields = True
37 filter_extra_fields = True
37 filter_extra_fields = True
38 username = v.UnicodeString(
38 username = v.UnicodeString(
39 strip=True,
39 strip=True,
40 min=1,
40 min=1,
41 not_empty=True,
41 not_empty=True,
42 messages={
42 messages={
43 'empty': _(u'Please enter a login'),
43 'empty': _(u'Please enter a login'),
44 'tooShort': _(u'Enter a value %(min)i characters long or more')}
44 'tooShort': _(u'Enter a value %(min)i characters long or more')}
45 )
45 )
46
46
47 password = v.UnicodeString(
47 password = v.UnicodeString(
48 strip=False,
48 strip=False,
49 min=3,
49 min=3,
50 not_empty=True,
50 not_empty=True,
51 messages={
51 messages={
52 'empty': _(u'Please enter a password'),
52 'empty': _(u'Please enter a password'),
53 'tooShort': _(u'Enter %(min)i characters or more')}
53 'tooShort': _(u'Enter %(min)i characters or more')}
54 )
54 )
55
55
56 remember = v.StringBoolean(if_missing=False)
56 remember = v.StringBoolean(if_missing=False)
57
57
58 chained_validators = [v.ValidAuth()]
58 chained_validators = [v.ValidAuth()]
59
59
60
60
61 def UserForm(edit=False, old_data={}):
61 def UserForm(edit=False, old_data={}):
62 class _UserForm(formencode.Schema):
62 class _UserForm(formencode.Schema):
63 allow_extra_fields = True
63 allow_extra_fields = True
64 filter_extra_fields = True
64 filter_extra_fields = True
65 username = All(v.UnicodeString(strip=True, min=1, not_empty=True),
65 username = All(v.UnicodeString(strip=True, min=1, not_empty=True),
66 v.ValidUsername(edit, old_data))
66 v.ValidUsername(edit, old_data))
67 if edit:
67 if edit:
68 new_password = All(
68 new_password = All(
69 v.ValidPassword(),
69 v.UnicodeString(strip=False, min=6, not_empty=False)
70 v.UnicodeString(strip=False, min=6, not_empty=False)
70 )
71 )
71 password_confirmation = All(
72 password_confirmation = All(
72 v.ValidPassword(),
73 v.ValidPassword(),
73 v.UnicodeString(strip=False, min=6, not_empty=False),
74 v.UnicodeString(strip=False, min=6, not_empty=False),
74 )
75 )
75 admin = v.StringBoolean(if_missing=False)
76 admin = v.StringBoolean(if_missing=False)
76 else:
77 else:
77 password = All(
78 password = All(
78 v.ValidPassword(),
79 v.ValidPassword(),
79 v.UnicodeString(strip=False, min=6, not_empty=True)
80 v.UnicodeString(strip=False, min=6, not_empty=True)
80 )
81 )
81 password_confirmation = All(
82 password_confirmation = All(
82 v.ValidPassword(),
83 v.ValidPassword(),
83 v.UnicodeString(strip=False, min=6, not_empty=False)
84 v.UnicodeString(strip=False, min=6, not_empty=False)
84 )
85 )
85
86
86 active = v.StringBoolean(if_missing=False)
87 active = v.StringBoolean(if_missing=False)
87 name = v.UnicodeString(strip=True, min=1, not_empty=False)
88 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
88 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
89 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
89 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
90 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
90
91
91 chained_validators = [v.ValidPasswordsMatch()]
92 chained_validators = [v.ValidPasswordsMatch()]
92
93
93 return _UserForm
94 return _UserForm
94
95
95
96
96 def UsersGroupForm(edit=False, old_data={}, available_members=[]):
97 def UsersGroupForm(edit=False, old_data={}, available_members=[]):
97 class _UsersGroupForm(formencode.Schema):
98 class _UsersGroupForm(formencode.Schema):
98 allow_extra_fields = True
99 allow_extra_fields = True
99 filter_extra_fields = True
100 filter_extra_fields = True
100
101
101 users_group_name = All(
102 users_group_name = All(
102 v.UnicodeString(strip=True, min=1, not_empty=True),
103 v.UnicodeString(strip=True, min=1, not_empty=True),
103 v.ValidUsersGroup(edit, old_data)
104 v.ValidUsersGroup(edit, old_data)
104 )
105 )
105
106
106 users_group_active = v.StringBoolean(if_missing=False)
107 users_group_active = v.StringBoolean(if_missing=False)
107
108
108 if edit:
109 if edit:
109 users_group_members = v.OneOf(
110 users_group_members = v.OneOf(
110 available_members, hideList=False, testValueList=True,
111 available_members, hideList=False, testValueList=True,
111 if_missing=None, not_empty=False
112 if_missing=None, not_empty=False
112 )
113 )
113
114
114 return _UsersGroupForm
115 return _UsersGroupForm
115
116
116
117
117 def ReposGroupForm(edit=False, old_data={}, available_groups=[]):
118 def ReposGroupForm(edit=False, old_data={}, available_groups=[]):
118 class _ReposGroupForm(formencode.Schema):
119 class _ReposGroupForm(formencode.Schema):
119 allow_extra_fields = True
120 allow_extra_fields = True
120 filter_extra_fields = False
121 filter_extra_fields = False
121
122
122 group_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
123 group_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
123 v.SlugifyName())
124 v.SlugifyName())
124 group_description = v.UnicodeString(strip=True, min=1,
125 group_description = v.UnicodeString(strip=True, min=1,
125 not_empty=True)
126 not_empty=True)
126 group_parent_id = v.OneOf(available_groups, hideList=False,
127 group_parent_id = v.OneOf(available_groups, hideList=False,
127 testValueList=True,
128 testValueList=True,
128 if_missing=None, not_empty=False)
129 if_missing=None, not_empty=False)
129
130
130 chained_validators = [v.ValidReposGroup(edit, old_data),
131 chained_validators = [v.ValidReposGroup(edit, old_data),
131 v.ValidPerms('group')]
132 v.ValidPerms('group')]
132
133
133 return _ReposGroupForm
134 return _ReposGroupForm
134
135
135
136
136 def RegisterForm(edit=False, old_data={}):
137 def RegisterForm(edit=False, old_data={}):
137 class _RegisterForm(formencode.Schema):
138 class _RegisterForm(formencode.Schema):
138 allow_extra_fields = True
139 allow_extra_fields = True
139 filter_extra_fields = True
140 filter_extra_fields = True
140 username = All(
141 username = All(
141 v.ValidUsername(edit, old_data),
142 v.ValidUsername(edit, old_data),
142 v.UnicodeString(strip=True, min=1, not_empty=True)
143 v.UnicodeString(strip=True, min=1, not_empty=True)
143 )
144 )
144 password = All(
145 password = All(
145 v.ValidPassword(),
146 v.ValidPassword(),
146 v.UnicodeString(strip=False, min=6, not_empty=True)
147 v.UnicodeString(strip=False, min=6, not_empty=True)
147 )
148 )
148 password_confirmation = All(
149 password_confirmation = All(
149 v.ValidPassword(),
150 v.ValidPassword(),
150 v.UnicodeString(strip=False, min=6, not_empty=True)
151 v.UnicodeString(strip=False, min=6, not_empty=True)
151 )
152 )
152 active = v.StringBoolean(if_missing=False)
153 active = v.StringBoolean(if_missing=False)
153 name = v.UnicodeString(strip=True, min=1, not_empty=False)
154 name = v.UnicodeString(strip=True, min=1, not_empty=False)
154 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
155 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
155 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
156 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
156
157
157 chained_validators = [v.ValidPasswordsMatch()]
158 chained_validators = [v.ValidPasswordsMatch()]
158
159
159 return _RegisterForm
160 return _RegisterForm
160
161
161
162
162 def PasswordResetForm():
163 def PasswordResetForm():
163 class _PasswordResetForm(formencode.Schema):
164 class _PasswordResetForm(formencode.Schema):
164 allow_extra_fields = True
165 allow_extra_fields = True
165 filter_extra_fields = True
166 filter_extra_fields = True
166 email = All(v.ValidSystemEmail(), v.Email(not_empty=True))
167 email = All(v.ValidSystemEmail(), v.Email(not_empty=True))
167 return _PasswordResetForm
168 return _PasswordResetForm
168
169
169
170
170 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
171 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
171 repo_groups=[], landing_revs=[]):
172 repo_groups=[], landing_revs=[]):
172 class _RepoForm(formencode.Schema):
173 class _RepoForm(formencode.Schema):
173 allow_extra_fields = True
174 allow_extra_fields = True
174 filter_extra_fields = False
175 filter_extra_fields = False
175 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
176 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
176 v.SlugifyName())
177 v.SlugifyName())
177 clone_uri = All(v.UnicodeString(strip=True, min=1, not_empty=False))
178 clone_uri = All(v.UnicodeString(strip=True, min=1, not_empty=False))
178 repo_group = v.OneOf(repo_groups, hideList=True)
179 repo_group = v.OneOf(repo_groups, hideList=True)
179 repo_type = v.OneOf(supported_backends)
180 repo_type = v.OneOf(supported_backends)
180 description = v.UnicodeString(strip=True, min=1, not_empty=False)
181 description = v.UnicodeString(strip=True, min=1, not_empty=False)
181 private = v.StringBoolean(if_missing=False)
182 private = v.StringBoolean(if_missing=False)
182 enable_statistics = v.StringBoolean(if_missing=False)
183 enable_statistics = v.StringBoolean(if_missing=False)
183 enable_downloads = v.StringBoolean(if_missing=False)
184 enable_downloads = v.StringBoolean(if_missing=False)
184 landing_rev = v.OneOf(landing_revs, hideList=True)
185 landing_rev = v.OneOf(landing_revs, hideList=True)
185
186
186 if edit:
187 if edit:
187 #this is repo owner
188 #this is repo owner
188 user = All(v.UnicodeString(not_empty=True), v.ValidRepoUser())
189 user = All(v.UnicodeString(not_empty=True), v.ValidRepoUser())
189
190
190 chained_validators = [v.ValidCloneUri(),
191 chained_validators = [v.ValidCloneUri(),
191 v.ValidRepoName(edit, old_data),
192 v.ValidRepoName(edit, old_data),
192 v.ValidPerms()]
193 v.ValidPerms()]
193 return _RepoForm
194 return _RepoForm
194
195
195
196
196 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
197 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
197 repo_groups=[], landing_revs=[]):
198 repo_groups=[], landing_revs=[]):
198 class _RepoForkForm(formencode.Schema):
199 class _RepoForkForm(formencode.Schema):
199 allow_extra_fields = True
200 allow_extra_fields = True
200 filter_extra_fields = False
201 filter_extra_fields = False
201 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
202 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
202 v.SlugifyName())
203 v.SlugifyName())
203 repo_group = v.OneOf(repo_groups, hideList=True)
204 repo_group = v.OneOf(repo_groups, hideList=True)
204 repo_type = All(v.ValidForkType(old_data), v.OneOf(supported_backends))
205 repo_type = All(v.ValidForkType(old_data), v.OneOf(supported_backends))
205 description = v.UnicodeString(strip=True, min=1, not_empty=True)
206 description = v.UnicodeString(strip=True, min=1, not_empty=True)
206 private = v.StringBoolean(if_missing=False)
207 private = v.StringBoolean(if_missing=False)
207 copy_permissions = v.StringBoolean(if_missing=False)
208 copy_permissions = v.StringBoolean(if_missing=False)
208 update_after_clone = v.StringBoolean(if_missing=False)
209 update_after_clone = v.StringBoolean(if_missing=False)
209 fork_parent_id = v.UnicodeString()
210 fork_parent_id = v.UnicodeString()
210 chained_validators = [v.ValidForkName(edit, old_data)]
211 chained_validators = [v.ValidForkName(edit, old_data)]
211 landing_rev = v.OneOf(landing_revs, hideList=True)
212 landing_rev = v.OneOf(landing_revs, hideList=True)
212
213
213 return _RepoForkForm
214 return _RepoForkForm
214
215
215
216
216 def RepoSettingsForm(edit=False, old_data={},
217 def RepoSettingsForm(edit=False, old_data={},
217 supported_backends=BACKENDS.keys(), repo_groups=[],
218 supported_backends=BACKENDS.keys(), repo_groups=[],
218 landing_revs=[]):
219 landing_revs=[]):
219 class _RepoForm(formencode.Schema):
220 class _RepoForm(formencode.Schema):
220 allow_extra_fields = True
221 allow_extra_fields = True
221 filter_extra_fields = False
222 filter_extra_fields = False
222 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
223 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
223 v.SlugifyName())
224 v.SlugifyName())
224 description = v.UnicodeString(strip=True, min=1, not_empty=True)
225 description = v.UnicodeString(strip=True, min=1, not_empty=True)
225 repo_group = v.OneOf(repo_groups, hideList=True)
226 repo_group = v.OneOf(repo_groups, hideList=True)
226 private = v.StringBoolean(if_missing=False)
227 private = v.StringBoolean(if_missing=False)
227 landing_rev = v.OneOf(landing_revs, hideList=True)
228 landing_rev = v.OneOf(landing_revs, hideList=True)
228 chained_validators = [v.ValidRepoName(edit, old_data), v.ValidPerms(),
229 chained_validators = [v.ValidRepoName(edit, old_data), v.ValidPerms(),
229 v.ValidSettings()]
230 v.ValidSettings()]
230 return _RepoForm
231 return _RepoForm
231
232
232
233
233 def ApplicationSettingsForm():
234 def ApplicationSettingsForm():
234 class _ApplicationSettingsForm(formencode.Schema):
235 class _ApplicationSettingsForm(formencode.Schema):
235 allow_extra_fields = True
236 allow_extra_fields = True
236 filter_extra_fields = False
237 filter_extra_fields = False
237 rhodecode_title = v.UnicodeString(strip=True, min=1, not_empty=True)
238 rhodecode_title = v.UnicodeString(strip=True, min=1, not_empty=True)
238 rhodecode_realm = v.UnicodeString(strip=True, min=1, not_empty=True)
239 rhodecode_realm = v.UnicodeString(strip=True, min=1, not_empty=True)
239 rhodecode_ga_code = v.UnicodeString(strip=True, min=1, not_empty=False)
240 rhodecode_ga_code = v.UnicodeString(strip=True, min=1, not_empty=False)
240
241
241 return _ApplicationSettingsForm
242 return _ApplicationSettingsForm
242
243
243
244
244 def ApplicationUiSettingsForm():
245 def ApplicationUiSettingsForm():
245 class _ApplicationUiSettingsForm(formencode.Schema):
246 class _ApplicationUiSettingsForm(formencode.Schema):
246 allow_extra_fields = True
247 allow_extra_fields = True
247 filter_extra_fields = False
248 filter_extra_fields = False
248 web_push_ssl = v.OneOf(['true', 'false'], if_missing='false')
249 web_push_ssl = v.OneOf(['true', 'false'], if_missing='false')
249 paths_root_path = All(
250 paths_root_path = All(
250 v.ValidPath(),
251 v.ValidPath(),
251 v.UnicodeString(strip=True, min=1, not_empty=True)
252 v.UnicodeString(strip=True, min=1, not_empty=True)
252 )
253 )
253 hooks_changegroup_update = v.OneOf(['True', 'False'],
254 hooks_changegroup_update = v.OneOf(['True', 'False'],
254 if_missing=False)
255 if_missing=False)
255 hooks_changegroup_repo_size = v.OneOf(['True', 'False'],
256 hooks_changegroup_repo_size = v.OneOf(['True', 'False'],
256 if_missing=False)
257 if_missing=False)
257 hooks_changegroup_push_logger = v.OneOf(['True', 'False'],
258 hooks_changegroup_push_logger = v.OneOf(['True', 'False'],
258 if_missing=False)
259 if_missing=False)
259 hooks_preoutgoing_pull_logger = v.OneOf(['True', 'False'],
260 hooks_preoutgoing_pull_logger = v.OneOf(['True', 'False'],
260 if_missing=False)
261 if_missing=False)
261
262
262 return _ApplicationUiSettingsForm
263 return _ApplicationUiSettingsForm
263
264
264
265
265 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
266 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
266 class _DefaultPermissionsForm(formencode.Schema):
267 class _DefaultPermissionsForm(formencode.Schema):
267 allow_extra_fields = True
268 allow_extra_fields = True
268 filter_extra_fields = True
269 filter_extra_fields = True
269 overwrite_default = v.StringBoolean(if_missing=False)
270 overwrite_default = v.StringBoolean(if_missing=False)
270 anonymous = v.OneOf(['True', 'False'], if_missing=False)
271 anonymous = v.OneOf(['True', 'False'], if_missing=False)
271 default_perm = v.OneOf(perms_choices)
272 default_perm = v.OneOf(perms_choices)
272 default_register = v.OneOf(register_choices)
273 default_register = v.OneOf(register_choices)
273 default_create = v.OneOf(create_choices)
274 default_create = v.OneOf(create_choices)
274
275
275 return _DefaultPermissionsForm
276 return _DefaultPermissionsForm
276
277
277
278
278 def LdapSettingsForm(tls_reqcert_choices, search_scope_choices,
279 def LdapSettingsForm(tls_reqcert_choices, search_scope_choices,
279 tls_kind_choices):
280 tls_kind_choices):
280 class _LdapSettingsForm(formencode.Schema):
281 class _LdapSettingsForm(formencode.Schema):
281 allow_extra_fields = True
282 allow_extra_fields = True
282 filter_extra_fields = True
283 filter_extra_fields = True
283 #pre_validators = [LdapLibValidator]
284 #pre_validators = [LdapLibValidator]
284 ldap_active = v.StringBoolean(if_missing=False)
285 ldap_active = v.StringBoolean(if_missing=False)
285 ldap_host = v.UnicodeString(strip=True,)
286 ldap_host = v.UnicodeString(strip=True,)
286 ldap_port = v.Number(strip=True,)
287 ldap_port = v.Number(strip=True,)
287 ldap_tls_kind = v.OneOf(tls_kind_choices)
288 ldap_tls_kind = v.OneOf(tls_kind_choices)
288 ldap_tls_reqcert = v.OneOf(tls_reqcert_choices)
289 ldap_tls_reqcert = v.OneOf(tls_reqcert_choices)
289 ldap_dn_user = v.UnicodeString(strip=True,)
290 ldap_dn_user = v.UnicodeString(strip=True,)
290 ldap_dn_pass = v.UnicodeString(strip=True,)
291 ldap_dn_pass = v.UnicodeString(strip=True,)
291 ldap_base_dn = v.UnicodeString(strip=True,)
292 ldap_base_dn = v.UnicodeString(strip=True,)
292 ldap_filter = v.UnicodeString(strip=True,)
293 ldap_filter = v.UnicodeString(strip=True,)
293 ldap_search_scope = v.OneOf(search_scope_choices)
294 ldap_search_scope = v.OneOf(search_scope_choices)
294 ldap_attr_login = All(
295 ldap_attr_login = All(
295 v.AttrLoginValidator(),
296 v.AttrLoginValidator(),
296 v.UnicodeString(strip=True,)
297 v.UnicodeString(strip=True,)
297 )
298 )
298 ldap_attr_firstname = v.UnicodeString(strip=True,)
299 ldap_attr_firstname = v.UnicodeString(strip=True,)
299 ldap_attr_lastname = v.UnicodeString(strip=True,)
300 ldap_attr_lastname = v.UnicodeString(strip=True,)
300 ldap_attr_email = v.UnicodeString(strip=True,)
301 ldap_attr_email = v.UnicodeString(strip=True,)
301
302
302 return _LdapSettingsForm
303 return _LdapSettingsForm
303
304
304
305
305 def UserExtraEmailForm():
306 def UserExtraEmailForm():
306 class _UserExtraEmailForm(formencode.Schema):
307 class _UserExtraEmailForm(formencode.Schema):
307 email = All(v.UniqSystemEmail(), v.Email)
308 email = All(v.UniqSystemEmail(), v.Email)
308
309
309 return _UserExtraEmailForm
310 return _UserExtraEmailForm
@@ -1,625 +1,630 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.user
3 rhodecode.model.user
4 ~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~
5
5
6 users model for RhodeCode
6 users model for RhodeCode
7
7
8 :created_on: Apr 9, 2010
8 :created_on: Apr 9, 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
28
29 from pylons import url
29 from pylons import url
30 from pylons.i18n.translation import _
30 from pylons.i18n.translation import _
31
31
32 from sqlalchemy.exc import DatabaseError
32 from sqlalchemy.exc import DatabaseError
33 from sqlalchemy.orm import joinedload
33 from sqlalchemy.orm import joinedload
34
34
35 from rhodecode.lib.utils2 import safe_unicode, generate_api_key
35 from rhodecode.lib.utils2 import safe_unicode, generate_api_key
36 from rhodecode.lib.caching_query import FromCache
36 from rhodecode.lib.caching_query import FromCache
37 from rhodecode.model import BaseModel
37 from rhodecode.model import BaseModel
38 from rhodecode.model.db import User, UserRepoToPerm, Repository, Permission, \
38 from rhodecode.model.db import User, UserRepoToPerm, Repository, Permission, \
39 UserToPerm, UsersGroupRepoToPerm, UsersGroupToPerm, UsersGroupMember, \
39 UserToPerm, UsersGroupRepoToPerm, UsersGroupToPerm, UsersGroupMember, \
40 Notification, RepoGroup, UserRepoGroupToPerm, UsersGroupRepoGroupToPerm, \
40 Notification, RepoGroup, UserRepoGroupToPerm, UsersGroupRepoGroupToPerm, \
41 UserEmailMap
41 UserEmailMap
42 from rhodecode.lib.exceptions import DefaultUserException, \
42 from rhodecode.lib.exceptions import DefaultUserException, \
43 UserOwnsReposException
43 UserOwnsReposException
44
44
45
45
46 log = logging.getLogger(__name__)
46 log = logging.getLogger(__name__)
47
47
48
48
49 PERM_WEIGHTS = {
49 PERM_WEIGHTS = {
50 'repository.none': 0,
50 'repository.none': 0,
51 'repository.read': 1,
51 'repository.read': 1,
52 'repository.write': 3,
52 'repository.write': 3,
53 'repository.admin': 4,
53 'repository.admin': 4,
54 'group.none': 0,
54 'group.none': 0,
55 'group.read': 1,
55 'group.read': 1,
56 'group.write': 3,
56 'group.write': 3,
57 'group.admin': 4,
57 'group.admin': 4,
58 }
58 }
59
59
60
60
61 class UserModel(BaseModel):
61 class UserModel(BaseModel):
62 cls = User
62 cls = User
63
63
64 def get(self, user_id, cache=False):
64 def get(self, user_id, cache=False):
65 user = self.sa.query(User)
65 user = self.sa.query(User)
66 if cache:
66 if cache:
67 user = user.options(FromCache("sql_cache_short",
67 user = user.options(FromCache("sql_cache_short",
68 "get_user_%s" % user_id))
68 "get_user_%s" % user_id))
69 return user.get(user_id)
69 return user.get(user_id)
70
70
71 def get_user(self, user):
71 def get_user(self, user):
72 return self._get_user(user)
72 return self._get_user(user)
73
73
74 def get_by_username(self, username, cache=False, case_insensitive=False):
74 def get_by_username(self, username, cache=False, case_insensitive=False):
75
75
76 if case_insensitive:
76 if case_insensitive:
77 user = self.sa.query(User).filter(User.username.ilike(username))
77 user = self.sa.query(User).filter(User.username.ilike(username))
78 else:
78 else:
79 user = self.sa.query(User)\
79 user = self.sa.query(User)\
80 .filter(User.username == username)
80 .filter(User.username == username)
81 if cache:
81 if cache:
82 user = user.options(FromCache("sql_cache_short",
82 user = user.options(FromCache("sql_cache_short",
83 "get_user_%s" % username))
83 "get_user_%s" % username))
84 return user.scalar()
84 return user.scalar()
85
85
86 def get_by_email(self, email, cache=False, case_insensitive=False):
86 def get_by_email(self, email, cache=False, case_insensitive=False):
87 return User.get_by_email(email, case_insensitive, cache)
87 return User.get_by_email(email, case_insensitive, cache)
88
88
89 def get_by_api_key(self, api_key, cache=False):
89 def get_by_api_key(self, api_key, cache=False):
90 return User.get_by_api_key(api_key, cache)
90 return User.get_by_api_key(api_key, cache)
91
91
92 def create(self, form_data):
92 def create(self, form_data):
93 from rhodecode.lib.auth import get_crypt_password
93 from rhodecode.lib.auth import get_crypt_password
94 try:
94 try:
95 new_user = User()
95 new_user = User()
96 for k, v in form_data.items():
96 for k, v in form_data.items():
97 if k == 'password':
97 if k == 'password':
98 v = get_crypt_password(v)
98 v = get_crypt_password(v)
99 if k == 'firstname':
100 k = 'name'
99 setattr(new_user, k, v)
101 setattr(new_user, k, v)
100
102
101 new_user.api_key = generate_api_key(form_data['username'])
103 new_user.api_key = generate_api_key(form_data['username'])
102 self.sa.add(new_user)
104 self.sa.add(new_user)
103 return new_user
105 return new_user
104 except:
106 except:
105 log.error(traceback.format_exc())
107 log.error(traceback.format_exc())
106 raise
108 raise
107
109
108 def create_or_update(self, username, password, email, firstname='',
110 def create_or_update(self, username, password, email, firstname='',
109 lastname='', active=True, admin=False, ldap_dn=None):
111 lastname='', active=True, admin=False, ldap_dn=None):
110 """
112 """
111 Creates a new instance if not found, or updates current one
113 Creates a new instance if not found, or updates current one
112
114
113 :param username:
115 :param username:
114 :param password:
116 :param password:
115 :param email:
117 :param email:
116 :param active:
118 :param active:
117 :param firstname:
119 :param firstname:
118 :param lastname:
120 :param lastname:
119 :param active:
121 :param active:
120 :param admin:
122 :param admin:
121 :param ldap_dn:
123 :param ldap_dn:
122 """
124 """
123
125
124 from rhodecode.lib.auth import get_crypt_password
126 from rhodecode.lib.auth import get_crypt_password
125
127
126 log.debug('Checking for %s account in RhodeCode database' % username)
128 log.debug('Checking for %s account in RhodeCode database' % username)
127 user = User.get_by_username(username, case_insensitive=True)
129 user = User.get_by_username(username, case_insensitive=True)
128 if user is None:
130 if user is None:
129 log.debug('creating new user %s' % username)
131 log.debug('creating new user %s' % username)
130 new_user = User()
132 new_user = User()
131 edit = False
133 edit = False
132 else:
134 else:
133 log.debug('updating user %s' % username)
135 log.debug('updating user %s' % username)
134 new_user = user
136 new_user = user
135 edit = True
137 edit = True
136
138
137 try:
139 try:
138 new_user.username = username
140 new_user.username = username
139 new_user.admin = admin
141 new_user.admin = admin
140 # set password only if creating an user or password is changed
142 # set password only if creating an user or password is changed
141 if edit is False or user.password != password:
143 if edit is False or user.password != password:
142 new_user.password = get_crypt_password(password)
144 new_user.password = get_crypt_password(password)
143 new_user.api_key = generate_api_key(username)
145 new_user.api_key = generate_api_key(username)
144 new_user.email = email
146 new_user.email = email
145 new_user.active = active
147 new_user.active = active
146 new_user.ldap_dn = safe_unicode(ldap_dn) if ldap_dn else None
148 new_user.ldap_dn = safe_unicode(ldap_dn) if ldap_dn else None
147 new_user.name = firstname
149 new_user.name = firstname
148 new_user.lastname = lastname
150 new_user.lastname = lastname
149 self.sa.add(new_user)
151 self.sa.add(new_user)
150 return new_user
152 return new_user
151 except (DatabaseError,):
153 except (DatabaseError,):
152 log.error(traceback.format_exc())
154 log.error(traceback.format_exc())
153 raise
155 raise
154
156
155 def create_for_container_auth(self, username, attrs):
157 def create_for_container_auth(self, username, attrs):
156 """
158 """
157 Creates the given user if it's not already in the database
159 Creates the given user if it's not already in the database
158
160
159 :param username:
161 :param username:
160 :param attrs:
162 :param attrs:
161 """
163 """
162 if self.get_by_username(username, case_insensitive=True) is None:
164 if self.get_by_username(username, case_insensitive=True) is None:
163
165
164 # autogenerate email for container account without one
166 # autogenerate email for container account without one
165 generate_email = lambda usr: '%s@container_auth.account' % usr
167 generate_email = lambda usr: '%s@container_auth.account' % usr
166
168
167 try:
169 try:
168 new_user = User()
170 new_user = User()
169 new_user.username = username
171 new_user.username = username
170 new_user.password = None
172 new_user.password = None
171 new_user.api_key = generate_api_key(username)
173 new_user.api_key = generate_api_key(username)
172 new_user.email = attrs['email']
174 new_user.email = attrs['email']
173 new_user.active = attrs.get('active', True)
175 new_user.active = attrs.get('active', True)
174 new_user.name = attrs['name'] or generate_email(username)
176 new_user.name = attrs['name'] or generate_email(username)
175 new_user.lastname = attrs['lastname']
177 new_user.lastname = attrs['lastname']
176
178
177 self.sa.add(new_user)
179 self.sa.add(new_user)
178 return new_user
180 return new_user
179 except (DatabaseError,):
181 except (DatabaseError,):
180 log.error(traceback.format_exc())
182 log.error(traceback.format_exc())
181 self.sa.rollback()
183 self.sa.rollback()
182 raise
184 raise
183 log.debug('User %s already exists. Skipping creation of account'
185 log.debug('User %s already exists. Skipping creation of account'
184 ' for container auth.', username)
186 ' for container auth.', username)
185 return None
187 return None
186
188
187 def create_ldap(self, username, password, user_dn, attrs):
189 def create_ldap(self, username, password, user_dn, attrs):
188 """
190 """
189 Checks if user is in database, if not creates this user marked
191 Checks if user is in database, if not creates this user marked
190 as ldap user
192 as ldap user
191
193
192 :param username:
194 :param username:
193 :param password:
195 :param password:
194 :param user_dn:
196 :param user_dn:
195 :param attrs:
197 :param attrs:
196 """
198 """
197 from rhodecode.lib.auth import get_crypt_password
199 from rhodecode.lib.auth import get_crypt_password
198 log.debug('Checking for such ldap account in RhodeCode database')
200 log.debug('Checking for such ldap account in RhodeCode database')
199 if self.get_by_username(username, case_insensitive=True) is None:
201 if self.get_by_username(username, case_insensitive=True) is None:
200
202
201 # autogenerate email for ldap account without one
203 # autogenerate email for ldap account without one
202 generate_email = lambda usr: '%s@ldap.account' % usr
204 generate_email = lambda usr: '%s@ldap.account' % usr
203
205
204 try:
206 try:
205 new_user = User()
207 new_user = User()
206 username = username.lower()
208 username = username.lower()
207 # add ldap account always lowercase
209 # add ldap account always lowercase
208 new_user.username = username
210 new_user.username = username
209 new_user.password = get_crypt_password(password)
211 new_user.password = get_crypt_password(password)
210 new_user.api_key = generate_api_key(username)
212 new_user.api_key = generate_api_key(username)
211 new_user.email = attrs['email'] or generate_email(username)
213 new_user.email = attrs['email'] or generate_email(username)
212 new_user.active = attrs.get('active', True)
214 new_user.active = attrs.get('active', True)
213 new_user.ldap_dn = safe_unicode(user_dn)
215 new_user.ldap_dn = safe_unicode(user_dn)
214 new_user.name = attrs['name']
216 new_user.name = attrs['name']
215 new_user.lastname = attrs['lastname']
217 new_user.lastname = attrs['lastname']
216
218
217 self.sa.add(new_user)
219 self.sa.add(new_user)
218 return new_user
220 return new_user
219 except (DatabaseError,):
221 except (DatabaseError,):
220 log.error(traceback.format_exc())
222 log.error(traceback.format_exc())
221 self.sa.rollback()
223 self.sa.rollback()
222 raise
224 raise
223 log.debug('this %s user exists skipping creation of ldap account',
225 log.debug('this %s user exists skipping creation of ldap account',
224 username)
226 username)
225 return None
227 return None
226
228
227 def create_registration(self, form_data):
229 def create_registration(self, form_data):
228 from rhodecode.model.notification import NotificationModel
230 from rhodecode.model.notification import NotificationModel
229
231
230 try:
232 try:
231 form_data['admin'] = False
233 form_data['admin'] = False
232 new_user = self.create(form_data)
234 new_user = self.create(form_data)
233
235
234 self.sa.add(new_user)
236 self.sa.add(new_user)
235 self.sa.flush()
237 self.sa.flush()
236
238
237 # notification to admins
239 # notification to admins
238 subject = _('new user registration')
240 subject = _('new user registration')
239 body = ('New user registration\n'
241 body = ('New user registration\n'
240 '---------------------\n'
242 '---------------------\n'
241 '- Username: %s\n'
243 '- Username: %s\n'
242 '- Full Name: %s\n'
244 '- Full Name: %s\n'
243 '- Email: %s\n')
245 '- Email: %s\n')
244 body = body % (new_user.username, new_user.full_name,
246 body = body % (new_user.username, new_user.full_name,
245 new_user.email)
247 new_user.email)
246 edit_url = url('edit_user', id=new_user.user_id, qualified=True)
248 edit_url = url('edit_user', id=new_user.user_id, qualified=True)
247 kw = {'registered_user_url': edit_url}
249 kw = {'registered_user_url': edit_url}
248 NotificationModel().create(created_by=new_user, subject=subject,
250 NotificationModel().create(created_by=new_user, subject=subject,
249 body=body, recipients=None,
251 body=body, recipients=None,
250 type_=Notification.TYPE_REGISTRATION,
252 type_=Notification.TYPE_REGISTRATION,
251 email_kwargs=kw)
253 email_kwargs=kw)
252
254
253 except:
255 except:
254 log.error(traceback.format_exc())
256 log.error(traceback.format_exc())
255 raise
257 raise
256
258
257 def update(self, user_id, form_data):
259 def update(self, user_id, form_data):
258 from rhodecode.lib.auth import get_crypt_password
260 from rhodecode.lib.auth import get_crypt_password
259 try:
261 try:
260 user = self.get(user_id, cache=False)
262 user = self.get(user_id, cache=False)
261 if user.username == 'default':
263 if user.username == 'default':
262 raise DefaultUserException(
264 raise DefaultUserException(
263 _("You can't Edit this user since it's"
265 _("You can't Edit this user since it's"
264 " crucial for entire application"))
266 " crucial for entire application"))
265
267
266 for k, v in form_data.items():
268 for k, v in form_data.items():
267 if k == 'new_password' and v != '':
269 if k == 'new_password' and v:
268 user.password = get_crypt_password(v)
270 user.password = get_crypt_password(v)
269 user.api_key = generate_api_key(user.username)
271 user.api_key = generate_api_key(user.username)
270 else:
272 else:
273 if k == 'firstname':
274 k = 'name'
271 setattr(user, k, v)
275 setattr(user, k, v)
272
273 self.sa.add(user)
276 self.sa.add(user)
274 except:
277 except:
275 log.error(traceback.format_exc())
278 log.error(traceback.format_exc())
276 raise
279 raise
277
280
278 def update_my_account(self, user_id, form_data):
281 def update_my_account(self, user_id, form_data):
279 from rhodecode.lib.auth import get_crypt_password
282 from rhodecode.lib.auth import get_crypt_password
280 try:
283 try:
281 user = self.get(user_id, cache=False)
284 user = self.get(user_id, cache=False)
282 if user.username == 'default':
285 if user.username == 'default':
283 raise DefaultUserException(
286 raise DefaultUserException(
284 _("You can't Edit this user since it's"
287 _("You can't Edit this user since it's"
285 " crucial for entire application")
288 " crucial for entire application")
286 )
289 )
287 for k, v in form_data.items():
290 for k, v in form_data.items():
288 if k == 'new_password' and v != '':
291 if k == 'new_password' and v:
289 user.password = get_crypt_password(v)
292 user.password = get_crypt_password(v)
290 user.api_key = generate_api_key(user.username)
293 user.api_key = generate_api_key(user.username)
291 else:
294 else:
295 if k == 'firstname':
296 k = 'name'
292 if k not in ['admin', 'active']:
297 if k not in ['admin', 'active']:
293 setattr(user, k, v)
298 setattr(user, k, v)
294
299
295 self.sa.add(user)
300 self.sa.add(user)
296 except:
301 except:
297 log.error(traceback.format_exc())
302 log.error(traceback.format_exc())
298 raise
303 raise
299
304
300 def delete(self, user):
305 def delete(self, user):
301 user = self._get_user(user)
306 user = self._get_user(user)
302
307
303 try:
308 try:
304 if user.username == 'default':
309 if user.username == 'default':
305 raise DefaultUserException(
310 raise DefaultUserException(
306 _(u"You can't remove this user since it's"
311 _(u"You can't remove this user since it's"
307 " crucial for entire application")
312 " crucial for entire application")
308 )
313 )
309 if user.repositories:
314 if user.repositories:
310 repos = [x.repo_name for x in user.repositories]
315 repos = [x.repo_name for x in user.repositories]
311 raise UserOwnsReposException(
316 raise UserOwnsReposException(
312 _(u'user "%s" still owns %s repositories and cannot be '
317 _(u'user "%s" still owns %s repositories and cannot be '
313 'removed. Switch owners or remove those repositories. %s')
318 'removed. Switch owners or remove those repositories. %s')
314 % (user.username, len(repos), ', '.join(repos))
319 % (user.username, len(repos), ', '.join(repos))
315 )
320 )
316 self.sa.delete(user)
321 self.sa.delete(user)
317 except:
322 except:
318 log.error(traceback.format_exc())
323 log.error(traceback.format_exc())
319 raise
324 raise
320
325
321 def reset_password_link(self, data):
326 def reset_password_link(self, data):
322 from rhodecode.lib.celerylib import tasks, run_task
327 from rhodecode.lib.celerylib import tasks, run_task
323 run_task(tasks.send_password_link, data['email'])
328 run_task(tasks.send_password_link, data['email'])
324
329
325 def reset_password(self, data):
330 def reset_password(self, data):
326 from rhodecode.lib.celerylib import tasks, run_task
331 from rhodecode.lib.celerylib import tasks, run_task
327 run_task(tasks.reset_user_password, data['email'])
332 run_task(tasks.reset_user_password, data['email'])
328
333
329 def fill_data(self, auth_user, user_id=None, api_key=None):
334 def fill_data(self, auth_user, user_id=None, api_key=None):
330 """
335 """
331 Fetches auth_user by user_id,or api_key if present.
336 Fetches auth_user by user_id,or api_key if present.
332 Fills auth_user attributes with those taken from database.
337 Fills auth_user attributes with those taken from database.
333 Additionally set's is_authenitated if lookup fails
338 Additionally set's is_authenitated if lookup fails
334 present in database
339 present in database
335
340
336 :param auth_user: instance of user to set attributes
341 :param auth_user: instance of user to set attributes
337 :param user_id: user id to fetch by
342 :param user_id: user id to fetch by
338 :param api_key: api key to fetch by
343 :param api_key: api key to fetch by
339 """
344 """
340 if user_id is None and api_key is None:
345 if user_id is None and api_key is None:
341 raise Exception('You need to pass user_id or api_key')
346 raise Exception('You need to pass user_id or api_key')
342
347
343 try:
348 try:
344 if api_key:
349 if api_key:
345 dbuser = self.get_by_api_key(api_key)
350 dbuser = self.get_by_api_key(api_key)
346 else:
351 else:
347 dbuser = self.get(user_id)
352 dbuser = self.get(user_id)
348
353
349 if dbuser is not None and dbuser.active:
354 if dbuser is not None and dbuser.active:
350 log.debug('filling %s data' % dbuser)
355 log.debug('filling %s data' % dbuser)
351 for k, v in dbuser.get_dict().items():
356 for k, v in dbuser.get_dict().items():
352 setattr(auth_user, k, v)
357 setattr(auth_user, k, v)
353 else:
358 else:
354 return False
359 return False
355
360
356 except:
361 except:
357 log.error(traceback.format_exc())
362 log.error(traceback.format_exc())
358 auth_user.is_authenticated = False
363 auth_user.is_authenticated = False
359 return False
364 return False
360
365
361 return True
366 return True
362
367
363 def fill_perms(self, user):
368 def fill_perms(self, user):
364 """
369 """
365 Fills user permission attribute with permissions taken from database
370 Fills user permission attribute with permissions taken from database
366 works for permissions given for repositories, and for permissions that
371 works for permissions given for repositories, and for permissions that
367 are granted to groups
372 are granted to groups
368
373
369 :param user: user instance to fill his perms
374 :param user: user instance to fill his perms
370 """
375 """
371 RK = 'repositories'
376 RK = 'repositories'
372 GK = 'repositories_groups'
377 GK = 'repositories_groups'
373 GLOBAL = 'global'
378 GLOBAL = 'global'
374 user.permissions[RK] = {}
379 user.permissions[RK] = {}
375 user.permissions[GK] = {}
380 user.permissions[GK] = {}
376 user.permissions[GLOBAL] = set()
381 user.permissions[GLOBAL] = set()
377
382
378 #======================================================================
383 #======================================================================
379 # fetch default permissions
384 # fetch default permissions
380 #======================================================================
385 #======================================================================
381 default_user = User.get_by_username('default', cache=True)
386 default_user = User.get_by_username('default', cache=True)
382 default_user_id = default_user.user_id
387 default_user_id = default_user.user_id
383
388
384 default_repo_perms = Permission.get_default_perms(default_user_id)
389 default_repo_perms = Permission.get_default_perms(default_user_id)
385 default_repo_groups_perms = Permission.get_default_group_perms(default_user_id)
390 default_repo_groups_perms = Permission.get_default_group_perms(default_user_id)
386
391
387 if user.is_admin:
392 if user.is_admin:
388 #==================================================================
393 #==================================================================
389 # admin user have all default rights for repositories
394 # admin user have all default rights for repositories
390 # and groups set to admin
395 # and groups set to admin
391 #==================================================================
396 #==================================================================
392 user.permissions[GLOBAL].add('hg.admin')
397 user.permissions[GLOBAL].add('hg.admin')
393
398
394 # repositories
399 # repositories
395 for perm in default_repo_perms:
400 for perm in default_repo_perms:
396 r_k = perm.UserRepoToPerm.repository.repo_name
401 r_k = perm.UserRepoToPerm.repository.repo_name
397 p = 'repository.admin'
402 p = 'repository.admin'
398 user.permissions[RK][r_k] = p
403 user.permissions[RK][r_k] = p
399
404
400 # repositories groups
405 # repositories groups
401 for perm in default_repo_groups_perms:
406 for perm in default_repo_groups_perms:
402 rg_k = perm.UserRepoGroupToPerm.group.group_name
407 rg_k = perm.UserRepoGroupToPerm.group.group_name
403 p = 'group.admin'
408 p = 'group.admin'
404 user.permissions[GK][rg_k] = p
409 user.permissions[GK][rg_k] = p
405 return user
410 return user
406
411
407 #==================================================================
412 #==================================================================
408 # set default permissions first for repositories and groups
413 # set default permissions first for repositories and groups
409 #==================================================================
414 #==================================================================
410 uid = user.user_id
415 uid = user.user_id
411
416
412 # default global permissions
417 # default global permissions
413 default_global_perms = self.sa.query(UserToPerm)\
418 default_global_perms = self.sa.query(UserToPerm)\
414 .filter(UserToPerm.user_id == default_user_id)
419 .filter(UserToPerm.user_id == default_user_id)
415
420
416 for perm in default_global_perms:
421 for perm in default_global_perms:
417 user.permissions[GLOBAL].add(perm.permission.permission_name)
422 user.permissions[GLOBAL].add(perm.permission.permission_name)
418
423
419 # defaults for repositories, taken from default user
424 # defaults for repositories, taken from default user
420 for perm in default_repo_perms:
425 for perm in default_repo_perms:
421 r_k = perm.UserRepoToPerm.repository.repo_name
426 r_k = perm.UserRepoToPerm.repository.repo_name
422 if perm.Repository.private and not (perm.Repository.user_id == uid):
427 if perm.Repository.private and not (perm.Repository.user_id == uid):
423 # disable defaults for private repos,
428 # disable defaults for private repos,
424 p = 'repository.none'
429 p = 'repository.none'
425 elif perm.Repository.user_id == uid:
430 elif perm.Repository.user_id == uid:
426 # set admin if owner
431 # set admin if owner
427 p = 'repository.admin'
432 p = 'repository.admin'
428 else:
433 else:
429 p = perm.Permission.permission_name
434 p = perm.Permission.permission_name
430
435
431 user.permissions[RK][r_k] = p
436 user.permissions[RK][r_k] = p
432
437
433 # defaults for repositories groups taken from default user permission
438 # defaults for repositories groups taken from default user permission
434 # on given group
439 # on given group
435 for perm in default_repo_groups_perms:
440 for perm in default_repo_groups_perms:
436 rg_k = perm.UserRepoGroupToPerm.group.group_name
441 rg_k = perm.UserRepoGroupToPerm.group.group_name
437 p = perm.Permission.permission_name
442 p = perm.Permission.permission_name
438 user.permissions[GK][rg_k] = p
443 user.permissions[GK][rg_k] = p
439
444
440 #==================================================================
445 #==================================================================
441 # overwrite defaults with user permissions if any found
446 # overwrite defaults with user permissions if any found
442 #==================================================================
447 #==================================================================
443
448
444 # user global permissions
449 # user global permissions
445 user_perms = self.sa.query(UserToPerm)\
450 user_perms = self.sa.query(UserToPerm)\
446 .options(joinedload(UserToPerm.permission))\
451 .options(joinedload(UserToPerm.permission))\
447 .filter(UserToPerm.user_id == uid).all()
452 .filter(UserToPerm.user_id == uid).all()
448
453
449 for perm in user_perms:
454 for perm in user_perms:
450 user.permissions[GLOBAL].add(perm.permission.permission_name)
455 user.permissions[GLOBAL].add(perm.permission.permission_name)
451
456
452 # user explicit permissions for repositories
457 # user explicit permissions for repositories
453 user_repo_perms = \
458 user_repo_perms = \
454 self.sa.query(UserRepoToPerm, Permission, Repository)\
459 self.sa.query(UserRepoToPerm, Permission, Repository)\
455 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
460 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
456 .join((Permission, UserRepoToPerm.permission_id == Permission.permission_id))\
461 .join((Permission, UserRepoToPerm.permission_id == Permission.permission_id))\
457 .filter(UserRepoToPerm.user_id == uid)\
462 .filter(UserRepoToPerm.user_id == uid)\
458 .all()
463 .all()
459
464
460 for perm in user_repo_perms:
465 for perm in user_repo_perms:
461 # set admin if owner
466 # set admin if owner
462 r_k = perm.UserRepoToPerm.repository.repo_name
467 r_k = perm.UserRepoToPerm.repository.repo_name
463 if perm.Repository.user_id == uid:
468 if perm.Repository.user_id == uid:
464 p = 'repository.admin'
469 p = 'repository.admin'
465 else:
470 else:
466 p = perm.Permission.permission_name
471 p = perm.Permission.permission_name
467 user.permissions[RK][r_k] = p
472 user.permissions[RK][r_k] = p
468
473
469 # USER GROUP
474 # USER GROUP
470 #==================================================================
475 #==================================================================
471 # check if user is part of user groups for this repository and
476 # check if user is part of user groups for this repository and
472 # fill in (or replace with higher) permissions
477 # fill in (or replace with higher) permissions
473 #==================================================================
478 #==================================================================
474
479
475 # users group global
480 # users group global
476 user_perms_from_users_groups = self.sa.query(UsersGroupToPerm)\
481 user_perms_from_users_groups = self.sa.query(UsersGroupToPerm)\
477 .options(joinedload(UsersGroupToPerm.permission))\
482 .options(joinedload(UsersGroupToPerm.permission))\
478 .join((UsersGroupMember, UsersGroupToPerm.users_group_id ==
483 .join((UsersGroupMember, UsersGroupToPerm.users_group_id ==
479 UsersGroupMember.users_group_id))\
484 UsersGroupMember.users_group_id))\
480 .filter(UsersGroupMember.user_id == uid).all()
485 .filter(UsersGroupMember.user_id == uid).all()
481
486
482 for perm in user_perms_from_users_groups:
487 for perm in user_perms_from_users_groups:
483 user.permissions[GLOBAL].add(perm.permission.permission_name)
488 user.permissions[GLOBAL].add(perm.permission.permission_name)
484
489
485 # users group for repositories permissions
490 # users group for repositories permissions
486 user_repo_perms_from_users_groups = \
491 user_repo_perms_from_users_groups = \
487 self.sa.query(UsersGroupRepoToPerm, Permission, Repository,)\
492 self.sa.query(UsersGroupRepoToPerm, Permission, Repository,)\
488 .join((Repository, UsersGroupRepoToPerm.repository_id == Repository.repo_id))\
493 .join((Repository, UsersGroupRepoToPerm.repository_id == Repository.repo_id))\
489 .join((Permission, UsersGroupRepoToPerm.permission_id == Permission.permission_id))\
494 .join((Permission, UsersGroupRepoToPerm.permission_id == Permission.permission_id))\
490 .join((UsersGroupMember, UsersGroupRepoToPerm.users_group_id == UsersGroupMember.users_group_id))\
495 .join((UsersGroupMember, UsersGroupRepoToPerm.users_group_id == UsersGroupMember.users_group_id))\
491 .filter(UsersGroupMember.user_id == uid)\
496 .filter(UsersGroupMember.user_id == uid)\
492 .all()
497 .all()
493
498
494 for perm in user_repo_perms_from_users_groups:
499 for perm in user_repo_perms_from_users_groups:
495 r_k = perm.UsersGroupRepoToPerm.repository.repo_name
500 r_k = perm.UsersGroupRepoToPerm.repository.repo_name
496 p = perm.Permission.permission_name
501 p = perm.Permission.permission_name
497 cur_perm = user.permissions[RK][r_k]
502 cur_perm = user.permissions[RK][r_k]
498 # overwrite permission only if it's greater than permission
503 # overwrite permission only if it's greater than permission
499 # given from other sources
504 # given from other sources
500 if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]:
505 if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]:
501 user.permissions[RK][r_k] = p
506 user.permissions[RK][r_k] = p
502
507
503 # REPO GROUP
508 # REPO GROUP
504 #==================================================================
509 #==================================================================
505 # get access for this user for repos group and override defaults
510 # get access for this user for repos group and override defaults
506 #==================================================================
511 #==================================================================
507
512
508 # user explicit permissions for repository
513 # user explicit permissions for repository
509 user_repo_groups_perms = \
514 user_repo_groups_perms = \
510 self.sa.query(UserRepoGroupToPerm, Permission, RepoGroup)\
515 self.sa.query(UserRepoGroupToPerm, Permission, RepoGroup)\
511 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
516 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
512 .join((Permission, UserRepoGroupToPerm.permission_id == Permission.permission_id))\
517 .join((Permission, UserRepoGroupToPerm.permission_id == Permission.permission_id))\
513 .filter(UserRepoGroupToPerm.user_id == uid)\
518 .filter(UserRepoGroupToPerm.user_id == uid)\
514 .all()
519 .all()
515
520
516 for perm in user_repo_groups_perms:
521 for perm in user_repo_groups_perms:
517 rg_k = perm.UserRepoGroupToPerm.group.group_name
522 rg_k = perm.UserRepoGroupToPerm.group.group_name
518 p = perm.Permission.permission_name
523 p = perm.Permission.permission_name
519 cur_perm = user.permissions[GK][rg_k]
524 cur_perm = user.permissions[GK][rg_k]
520 if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]:
525 if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]:
521 user.permissions[GK][rg_k] = p
526 user.permissions[GK][rg_k] = p
522
527
523 # REPO GROUP + USER GROUP
528 # REPO GROUP + USER GROUP
524 #==================================================================
529 #==================================================================
525 # check if user is part of user groups for this repo group and
530 # check if user is part of user groups for this repo group and
526 # fill in (or replace with higher) permissions
531 # fill in (or replace with higher) permissions
527 #==================================================================
532 #==================================================================
528
533
529 # users group for repositories permissions
534 # users group for repositories permissions
530 user_repo_group_perms_from_users_groups = \
535 user_repo_group_perms_from_users_groups = \
531 self.sa.query(UsersGroupRepoGroupToPerm, Permission, RepoGroup)\
536 self.sa.query(UsersGroupRepoGroupToPerm, Permission, RepoGroup)\
532 .join((RepoGroup, UsersGroupRepoGroupToPerm.group_id == RepoGroup.group_id))\
537 .join((RepoGroup, UsersGroupRepoGroupToPerm.group_id == RepoGroup.group_id))\
533 .join((Permission, UsersGroupRepoGroupToPerm.permission_id == Permission.permission_id))\
538 .join((Permission, UsersGroupRepoGroupToPerm.permission_id == Permission.permission_id))\
534 .join((UsersGroupMember, UsersGroupRepoGroupToPerm.users_group_id == UsersGroupMember.users_group_id))\
539 .join((UsersGroupMember, UsersGroupRepoGroupToPerm.users_group_id == UsersGroupMember.users_group_id))\
535 .filter(UsersGroupMember.user_id == uid)\
540 .filter(UsersGroupMember.user_id == uid)\
536 .all()
541 .all()
537
542
538 for perm in user_repo_group_perms_from_users_groups:
543 for perm in user_repo_group_perms_from_users_groups:
539 g_k = perm.UsersGroupRepoGroupToPerm.group.group_name
544 g_k = perm.UsersGroupRepoGroupToPerm.group.group_name
540 p = perm.Permission.permission_name
545 p = perm.Permission.permission_name
541 cur_perm = user.permissions[GK][g_k]
546 cur_perm = user.permissions[GK][g_k]
542 # overwrite permission only if it's greater than permission
547 # overwrite permission only if it's greater than permission
543 # given from other sources
548 # given from other sources
544 if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]:
549 if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]:
545 user.permissions[GK][g_k] = p
550 user.permissions[GK][g_k] = p
546
551
547 return user
552 return user
548
553
549 def has_perm(self, user, perm):
554 def has_perm(self, user, perm):
550 if not isinstance(perm, Permission):
555 if not isinstance(perm, Permission):
551 raise Exception('perm needs to be an instance of Permission class '
556 raise Exception('perm needs to be an instance of Permission class '
552 'got %s instead' % type(perm))
557 'got %s instead' % type(perm))
553
558
554 user = self._get_user(user)
559 user = self._get_user(user)
555
560
556 return UserToPerm.query().filter(UserToPerm.user == user)\
561 return UserToPerm.query().filter(UserToPerm.user == user)\
557 .filter(UserToPerm.permission == perm).scalar() is not None
562 .filter(UserToPerm.permission == perm).scalar() is not None
558
563
559 def grant_perm(self, user, perm):
564 def grant_perm(self, user, perm):
560 """
565 """
561 Grant user global permissions
566 Grant user global permissions
562
567
563 :param user:
568 :param user:
564 :param perm:
569 :param perm:
565 """
570 """
566 user = self._get_user(user)
571 user = self._get_user(user)
567 perm = self._get_perm(perm)
572 perm = self._get_perm(perm)
568 # if this permission is already granted skip it
573 # if this permission is already granted skip it
569 _perm = UserToPerm.query()\
574 _perm = UserToPerm.query()\
570 .filter(UserToPerm.user == user)\
575 .filter(UserToPerm.user == user)\
571 .filter(UserToPerm.permission == perm)\
576 .filter(UserToPerm.permission == perm)\
572 .scalar()
577 .scalar()
573 if _perm:
578 if _perm:
574 return
579 return
575 new = UserToPerm()
580 new = UserToPerm()
576 new.user = user
581 new.user = user
577 new.permission = perm
582 new.permission = perm
578 self.sa.add(new)
583 self.sa.add(new)
579
584
580 def revoke_perm(self, user, perm):
585 def revoke_perm(self, user, perm):
581 """
586 """
582 Revoke users global permissions
587 Revoke users global permissions
583
588
584 :param user:
589 :param user:
585 :param perm:
590 :param perm:
586 """
591 """
587 user = self._get_user(user)
592 user = self._get_user(user)
588 perm = self._get_perm(perm)
593 perm = self._get_perm(perm)
589
594
590 obj = UserToPerm.query()\
595 obj = UserToPerm.query()\
591 .filter(UserToPerm.user == user)\
596 .filter(UserToPerm.user == user)\
592 .filter(UserToPerm.permission == perm)\
597 .filter(UserToPerm.permission == perm)\
593 .scalar()
598 .scalar()
594 if obj:
599 if obj:
595 self.sa.delete(obj)
600 self.sa.delete(obj)
596
601
597 def add_extra_email(self, user, email):
602 def add_extra_email(self, user, email):
598 """
603 """
599 Adds email address to UserEmailMap
604 Adds email address to UserEmailMap
600
605
601 :param user:
606 :param user:
602 :param email:
607 :param email:
603 """
608 """
604 from rhodecode.model import forms
609 from rhodecode.model import forms
605 form = forms.UserExtraEmailForm()()
610 form = forms.UserExtraEmailForm()()
606 data = form.to_python(dict(email=email))
611 data = form.to_python(dict(email=email))
607 user = self._get_user(user)
612 user = self._get_user(user)
608
613
609 obj = UserEmailMap()
614 obj = UserEmailMap()
610 obj.user = user
615 obj.user = user
611 obj.email = data['email']
616 obj.email = data['email']
612 self.sa.add(obj)
617 self.sa.add(obj)
613 return obj
618 return obj
614
619
615 def delete_extra_email(self, user, email_id):
620 def delete_extra_email(self, user, email_id):
616 """
621 """
617 Removes email address from UserEmailMap
622 Removes email address from UserEmailMap
618
623
619 :param user:
624 :param user:
620 :param email_id:
625 :param email_id:
621 """
626 """
622 user = self._get_user(user)
627 user = self._get_user(user)
623 obj = UserEmailMap.query().get(email_id)
628 obj = UserEmailMap.query().get(email_id)
624 if obj:
629 if obj:
625 self.sa.delete(obj)
630 self.sa.delete(obj)
@@ -1,251 +1,251 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Edit user')} ${c.user.username} - ${c.rhodecode_name}
5 ${_('Edit user')} ${c.user.username} - ${c.rhodecode_name}
6 </%def>
6 </%def>
7
7
8 <%def name="breadcrumbs_links()">
8 <%def name="breadcrumbs_links()">
9 ${h.link_to(_('Admin'),h.url('admin_home'))}
9 ${h.link_to(_('Admin'),h.url('admin_home'))}
10 &raquo;
10 &raquo;
11 ${h.link_to(_('Users'),h.url('users'))}
11 ${h.link_to(_('Users'),h.url('users'))}
12 &raquo;
12 &raquo;
13 ${_('edit')} "${c.user.username}"
13 ${_('edit')} "${c.user.username}"
14 </%def>
14 </%def>
15
15
16 <%def name="page_nav()">
16 <%def name="page_nav()">
17 ${self.menu('admin')}
17 ${self.menu('admin')}
18 </%def>
18 </%def>
19
19
20 <%def name="main()">
20 <%def name="main()">
21 <div class="box box-left">
21 <div class="box box-left">
22 <!-- box / title -->
22 <!-- box / title -->
23 <div class="title">
23 <div class="title">
24 ${self.breadcrumbs()}
24 ${self.breadcrumbs()}
25 </div>
25 </div>
26 <!-- end box / title -->
26 <!-- end box / title -->
27 ${h.form(url('update_user', id=c.user.user_id),method='put')}
27 ${h.form(url('update_user', id=c.user.user_id),method='put')}
28 <div class="form">
28 <div class="form">
29 <div class="field">
29 <div class="field">
30 <div class="gravatar_box">
30 <div class="gravatar_box">
31 <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(c.user.email)}"/></div>
31 <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(c.user.email)}"/></div>
32 <p>
32 <p>
33 %if c.use_gravatar:
33 %if c.use_gravatar:
34 <strong>${_('Change your avatar at')} <a href="http://gravatar.com">gravatar.com</a></strong>
34 <strong>${_('Change your avatar at')} <a href="http://gravatar.com">gravatar.com</a></strong>
35 <br/>${_('Using')} ${c.user.email}
35 <br/>${_('Using')} ${c.user.email}
36 %else:
36 %else:
37 <br/>${c.user.email}
37 <br/>${c.user.email}
38 %endif
38 %endif
39 </div>
39 </div>
40 </div>
40 </div>
41 <div class="field">
41 <div class="field">
42 <div class="label">
42 <div class="label">
43 <label>${_('API key')}</label> ${c.user.api_key}
43 <label>${_('API key')}</label> ${c.user.api_key}
44 </div>
44 </div>
45 </div>
45 </div>
46
46
47 <div class="fields">
47 <div class="fields">
48 <div class="field">
48 <div class="field">
49 <div class="label">
49 <div class="label">
50 <label for="username">${_('Username')}:</label>
50 <label for="username">${_('Username')}:</label>
51 </div>
51 </div>
52 <div class="input">
52 <div class="input">
53 ${h.text('username',class_='medium')}
53 ${h.text('username',class_='medium')}
54 </div>
54 </div>
55 </div>
55 </div>
56
56
57 <div class="field">
57 <div class="field">
58 <div class="label">
58 <div class="label">
59 <label for="ldap_dn">${_('LDAP DN')}:</label>
59 <label for="ldap_dn">${_('LDAP DN')}:</label>
60 </div>
60 </div>
61 <div class="input">
61 <div class="input">
62 ${h.text('ldap_dn',class_='medium disabled',readonly="readonly")}
62 ${h.text('ldap_dn',class_='medium disabled',readonly="readonly")}
63 </div>
63 </div>
64 </div>
64 </div>
65
65
66 <div class="field">
66 <div class="field">
67 <div class="label">
67 <div class="label">
68 <label for="new_password">${_('New password')}:</label>
68 <label for="new_password">${_('New password')}:</label>
69 </div>
69 </div>
70 <div class="input">
70 <div class="input">
71 ${h.password('new_password',class_='medium',autocomplete="off")}
71 ${h.password('new_password',class_='medium',autocomplete="off")}
72 </div>
72 </div>
73 </div>
73 </div>
74
74
75 <div class="field">
75 <div class="field">
76 <div class="label">
76 <div class="label">
77 <label for="password_confirmation">${_('New password confirmation')}:</label>
77 <label for="password_confirmation">${_('New password confirmation')}:</label>
78 </div>
78 </div>
79 <div class="input">
79 <div class="input">
80 ${h.password('password_confirmation',class_="medium",autocomplete="off")}
80 ${h.password('password_confirmation',class_="medium",autocomplete="off")}
81 </div>
81 </div>
82 </div>
82 </div>
83
83
84 <div class="field">
84 <div class="field">
85 <div class="label">
85 <div class="label">
86 <label for="name">${_('First Name')}:</label>
86 <label for="firstname">${_('First Name')}:</label>
87 </div>
87 </div>
88 <div class="input">
88 <div class="input">
89 ${h.text('name',class_='medium')}
89 ${h.text('firstname',class_='medium')}
90 </div>
90 </div>
91 </div>
91 </div>
92
92
93 <div class="field">
93 <div class="field">
94 <div class="label">
94 <div class="label">
95 <label for="lastname">${_('Last Name')}:</label>
95 <label for="lastname">${_('Last Name')}:</label>
96 </div>
96 </div>
97 <div class="input">
97 <div class="input">
98 ${h.text('lastname',class_='medium')}
98 ${h.text('lastname',class_='medium')}
99 </div>
99 </div>
100 </div>
100 </div>
101
101
102 <div class="field">
102 <div class="field">
103 <div class="label">
103 <div class="label">
104 <label for="email">${_('Email')}:</label>
104 <label for="email">${_('Email')}:</label>
105 </div>
105 </div>
106 <div class="input">
106 <div class="input">
107 ${h.text('email',class_='medium')}
107 ${h.text('email',class_='medium')}
108 </div>
108 </div>
109 </div>
109 </div>
110
110
111 <div class="field">
111 <div class="field">
112 <div class="label label-checkbox">
112 <div class="label label-checkbox">
113 <label for="active">${_('Active')}:</label>
113 <label for="active">${_('Active')}:</label>
114 </div>
114 </div>
115 <div class="checkboxes">
115 <div class="checkboxes">
116 ${h.checkbox('active',value=True)}
116 ${h.checkbox('active',value=True)}
117 </div>
117 </div>
118 </div>
118 </div>
119
119
120 <div class="field">
120 <div class="field">
121 <div class="label label-checkbox">
121 <div class="label label-checkbox">
122 <label for="admin">${_('Admin')}:</label>
122 <label for="admin">${_('Admin')}:</label>
123 </div>
123 </div>
124 <div class="checkboxes">
124 <div class="checkboxes">
125 ${h.checkbox('admin',value=True)}
125 ${h.checkbox('admin',value=True)}
126 </div>
126 </div>
127 </div>
127 </div>
128 <div class="buttons">
128 <div class="buttons">
129 ${h.submit('save',_('Save'),class_="ui-button")}
129 ${h.submit('save',_('Save'),class_="ui-button")}
130 ${h.reset('reset',_('Reset'),class_="ui-button")}
130 ${h.reset('reset',_('Reset'),class_="ui-button")}
131 </div>
131 </div>
132 </div>
132 </div>
133 </div>
133 </div>
134 ${h.end_form()}
134 ${h.end_form()}
135 </div>
135 </div>
136 <div class="box box-right">
136 <div class="box box-right">
137 <!-- box / title -->
137 <!-- box / title -->
138 <div class="title">
138 <div class="title">
139 <h5>${_('Permissions')}</h5>
139 <h5>${_('Permissions')}</h5>
140 </div>
140 </div>
141 ${h.form(url('user_perm', id=c.user.user_id),method='put')}
141 ${h.form(url('user_perm', id=c.user.user_id),method='put')}
142 <div class="form">
142 <div class="form">
143 <!-- fields -->
143 <!-- fields -->
144 <div class="fields">
144 <div class="fields">
145 <div class="field">
145 <div class="field">
146 <div class="label label-checkbox">
146 <div class="label label-checkbox">
147 <label for="create_repo_perm">${_('Create repositories')}:</label>
147 <label for="create_repo_perm">${_('Create repositories')}:</label>
148 </div>
148 </div>
149 <div class="checkboxes">
149 <div class="checkboxes">
150 ${h.checkbox('create_repo_perm',value=True)}
150 ${h.checkbox('create_repo_perm',value=True)}
151 </div>
151 </div>
152 </div>
152 </div>
153 <div class="buttons">
153 <div class="buttons">
154 ${h.submit('save',_('Save'),class_="ui-button")}
154 ${h.submit('save',_('Save'),class_="ui-button")}
155 ${h.reset('reset',_('Reset'),class_="ui-button")}
155 ${h.reset('reset',_('Reset'),class_="ui-button")}
156 </div>
156 </div>
157 </div>
157 </div>
158 </div>
158 </div>
159 ${h.end_form()}
159 ${h.end_form()}
160
160
161 ## permissions overview
161 ## permissions overview
162 <div id="perms" class="table">
162 <div id="perms" class="table">
163 %for section in sorted(c.perm_user.permissions.keys()):
163 %for section in sorted(c.perm_user.permissions.keys()):
164 <div class="perms_section_head">${section.replace("_"," ").capitalize()}</div>
164 <div class="perms_section_head">${section.replace("_"," ").capitalize()}</div>
165
165
166 <div id='tbl_list_wrap_${section}' class="yui-skin-sam">
166 <div id='tbl_list_wrap_${section}' class="yui-skin-sam">
167 <table id="tbl_list_${section}">
167 <table id="tbl_list_${section}">
168 <thead>
168 <thead>
169 <tr>
169 <tr>
170 <th class="left">${_('Name')}</th>
170 <th class="left">${_('Name')}</th>
171 <th class="left">${_('Permission')}</th>
171 <th class="left">${_('Permission')}</th>
172 </thead>
172 </thead>
173 <tbody>
173 <tbody>
174 %for k in c.perm_user.permissions[section]:
174 %for k in c.perm_user.permissions[section]:
175 <%
175 <%
176 if section != 'global':
176 if section != 'global':
177 section_perm = c.perm_user.permissions[section].get(k)
177 section_perm = c.perm_user.permissions[section].get(k)
178 _perm = section_perm.split('.')[-1]
178 _perm = section_perm.split('.')[-1]
179 else:
179 else:
180 _perm = section_perm = None
180 _perm = section_perm = None
181 %>
181 %>
182 <tr>
182 <tr>
183 <td>
183 <td>
184 %if section == 'repositories':
184 %if section == 'repositories':
185 <a href="${h.url('summary_home',repo_name=k)}">${k}</a>
185 <a href="${h.url('summary_home',repo_name=k)}">${k}</a>
186 %elif section == 'repositories_groups':
186 %elif section == 'repositories_groups':
187 <a href="${h.url('repos_group_home',group_name=k)}">${k}</a>
187 <a href="${h.url('repos_group_home',group_name=k)}">${k}</a>
188 %else:
188 %else:
189 ${h.get_permission_name(k)}
189 ${h.get_permission_name(k)}
190 %endif
190 %endif
191 </td>
191 </td>
192 <td>
192 <td>
193 %if section == 'global':
193 %if section == 'global':
194 ${h.bool2icon(k.split('.')[-1] != 'none')}
194 ${h.bool2icon(k.split('.')[-1] != 'none')}
195 %else:
195 %else:
196 <span class="perm_tag ${_perm}">${section_perm}</span>
196 <span class="perm_tag ${_perm}">${section_perm}</span>
197 %endif
197 %endif
198 </td>
198 </td>
199 </tr>
199 </tr>
200 %endfor
200 %endfor
201 </tbody>
201 </tbody>
202 </table>
202 </table>
203 </div>
203 </div>
204 %endfor
204 %endfor
205 </div>
205 </div>
206 </div>
206 </div>
207 <div class="box box-left">
207 <div class="box box-left">
208 <!-- box / title -->
208 <!-- box / title -->
209 <div class="title">
209 <div class="title">
210 <h5>${_('Email addresses')}</h5>
210 <h5>${_('Email addresses')}</h5>
211 </div>
211 </div>
212
212
213 <div class="emails_wrap">
213 <div class="emails_wrap">
214 <table class="noborder">
214 <table class="noborder">
215 %for em in c.user_email_map:
215 %for em in c.user_email_map:
216 <tr>
216 <tr>
217 <td><div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(em.user.email,16)}"/> </div></td>
217 <td><div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(em.user.email,16)}"/> </div></td>
218 <td><div class="email">${em.email}</div></td>
218 <td><div class="email">${em.email}</div></td>
219 <td>
219 <td>
220 ${h.form(url('user_emails_delete', id=c.user.user_id),method='delete')}
220 ${h.form(url('user_emails_delete', id=c.user.user_id),method='delete')}
221 ${h.hidden('del_email',em.email_id)}
221 ${h.hidden('del_email',em.email_id)}
222 ${h.submit('remove_',_('delete'),id="remove_email_%s" % em.email_id,
222 ${h.submit('remove_',_('delete'),id="remove_email_%s" % em.email_id,
223 class_="delete_icon action_button", onclick="return confirm('"+_('Confirm to delete this email: %s') % em.email+"');")}
223 class_="delete_icon action_button", onclick="return confirm('"+_('Confirm to delete this email: %s') % em.email+"');")}
224 ${h.end_form()}
224 ${h.end_form()}
225 </td>
225 </td>
226 </tr>
226 </tr>
227 %endfor
227 %endfor
228 </table>
228 </table>
229 </div>
229 </div>
230
230
231 ${h.form(url('user_emails', id=c.user.user_id),method='put')}
231 ${h.form(url('user_emails', id=c.user.user_id),method='put')}
232 <div class="form">
232 <div class="form">
233 <!-- fields -->
233 <!-- fields -->
234 <div class="fields">
234 <div class="fields">
235 <div class="field">
235 <div class="field">
236 <div class="label">
236 <div class="label">
237 <label for="email">${_('New email address')}:</label>
237 <label for="email">${_('New email address')}:</label>
238 </div>
238 </div>
239 <div class="input">
239 <div class="input">
240 ${h.text('new_email', class_='medium')}
240 ${h.text('new_email', class_='medium')}
241 </div>
241 </div>
242 </div>
242 </div>
243 <div class="buttons">
243 <div class="buttons">
244 ${h.submit('save',_('Add'),class_="ui-button")}
244 ${h.submit('save',_('Add'),class_="ui-button")}
245 ${h.reset('reset',_('Reset'),class_="ui-button")}
245 ${h.reset('reset',_('Reset'),class_="ui-button")}
246 </div>
246 </div>
247 </div>
247 </div>
248 </div>
248 </div>
249 ${h.end_form()}
249 ${h.end_form()}
250 </div>
250 </div>
251 </%def>
251 </%def>
@@ -1,85 +1,85 b''
1 <div>
1 <div>
2 ${h.form(url('admin_settings_my_account_update'),method='put')}
2 ${h.form(url('admin_settings_my_account_update'),method='put')}
3 <div class="form">
3 <div class="form">
4
4
5 <div class="field">
5 <div class="field">
6 <div class="gravatar_box">
6 <div class="gravatar_box">
7 <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(c.user.email)}"/></div>
7 <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(c.user.email)}"/></div>
8 <p>
8 <p>
9 %if c.use_gravatar:
9 %if c.use_gravatar:
10 <strong>${_('Change your avatar at')} <a href="http://gravatar.com">gravatar.com</a></strong>
10 <strong>${_('Change your avatar at')} <a href="http://gravatar.com">gravatar.com</a></strong>
11 <br/>${_('Using')} ${c.user.email}
11 <br/>${_('Using')} ${c.user.email}
12 %else:
12 %else:
13 <br/>${c.user.email}
13 <br/>${c.user.email}
14 %endif
14 %endif
15 </p>
15 </p>
16 </div>
16 </div>
17 </div>
17 </div>
18 <div class="field">
18 <div class="field">
19 <div class="label">
19 <div class="label">
20 <label>${_('API key')}</label> ${c.user.api_key}
20 <label>${_('API key')}</label> ${c.user.api_key}
21 </div>
21 </div>
22 </div>
22 </div>
23 <div class="fields">
23 <div class="fields">
24 <div class="field">
24 <div class="field">
25 <div class="label">
25 <div class="label">
26 <label for="username">${_('Username')}:</label>
26 <label for="username">${_('Username')}:</label>
27 </div>
27 </div>
28 <div class="input">
28 <div class="input">
29 ${h.text('username',class_="medium")}
29 ${h.text('username',class_="medium")}
30 </div>
30 </div>
31 </div>
31 </div>
32
32
33 <div class="field">
33 <div class="field">
34 <div class="label">
34 <div class="label">
35 <label for="new_password">${_('New password')}:</label>
35 <label for="new_password">${_('New password')}:</label>
36 </div>
36 </div>
37 <div class="input">
37 <div class="input">
38 ${h.password('new_password',class_="medium",autocomplete="off")}
38 ${h.password('new_password',class_="medium",autocomplete="off")}
39 </div>
39 </div>
40 </div>
40 </div>
41
41
42 <div class="field">
42 <div class="field">
43 <div class="label">
43 <div class="label">
44 <label for="password_confirmation">${_('New password confirmation')}:</label>
44 <label for="password_confirmation">${_('New password confirmation')}:</label>
45 </div>
45 </div>
46 <div class="input">
46 <div class="input">
47 ${h.password('password_confirmation',class_="medium",autocomplete="off")}
47 ${h.password('password_confirmation',class_="medium",autocomplete="off")}
48 </div>
48 </div>
49 </div>
49 </div>
50
50
51 <div class="field">
51 <div class="field">
52 <div class="label">
52 <div class="label">
53 <label for="name">${_('First Name')}:</label>
53 <label for="name">${_('First Name')}:</label>
54 </div>
54 </div>
55 <div class="input">
55 <div class="input">
56 ${h.text('name',class_="medium")}
56 ${h.text('firstname',class_="medium")}
57 </div>
57 </div>
58 </div>
58 </div>
59
59
60 <div class="field">
60 <div class="field">
61 <div class="label">
61 <div class="label">
62 <label for="lastname">${_('Last Name')}:</label>
62 <label for="lastname">${_('Last Name')}:</label>
63 </div>
63 </div>
64 <div class="input">
64 <div class="input">
65 ${h.text('lastname',class_="medium")}
65 ${h.text('lastname',class_="medium")}
66 </div>
66 </div>
67 </div>
67 </div>
68
68
69 <div class="field">
69 <div class="field">
70 <div class="label">
70 <div class="label">
71 <label for="email">${_('Email')}:</label>
71 <label for="email">${_('Email')}:</label>
72 </div>
72 </div>
73 <div class="input">
73 <div class="input">
74 ${h.text('email',class_="medium")}
74 ${h.text('email',class_="medium")}
75 </div>
75 </div>
76 </div>
76 </div>
77
77
78 <div class="buttons">
78 <div class="buttons">
79 ${h.submit('save',_('Save'),class_="ui-button")}
79 ${h.submit('save',_('Save'),class_="ui-button")}
80 ${h.reset('reset',_('Reset'),class_="ui-button")}
80 ${h.reset('reset',_('Reset'),class_="ui-button")}
81 </div>
81 </div>
82 </div>
82 </div>
83 </div>
83 </div>
84 ${h.end_form()}
84 ${h.end_form()}
85 </div>
85 </div>
@@ -1,215 +1,213 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 from rhodecode.lib.auth import get_crypt_password, check_password
3 from rhodecode.lib.auth import get_crypt_password, check_password
4 from rhodecode.model.db import User, RhodeCodeSetting
4 from rhodecode.model.db import User, RhodeCodeSetting
5 from rhodecode.tests import *
5 from rhodecode.tests import *
6 from rhodecode.lib import helpers as h
6 from rhodecode.lib import helpers as h
7 from rhodecode.model.user import UserModel
7
8
8
9
9 class TestAdminSettingsController(TestController):
10 class TestAdminSettingsController(TestController):
10
11
11 def test_index(self):
12 def test_index(self):
12 response = self.app.get(url('admin_settings'))
13 response = self.app.get(url('admin_settings'))
13 # Test response...
14 # Test response...
14
15
15 def test_index_as_xml(self):
16 def test_index_as_xml(self):
16 response = self.app.get(url('formatted_admin_settings', format='xml'))
17 response = self.app.get(url('formatted_admin_settings', format='xml'))
17
18
18 def test_create(self):
19 def test_create(self):
19 response = self.app.post(url('admin_settings'))
20 response = self.app.post(url('admin_settings'))
20
21
21 def test_new(self):
22 def test_new(self):
22 response = self.app.get(url('admin_new_setting'))
23 response = self.app.get(url('admin_new_setting'))
23
24
24 def test_new_as_xml(self):
25 def test_new_as_xml(self):
25 response = self.app.get(url('formatted_admin_new_setting', format='xml'))
26 response = self.app.get(url('formatted_admin_new_setting', format='xml'))
26
27
27 def test_update(self):
28 def test_update(self):
28 response = self.app.put(url('admin_setting', setting_id=1))
29 response = self.app.put(url('admin_setting', setting_id=1))
29
30
30 def test_update_browser_fakeout(self):
31 def test_update_browser_fakeout(self):
31 response = self.app.post(url('admin_setting', setting_id=1), params=dict(_method='put'))
32 response = self.app.post(url('admin_setting', setting_id=1), params=dict(_method='put'))
32
33
33 def test_delete(self):
34 def test_delete(self):
34 response = self.app.delete(url('admin_setting', setting_id=1))
35 response = self.app.delete(url('admin_setting', setting_id=1))
35
36
36 def test_delete_browser_fakeout(self):
37 def test_delete_browser_fakeout(self):
37 response = self.app.post(url('admin_setting', setting_id=1), params=dict(_method='delete'))
38 response = self.app.post(url('admin_setting', setting_id=1), params=dict(_method='delete'))
38
39
39 def test_show(self):
40 def test_show(self):
40 response = self.app.get(url('admin_setting', setting_id=1))
41 response = self.app.get(url('admin_setting', setting_id=1))
41
42
42 def test_show_as_xml(self):
43 def test_show_as_xml(self):
43 response = self.app.get(url('formatted_admin_setting', setting_id=1, format='xml'))
44 response = self.app.get(url('formatted_admin_setting', setting_id=1, format='xml'))
44
45
45 def test_edit(self):
46 def test_edit(self):
46 response = self.app.get(url('admin_edit_setting', setting_id=1))
47 response = self.app.get(url('admin_edit_setting', setting_id=1))
47
48
48 def test_edit_as_xml(self):
49 def test_edit_as_xml(self):
49 response = self.app.get(url('formatted_admin_edit_setting',
50 response = self.app.get(url('formatted_admin_edit_setting',
50 setting_id=1, format='xml'))
51 setting_id=1, format='xml'))
51
52
52 def test_ga_code_active(self):
53 def test_ga_code_active(self):
53 self.log_user()
54 self.log_user()
54 old_title = 'RhodeCode'
55 old_title = 'RhodeCode'
55 old_realm = 'RhodeCode authentication'
56 old_realm = 'RhodeCode authentication'
56 new_ga_code = 'ga-test-123456789'
57 new_ga_code = 'ga-test-123456789'
57 response = self.app.post(url('admin_setting', setting_id='global'),
58 response = self.app.post(url('admin_setting', setting_id='global'),
58 params=dict(
59 params=dict(
59 _method='put',
60 _method='put',
60 rhodecode_title=old_title,
61 rhodecode_title=old_title,
61 rhodecode_realm=old_realm,
62 rhodecode_realm=old_realm,
62 rhodecode_ga_code=new_ga_code
63 rhodecode_ga_code=new_ga_code
63 ))
64 ))
64
65
65 self.checkSessionFlash(response, 'Updated application settings')
66 self.checkSessionFlash(response, 'Updated application settings')
66
67
67 self.assertEqual(RhodeCodeSetting
68 self.assertEqual(RhodeCodeSetting
68 .get_app_settings()['rhodecode_ga_code'], new_ga_code)
69 .get_app_settings()['rhodecode_ga_code'], new_ga_code)
69
70
70 response = response.follow()
71 response = response.follow()
71 self.assertTrue("""_gaq.push(['_setAccount', '%s']);""" % new_ga_code
72 response.mustcontain("""_gaq.push(['_setAccount', '%s']);""" % new_ga_code)
72 in response.body)
73
73
74 def test_ga_code_inactive(self):
74 def test_ga_code_inactive(self):
75 self.log_user()
75 self.log_user()
76 old_title = 'RhodeCode'
76 old_title = 'RhodeCode'
77 old_realm = 'RhodeCode authentication'
77 old_realm = 'RhodeCode authentication'
78 new_ga_code = ''
78 new_ga_code = ''
79 response = self.app.post(url('admin_setting', setting_id='global'),
79 response = self.app.post(url('admin_setting', setting_id='global'),
80 params=dict(
80 params=dict(
81 _method='put',
81 _method='put',
82 rhodecode_title=old_title,
82 rhodecode_title=old_title,
83 rhodecode_realm=old_realm,
83 rhodecode_realm=old_realm,
84 rhodecode_ga_code=new_ga_code
84 rhodecode_ga_code=new_ga_code
85 ))
85 ))
86
86
87 self.assertTrue('Updated application settings' in
87 self.assertTrue('Updated application settings' in
88 response.session['flash'][0][1])
88 response.session['flash'][0][1])
89 self.assertEqual(RhodeCodeSetting
89 self.assertEqual(RhodeCodeSetting
90 .get_app_settings()['rhodecode_ga_code'], new_ga_code)
90 .get_app_settings()['rhodecode_ga_code'], new_ga_code)
91
91
92 response = response.follow()
92 response = response.follow()
93 self.assertTrue("""_gaq.push(['_setAccount', '%s']);""" % new_ga_code
93 self.assertFalse("""_gaq.push(['_setAccount', '%s']);""" % new_ga_code
94 not in response.body)
94 in response.body)
95
95
96 def test_title_change(self):
96 def test_title_change(self):
97 self.log_user()
97 self.log_user()
98 old_title = 'RhodeCode'
98 old_title = 'RhodeCode'
99 new_title = old_title + '_changed'
99 new_title = old_title + '_changed'
100 old_realm = 'RhodeCode authentication'
100 old_realm = 'RhodeCode authentication'
101
101
102 for new_title in ['Changed', 'Ε»Γ³Ε‚wik', old_title]:
102 for new_title in ['Changed', 'Ε»Γ³Ε‚wik', old_title]:
103 response = self.app.post(url('admin_setting', setting_id='global'),
103 response = self.app.post(url('admin_setting', setting_id='global'),
104 params=dict(
104 params=dict(
105 _method='put',
105 _method='put',
106 rhodecode_title=new_title,
106 rhodecode_title=new_title,
107 rhodecode_realm=old_realm,
107 rhodecode_realm=old_realm,
108 rhodecode_ga_code=''
108 rhodecode_ga_code=''
109 ))
109 ))
110
110
111 self.checkSessionFlash(response, 'Updated application settings')
111 self.checkSessionFlash(response, 'Updated application settings')
112 self.assertEqual(RhodeCodeSetting
112 self.assertEqual(RhodeCodeSetting
113 .get_app_settings()['rhodecode_title'],
113 .get_app_settings()['rhodecode_title'],
114 new_title.decode('utf-8'))
114 new_title.decode('utf-8'))
115
115
116 response = response.follow()
116 response = response.follow()
117 self.assertTrue("""<h1><a href="/">%s</a></h1>""" % new_title
117 response.mustcontain("""<h1><a href="/">%s</a></h1>""" % new_title)
118 in response.body)
119
118
120 def test_my_account(self):
119 def test_my_account(self):
121 self.log_user()
120 self.log_user()
122 response = self.app.get(url('admin_settings_my_account'))
121 response = self.app.get(url('admin_settings_my_account'))
123
122
124 self.assertTrue('value="test_admin' in response.body)
123 self.assertTrue('value="test_admin' in response.body)
125
124
126 def test_my_account_update(self):
125 @parameterized.expand([('firstname', 'new_username'),
127 self.log_user()
126 ('lastname', 'new_username'),
128
127 ('admin', True),
129 new_email = 'new@mail.pl'
128 ('admin', False),
130 new_name = 'NewName'
129 ('ldap_dn', 'test'),
131 new_lastname = 'NewLastname'
130 ('ldap_dn', None),
132 new_password = 'test123'
131 ('active', False),
133
132 ('active', True),
134 response = self.app.post(url('admin_settings_my_account_update'),
133 ('email', 'some@email.com'),
135 params=dict(_method='put',
134 ])
136 username='test_admin',
135 def test_my_account_update(self, name, expected):
137 new_password=new_password,
136 uname = 'testme'
138 password_confirmation=new_password,
137 usr = UserModel().create_or_update(username=uname, password='qweqwe',
139 password='',
138 email='testme@rhodecod.org')
140 name=new_name,
139 self.Session().commit()
141 lastname=new_lastname,
140 params = usr.get_api_data()
142 email=new_email,))
141 user_id = usr.user_id
143 response.follow()
142 self.log_user(username=uname, password='qweqwe')
143 params.update({name: expected})
144 params.update({'password_confirmation': ''})
145 params.update({'new_password': ''})
144
146
145 assert 'Your account was updated successfully' in response.session['flash'][0][1], 'no flash message about success of change'
147 try:
146 user = self.Session.query(User).filter(User.username == 'test_admin').one()
148 response = self.app.put(url('admin_settings_my_account_update',
147 assert user.email == new_email, 'incorrect user email after update got %s vs %s' % (user.email, new_email)
149 id=user_id), params)
148 assert user.name == new_name, 'updated field mismatch %s vs %s' % (user.name, new_name)
149 assert user.lastname == new_lastname, 'updated field mismatch %s vs %s' % (user.lastname, new_lastname)
150 assert check_password(new_password, user.password) is True, 'password field mismatch %s vs %s' % (user.password, new_password)
151
150
152 #bring back the admin settings
153 old_email = 'test_admin@mail.com'
154 old_name = 'RhodeCode'
155 old_lastname = 'Admin'
156 old_password = 'test12'
157
158 response = self.app.post(url('admin_settings_my_account_update'), params=dict(
159 _method='put',
160 username='test_admin',
161 new_password=old_password,
162 password_confirmation=old_password,
163 password='',
164 name=old_name,
165 lastname=old_lastname,
166 email=old_email,))
167
168 response.follow()
169 self.checkSessionFlash(response,
151 self.checkSessionFlash(response,
170 'Your account was updated successfully')
152 'Your account was updated successfully')
171
153
172 user = self.Session.query(User).filter(User.username == 'test_admin').one()
154 updated_user = User.get_by_username(uname)
173 assert user.email == old_email, 'incorrect user email after update got %s vs %s' % (user.email, old_email)
155 updated_params = updated_user.get_api_data()
156 updated_params.update({'password_confirmation': ''})
157 updated_params.update({'new_password': ''})
174
158
175 assert user.email == old_email, 'incorrect user email after update got %s vs %s' % (user.email, old_email)
159 params['last_login'] = updated_params['last_login']
176 assert user.name == old_name, 'updated field mismatch %s vs %s' % (user.name, old_name)
160 if name == 'email':
177 assert user.lastname == old_lastname, 'updated field mismatch %s vs %s' % (user.lastname, old_lastname)
161 params['emails'] = [expected]
178 assert check_password(old_password, user.password) is True, 'password updated field mismatch %s vs %s' % (user.password, old_password)
162 if name == 'ldap_dn':
163 #cannot update this via form
164 params['ldap_dn'] = None
165 if name == 'active':
166 #my account cannot deactivate account
167 params['active'] = True
168 if name == 'admin':
169 #my account cannot make you an admin !
170 params['admin'] = False
171
172 self.assertEqual(params, updated_params)
173
174 finally:
175 UserModel().delete('testme')
179
176
180 def test_my_account_update_err_email_exists(self):
177 def test_my_account_update_err_email_exists(self):
181 self.log_user()
178 self.log_user()
182
179
183 new_email = 'test_regular@mail.com' # already exisitn email
180 new_email = 'test_regular@mail.com' # already exisitn email
184 response = self.app.post(url('admin_settings_my_account_update'), params=dict(
181 response = self.app.put(url('admin_settings_my_account_update'),
185 _method='put',
182 params=dict(
186 username='test_admin',
183 username='test_admin',
187 new_password='test12',
184 new_password='test12',
188 password_confirmation='test122',
185 password_confirmation='test122',
189 name='NewName',
186 firstname='NewName',
190 lastname='NewLastname',
187 lastname='NewLastname',
191 email=new_email,))
188 email=new_email,)
189 )
192
190
193 assert 'This e-mail address is already taken' in response.body, 'Missing error message about existing email'
191 response.mustcontain('This e-mail address is already taken')
194
192
195 def test_my_account_update_err(self):
193 def test_my_account_update_err(self):
196 self.log_user('test_regular2', 'test12')
194 self.log_user('test_regular2', 'test12')
197
195
198 new_email = 'newmail.pl'
196 new_email = 'newmail.pl'
199 response = self.app.post(url('admin_settings_my_account_update'),
197 response = self.app.post(url('admin_settings_my_account_update'),
200 params=dict(
198 params=dict(
201 _method='put',
199 _method='put',
202 username='test_admin',
200 username='test_admin',
203 new_password='test12',
201 new_password='test12',
204 password_confirmation='test122',
202 password_confirmation='test122',
205 name='NewName',
203 firstname='NewName',
206 lastname='NewLastname',
204 lastname='NewLastname',
207 email=new_email,)
205 email=new_email,)
208 )
206 )
209
207
210 response.mustcontain('An email address must contain a single @')
208 response.mustcontain('An email address must contain a single @')
211 from rhodecode.model import validators
209 from rhodecode.model import validators
212 msg = validators.ValidUsername(edit=False,
210 msg = validators.ValidUsername(edit=False,
213 old_data={})._messages['username_exists']
211 old_data={})._messages['username_exists']
214 msg = h.html_escape(msg % {'username': 'test_admin'})
212 msg = h.html_escape(msg % {'username': 'test_admin'})
215 response.mustcontain(u"%s" % msg)
213 response.mustcontain(u"%s" % msg)
@@ -1,180 +1,216 b''
1 from sqlalchemy.orm.exc import NoResultFound
1 from sqlalchemy.orm.exc import NoResultFound
2
2
3 from rhodecode.tests import *
3 from rhodecode.tests import *
4 from rhodecode.model.db import User, Permission
4 from rhodecode.model.db import User, Permission
5 from rhodecode.lib.auth import check_password
5 from rhodecode.lib.auth import check_password
6 from rhodecode.model.user import UserModel
6 from rhodecode.model.user import UserModel
7 from rhodecode.model import validators
7 from rhodecode.model import validators
8 from rhodecode.lib import helpers as h
8 from rhodecode.lib import helpers as h
9
9
10
10
11 class TestAdminUsersController(TestController):
11 class TestAdminUsersController(TestController):
12
12
13 def test_index(self):
13 def test_index(self):
14 self.log_user()
14 self.log_user()
15 response = self.app.get(url('users'))
15 response = self.app.get(url('users'))
16 # Test response...
16 # Test response...
17
17
18 def test_index_as_xml(self):
18 def test_index_as_xml(self):
19 response = self.app.get(url('formatted_users', format='xml'))
19 response = self.app.get(url('formatted_users', format='xml'))
20
20
21 def test_create(self):
21 def test_create(self):
22 self.log_user()
22 self.log_user()
23 username = 'newtestuser'
23 username = 'newtestuser'
24 password = 'test12'
24 password = 'test12'
25 password_confirmation = password
25 password_confirmation = password
26 name = 'name'
26 name = 'name'
27 lastname = 'lastname'
27 lastname = 'lastname'
28 email = 'mail@mail.com'
28 email = 'mail@mail.com'
29
29
30 response = self.app.post(url('users'),
30 response = self.app.post(url('users'),
31 {'username': username,
31 {'username': username,
32 'password': password,
32 'password': password,
33 'password_confirmation': password_confirmation,
33 'password_confirmation': password_confirmation,
34 'name': name,
34 'firstname': name,
35 'active': True,
35 'active': True,
36 'lastname': lastname,
36 'lastname': lastname,
37 'email': email})
37 'email': email})
38
38
39 self.checkSessionFlash(response, '''created user %s''' % (username))
39 self.checkSessionFlash(response, '''created user %s''' % (username))
40
40
41
42 new_user = self.Session.query(User).\
41 new_user = self.Session.query(User).\
43 filter(User.username == username).one()
42 filter(User.username == username).one()
44
43
45 self.assertEqual(new_user.username, username)
44 self.assertEqual(new_user.username, username)
46 self.assertEqual(check_password(password, new_user.password), True)
45 self.assertEqual(check_password(password, new_user.password), True)
47 self.assertEqual(new_user.name, name)
46 self.assertEqual(new_user.name, name)
48 self.assertEqual(new_user.lastname, lastname)
47 self.assertEqual(new_user.lastname, lastname)
49 self.assertEqual(new_user.email, email)
48 self.assertEqual(new_user.email, email)
50
49
51 response.follow()
50 response.follow()
52 response = response.follow()
51 response = response.follow()
53 self.assertTrue("""edit">newtestuser</a>""" in response.body)
52 response.mustcontain("""edit">newtestuser</a>""")
54
53
55 def test_create_err(self):
54 def test_create_err(self):
56 self.log_user()
55 self.log_user()
57 username = 'new_user'
56 username = 'new_user'
58 password = ''
57 password = ''
59 name = 'name'
58 name = 'name'
60 lastname = 'lastname'
59 lastname = 'lastname'
61 email = 'errmail.com'
60 email = 'errmail.com'
62
61
63 response = self.app.post(url('users'), {'username': username,
62 response = self.app.post(url('users'), {'username': username,
64 'password': password,
63 'password': password,
65 'name': name,
64 'name': name,
66 'active': False,
65 'active': False,
67 'lastname': lastname,
66 'lastname': lastname,
68 'email': email})
67 'email': email})
69
68
70 msg = validators.ValidUsername(False, {})._messages['system_invalid_username']
69 msg = validators.ValidUsername(False, {})._messages['system_invalid_username']
71 msg = h.html_escape(msg % {'username': 'new_user'})
70 msg = h.html_escape(msg % {'username': 'new_user'})
72 response.mustcontain("""<span class="error-message">%s</span>""" % msg)
71 response.mustcontain("""<span class="error-message">%s</span>""" % msg)
73 response.mustcontain("""<span class="error-message">Please enter a value</span>""")
72 response.mustcontain("""<span class="error-message">Please enter a value</span>""")
74 response.mustcontain("""<span class="error-message">An email address must contain a single @</span>""")
73 response.mustcontain("""<span class="error-message">An email address must contain a single @</span>""")
75
74
76 def get_user():
75 def get_user():
77 self.Session.query(User).filter(User.username == username).one()
76 self.Session.query(User).filter(User.username == username).one()
78
77
79 self.assertRaises(NoResultFound, get_user), 'found user in database'
78 self.assertRaises(NoResultFound, get_user), 'found user in database'
80
79
81 def test_new(self):
80 def test_new(self):
82 self.log_user()
81 self.log_user()
83 response = self.app.get(url('new_user'))
82 response = self.app.get(url('new_user'))
84
83
85 def test_new_as_xml(self):
84 def test_new_as_xml(self):
86 response = self.app.get(url('formatted_new_user', format='xml'))
85 response = self.app.get(url('formatted_new_user', format='xml'))
87
86
88 def test_update(self):
87 @parameterized.expand([('firstname', 'new_username'),
89 response = self.app.put(url('user', id=1))
88 ('lastname', 'new_username'),
89 ('admin', True),
90 ('admin', False),
91 ('ldap_dn', 'test'),
92 ('ldap_dn', None),
93 ('active', False),
94 ('active', True),
95 ('email', 'some@email.com'),
96 ])
97 def test_update(self, name, expected):
98 self.log_user()
99 uname = 'testme'
100 usr = UserModel().create_or_update(username=uname, password='qweqwe',
101 email='testme@rhodecod.org')
102 self.Session().commit()
103 params = usr.get_api_data()
104 params.update({name: expected})
105 params.update({'password_confirmation': ''})
106 params.update({'new_password': ''})
107 if name == 'email':
108 params['emails'] = [expected]
109 if name == 'ldap_dn':
110 #cannot update this via form
111 params['ldap_dn'] = None
112 try:
113 response = self.app.put(url('user', id=usr.user_id), params)
114
115 self.checkSessionFlash(response, '''User updated successfully''')
116
117 updated_user = User.get_by_username(uname)
118 updated_params = updated_user.get_api_data()
119 updated_params.update({'password_confirmation': ''})
120 updated_params.update({'new_password': ''})
121
122 self.assertEqual(params, updated_params)
123
124 finally:
125 UserModel().delete('testme')
90
126
91 def test_update_browser_fakeout(self):
127 def test_update_browser_fakeout(self):
92 response = self.app.post(url('user', id=1), params=dict(_method='put'))
128 response = self.app.post(url('user', id=1), params=dict(_method='put'))
93
129
94 def test_delete(self):
130 def test_delete(self):
95 self.log_user()
131 self.log_user()
96 username = 'newtestuserdeleteme'
132 username = 'newtestuserdeleteme'
97 password = 'test12'
133 password = 'test12'
98 name = 'name'
134 name = 'name'
99 lastname = 'lastname'
135 lastname = 'lastname'
100 email = 'todeletemail@mail.com'
136 email = 'todeletemail@mail.com'
101
137
102 response = self.app.post(url('users'), {'username': username,
138 response = self.app.post(url('users'), {'username': username,
103 'password': password,
139 'password': password,
104 'password_confirmation': password,
140 'password_confirmation': password,
105 'name': name,
141 'firstname': name,
106 'active': True,
142 'active': True,
107 'lastname': lastname,
143 'lastname': lastname,
108 'email': email})
144 'email': email})
109
145
110 response = response.follow()
146 response = response.follow()
111
147
112 new_user = self.Session.query(User)\
148 new_user = self.Session.query(User)\
113 .filter(User.username == username).one()
149 .filter(User.username == username).one()
114 response = self.app.delete(url('user', id=new_user.user_id))
150 response = self.app.delete(url('user', id=new_user.user_id))
115
151
116 self.assertTrue("""successfully deleted user""" in
152 self.assertTrue("""successfully deleted user""" in
117 response.session['flash'][0])
153 response.session['flash'][0])
118
154
119 def test_delete_browser_fakeout(self):
155 def test_delete_browser_fakeout(self):
120 response = self.app.post(url('user', id=1),
156 response = self.app.post(url('user', id=1),
121 params=dict(_method='delete'))
157 params=dict(_method='delete'))
122
158
123 def test_show(self):
159 def test_show(self):
124 response = self.app.get(url('user', id=1))
160 response = self.app.get(url('user', id=1))
125
161
126 def test_show_as_xml(self):
162 def test_show_as_xml(self):
127 response = self.app.get(url('formatted_user', id=1, format='xml'))
163 response = self.app.get(url('formatted_user', id=1, format='xml'))
128
164
129 def test_edit(self):
165 def test_edit(self):
130 self.log_user()
166 self.log_user()
131 user = User.get_by_username(TEST_USER_ADMIN_LOGIN)
167 user = User.get_by_username(TEST_USER_ADMIN_LOGIN)
132 response = self.app.get(url('edit_user', id=user.user_id))
168 response = self.app.get(url('edit_user', id=user.user_id))
133
169
134 def test_add_perm_create_repo(self):
170 def test_add_perm_create_repo(self):
135 self.log_user()
171 self.log_user()
136 perm_none = Permission.get_by_key('hg.create.none')
172 perm_none = Permission.get_by_key('hg.create.none')
137 perm_create = Permission.get_by_key('hg.create.repository')
173 perm_create = Permission.get_by_key('hg.create.repository')
138
174
139 user = User.get_by_username(TEST_USER_REGULAR_LOGIN)
175 user = User.get_by_username(TEST_USER_REGULAR_LOGIN)
140
176
141 #User should have None permission on creation repository
177 #User should have None permission on creation repository
142 self.assertEqual(UserModel().has_perm(user, perm_none), False)
178 self.assertEqual(UserModel().has_perm(user, perm_none), False)
143 self.assertEqual(UserModel().has_perm(user, perm_create), False)
179 self.assertEqual(UserModel().has_perm(user, perm_create), False)
144
180
145 response = self.app.post(url('user_perm', id=user.user_id),
181 response = self.app.post(url('user_perm', id=user.user_id),
146 params=dict(_method='put',
182 params=dict(_method='put',
147 create_repo_perm=True))
183 create_repo_perm=True))
148
184
149 perm_none = Permission.get_by_key('hg.create.none')
185 perm_none = Permission.get_by_key('hg.create.none')
150 perm_create = Permission.get_by_key('hg.create.repository')
186 perm_create = Permission.get_by_key('hg.create.repository')
151
187
152 user = User.get_by_username(TEST_USER_REGULAR_LOGIN)
188 user = User.get_by_username(TEST_USER_REGULAR_LOGIN)
153 #User should have None permission on creation repository
189 #User should have None permission on creation repository
154 self.assertEqual(UserModel().has_perm(user, perm_none), False)
190 self.assertEqual(UserModel().has_perm(user, perm_none), False)
155 self.assertEqual(UserModel().has_perm(user, perm_create), True)
191 self.assertEqual(UserModel().has_perm(user, perm_create), True)
156
192
157 def test_revoke_perm_create_repo(self):
193 def test_revoke_perm_create_repo(self):
158 self.log_user()
194 self.log_user()
159 perm_none = Permission.get_by_key('hg.create.none')
195 perm_none = Permission.get_by_key('hg.create.none')
160 perm_create = Permission.get_by_key('hg.create.repository')
196 perm_create = Permission.get_by_key('hg.create.repository')
161
197
162 user = User.get_by_username(TEST_USER_REGULAR2_LOGIN)
198 user = User.get_by_username(TEST_USER_REGULAR2_LOGIN)
163
199
164 #User should have None permission on creation repository
200 #User should have None permission on creation repository
165 self.assertEqual(UserModel().has_perm(user, perm_none), False)
201 self.assertEqual(UserModel().has_perm(user, perm_none), False)
166 self.assertEqual(UserModel().has_perm(user, perm_create), False)
202 self.assertEqual(UserModel().has_perm(user, perm_create), False)
167
203
168 response = self.app.post(url('user_perm', id=user.user_id),
204 response = self.app.post(url('user_perm', id=user.user_id),
169 params=dict(_method='put'))
205 params=dict(_method='put'))
170
206
171 perm_none = Permission.get_by_key('hg.create.none')
207 perm_none = Permission.get_by_key('hg.create.none')
172 perm_create = Permission.get_by_key('hg.create.repository')
208 perm_create = Permission.get_by_key('hg.create.repository')
173
209
174 user = User.get_by_username(TEST_USER_REGULAR2_LOGIN)
210 user = User.get_by_username(TEST_USER_REGULAR2_LOGIN)
175 #User should have None permission on creation repository
211 #User should have None permission on creation repository
176 self.assertEqual(UserModel().has_perm(user, perm_none), True)
212 self.assertEqual(UserModel().has_perm(user, perm_none), True)
177 self.assertEqual(UserModel().has_perm(user, perm_create), False)
213 self.assertEqual(UserModel().has_perm(user, perm_create), False)
178
214
179 def test_edit_as_xml(self):
215 def test_edit_as_xml(self):
180 response = self.app.get(url('formatted_edit_user', id=1, format='xml'))
216 response = self.app.get(url('formatted_edit_user', id=1, format='xml'))
General Comments 0
You need to be logged in to leave comments. Login now