##// END OF EJS Templates
Added form for controlling mercurial extensions...
marcink -
r2708:9bce679a beta
parent child Browse files
Show More
@@ -1,479 +1,482 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, PullRequest, PullRequestReviewers
46 RhodeCodeSetting, PullRequest, PullRequestReviewers
47 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
47 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
48 ApplicationUiSettingsForm, ApplicationVisualisationForm
48 ApplicationUiSettingsForm, ApplicationVisualisationForm
49 from rhodecode.model.scm import ScmModel
49 from rhodecode.model.scm import ScmModel
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
110
111 if setting_id == 'mapping':
111 if setting_id == 'mapping':
112 rm_obsolete = request.POST.get('destroy', False)
112 rm_obsolete = request.POST.get('destroy', False)
113 log.debug('Rescanning directories with destroy=%s' % rm_obsolete)
113 log.debug('Rescanning directories with destroy=%s' % rm_obsolete)
114 initial = ScmModel().repo_scan()
114 initial = ScmModel().repo_scan()
115 log.debug('invalidating all repositories')
115 log.debug('invalidating all repositories')
116 for repo_name in initial.keys():
116 for repo_name in initial.keys():
117 invalidate_cache('get_repo_cached_%s' % repo_name)
117 invalidate_cache('get_repo_cached_%s' % repo_name)
118
118
119 added, removed = repo2db_mapper(initial, rm_obsolete)
119 added, removed = repo2db_mapper(initial, rm_obsolete)
120
120
121 h.flash(_('Repositories successfully'
121 h.flash(_('Repositories successfully'
122 ' rescanned added: %s,removed: %s') % (added, removed),
122 ' rescanned added: %s,removed: %s') % (added, removed),
123 category='success')
123 category='success')
124
124
125 if setting_id == 'whoosh':
125 if setting_id == 'whoosh':
126 repo_location = self.get_hg_ui_settings()['paths_root_path']
126 repo_location = self._get_hg_ui_settings()['paths_root_path']
127 full_index = request.POST.get('full_index', False)
127 full_index = request.POST.get('full_index', False)
128 run_task(tasks.whoosh_index, repo_location, full_index)
128 run_task(tasks.whoosh_index, repo_location, full_index)
129 h.flash(_('Whoosh reindex task scheduled'), category='success')
129 h.flash(_('Whoosh reindex task scheduled'), category='success')
130
130
131 if setting_id == 'global':
131 if setting_id == 'global':
132
132
133 application_form = ApplicationSettingsForm()()
133 application_form = ApplicationSettingsForm()()
134 try:
134 try:
135 form_result = application_form.to_python(dict(request.POST))
135 form_result = application_form.to_python(dict(request.POST))
136 except formencode.Invalid, errors:
136 except formencode.Invalid, errors:
137 return htmlfill.render(
137 return htmlfill.render(
138 render('admin/settings/settings.html'),
138 render('admin/settings/settings.html'),
139 defaults=errors.value,
139 defaults=errors.value,
140 errors=errors.error_dict or {},
140 errors=errors.error_dict or {},
141 prefix_error=False,
141 prefix_error=False,
142 encoding="UTF-8"
142 encoding="UTF-8"
143 )
143 )
144
144
145 try:
145 try:
146 sett1 = RhodeCodeSetting.get_by_name_or_create('title')
146 sett1 = RhodeCodeSetting.get_by_name_or_create('title')
147 sett1.app_settings_value = form_result['rhodecode_title']
147 sett1.app_settings_value = form_result['rhodecode_title']
148 Session().add(sett1)
148 Session().add(sett1)
149
149
150 sett2 = RhodeCodeSetting.get_by_name_or_create('realm')
150 sett2 = RhodeCodeSetting.get_by_name_or_create('realm')
151 sett2.app_settings_value = form_result['rhodecode_realm']
151 sett2.app_settings_value = form_result['rhodecode_realm']
152 Session().add(sett2)
152 Session().add(sett2)
153
153
154 sett3 = RhodeCodeSetting.get_by_name_or_create('ga_code')
154 sett3 = RhodeCodeSetting.get_by_name_or_create('ga_code')
155 sett3.app_settings_value = form_result['rhodecode_ga_code']
155 sett3.app_settings_value = form_result['rhodecode_ga_code']
156 Session().add(sett3)
156 Session().add(sett3)
157
157
158 Session().commit()
158 Session().commit()
159 set_rhodecode_config(config)
159 set_rhodecode_config(config)
160 h.flash(_('Updated application settings'), category='success')
160 h.flash(_('Updated application settings'), category='success')
161
161
162 except Exception:
162 except Exception:
163 log.error(traceback.format_exc())
163 log.error(traceback.format_exc())
164 h.flash(_('error occurred during updating '
164 h.flash(_('error occurred during updating '
165 'application settings'),
165 'application settings'),
166 category='error')
166 category='error')
167
167
168 if setting_id == 'visual':
168 if setting_id == 'visual':
169
169
170 application_form = ApplicationVisualisationForm()()
170 application_form = ApplicationVisualisationForm()()
171 try:
171 try:
172 form_result = application_form.to_python(dict(request.POST))
172 form_result = application_form.to_python(dict(request.POST))
173 except formencode.Invalid, errors:
173 except formencode.Invalid, errors:
174 return htmlfill.render(
174 return htmlfill.render(
175 render('admin/settings/settings.html'),
175 render('admin/settings/settings.html'),
176 defaults=errors.value,
176 defaults=errors.value,
177 errors=errors.error_dict or {},
177 errors=errors.error_dict or {},
178 prefix_error=False,
178 prefix_error=False,
179 encoding="UTF-8"
179 encoding="UTF-8"
180 )
180 )
181
181
182 try:
182 try:
183 sett1 = RhodeCodeSetting.get_by_name_or_create('show_public_icon')
183 sett1 = RhodeCodeSetting.get_by_name_or_create('show_public_icon')
184 sett1.app_settings_value = \
184 sett1.app_settings_value = \
185 form_result['rhodecode_show_public_icon']
185 form_result['rhodecode_show_public_icon']
186
186
187 sett2 = RhodeCodeSetting.get_by_name_or_create('show_private_icon')
187 sett2 = RhodeCodeSetting.get_by_name_or_create('show_private_icon')
188 sett2.app_settings_value = \
188 sett2.app_settings_value = \
189 form_result['rhodecode_show_private_icon']
189 form_result['rhodecode_show_private_icon']
190
190
191 sett3 = RhodeCodeSetting.get_by_name_or_create('stylify_metatags')
191 sett3 = RhodeCodeSetting.get_by_name_or_create('stylify_metatags')
192 sett3.app_settings_value = \
192 sett3.app_settings_value = \
193 form_result['rhodecode_stylify_metatags']
193 form_result['rhodecode_stylify_metatags']
194
194
195 Session().add(sett1)
195 Session().add(sett1)
196 Session().add(sett2)
196 Session().add(sett2)
197 Session().add(sett3)
197 Session().add(sett3)
198 Session().commit()
198 Session().commit()
199 set_rhodecode_config(config)
199 set_rhodecode_config(config)
200 h.flash(_('Updated visualisation settings'),
200 h.flash(_('Updated visualisation settings'),
201 category='success')
201 category='success')
202
202
203 except Exception:
203 except Exception:
204 log.error(traceback.format_exc())
204 log.error(traceback.format_exc())
205 h.flash(_('error occurred during updating '
205 h.flash(_('error occurred during updating '
206 'visualisation settings'),
206 'visualisation settings'),
207 category='error')
207 category='error')
208
208
209 if setting_id == 'vcs':
209 if setting_id == 'vcs':
210 application_form = ApplicationUiSettingsForm()()
210 application_form = ApplicationUiSettingsForm()()
211 try:
211 try:
212 form_result = application_form.to_python(dict(request.POST))
212 form_result = application_form.to_python(dict(request.POST))
213 except formencode.Invalid, errors:
213 except formencode.Invalid, errors:
214 return htmlfill.render(
214 return htmlfill.render(
215 render('admin/settings/settings.html'),
215 render('admin/settings/settings.html'),
216 defaults=errors.value,
216 defaults=errors.value,
217 errors=errors.error_dict or {},
217 errors=errors.error_dict or {},
218 prefix_error=False,
218 prefix_error=False,
219 encoding="UTF-8"
219 encoding="UTF-8"
220 )
220 )
221
221
222 try:
222 try:
223 # fix namespaces for hooks
223 # fix namespaces for hooks and extensions
224 _f = lambda s: s.replace('.', '_')
224 _f = lambda s: s.replace('.', '_')
225
225
226 sett1 = RhodeCodeUi.query()\
226 sett = RhodeCodeUi.get_by_key('push_ssl')
227 .filter(RhodeCodeUi.ui_key == 'push_ssl').one()
227 sett.ui_value = form_result['web_push_ssl']
228 sett1.ui_value = form_result['web_push_ssl']
228 Session().add(sett)
229
229
230 sett2 = RhodeCodeUi.query()\
230 sett = RhodeCodeUi.get_by_key('/')
231 .filter(RhodeCodeUi.ui_key == '/').one()
231 sett.ui_value = form_result['paths_root_path']
232 sett2.ui_value = form_result['paths_root_path']
232 Session().add(sett)
233
233
234 #HOOKS
234 #HOOKS
235 sett3 = RhodeCodeUi.query()\
235 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_UPDATE)
236 .filter(RhodeCodeUi.ui_key == RhodeCodeUi.HOOK_UPDATE)\
236 sett.ui_active = form_result[_f('hooks_%s' %
237 .one()
237 RhodeCodeUi.HOOK_UPDATE)]
238 sett3.ui_active = bool(form_result[_f('hooks_%s' %
238 Session().add(sett)
239 RhodeCodeUi.HOOK_UPDATE)])
240
239
241 sett4 = RhodeCodeUi.query()\
240 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_REPO_SIZE)
242 .filter(RhodeCodeUi.ui_key == RhodeCodeUi.HOOK_REPO_SIZE)\
241 sett.ui_active = form_result[_f('hooks_%s' %
243 .one()
242 RhodeCodeUi.HOOK_REPO_SIZE)]
244 sett4.ui_active = bool(form_result[_f('hooks_%s' %
243 Session().add(sett)
245 RhodeCodeUi.HOOK_REPO_SIZE)])
244
245 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_PUSH)
246 sett.ui_active = form_result[_f('hooks_%s' %
247 RhodeCodeUi.HOOK_PUSH)]
248 Session().add(sett)
246
249
247 sett5 = RhodeCodeUi.query()\
250 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_PULL)
248 .filter(RhodeCodeUi.ui_key == RhodeCodeUi.HOOK_PUSH)\
251 sett.ui_active = form_result[_f('hooks_%s' %
249 .one()
252 RhodeCodeUi.HOOK_PULL)]
250 sett5.ui_active = bool(form_result[_f('hooks_%s' %
253
251 RhodeCodeUi.HOOK_PUSH)])
254 Session().add(sett)
252
255
253 sett6 = RhodeCodeUi.query()\
256 ## EXTENSIONS
254 .filter(RhodeCodeUi.ui_key == RhodeCodeUi.HOOK_PULL)\
257 sett = RhodeCodeUi.get_by_key('largefiles')
255 .one()
258 sett.ui_active = form_result[_f('extensions_largefiles')]
256 sett6.ui_active = bool(form_result[_f('hooks_%s' %
259 Session().add(sett)
257 RhodeCodeUi.HOOK_PULL)])
258
260
259 Session().add(sett1)
261 sett = RhodeCodeUi.get_by_key('hgsubversion')
260 Session().add(sett2)
262 sett.ui_active = form_result[_f('extensions_hgsubversion')]
261 Session().add(sett3)
263 Session().add(sett)
262 Session().add(sett4)
264
263 Session().add(sett5)
265 # sett = RhodeCodeUi.get_by_key('hggit')
264 Session().add(sett6)
266 # sett.ui_active = form_result[_f('extensions_hggit')]
267 # Session().add(sett)
268
265 Session().commit()
269 Session().commit()
266
270
267 h.flash(_('Updated mercurial settings'), category='success')
271 h.flash(_('Updated VCS settings'), category='success')
268
272
269 except Exception:
273 except Exception:
270 log.error(traceback.format_exc())
274 log.error(traceback.format_exc())
271 h.flash(_('error occurred during updating '
275 h.flash(_('error occurred during updating '
272 'application settings'), category='error')
276 'application settings'), category='error')
273
277
274 if setting_id == 'hooks':
278 if setting_id == 'hooks':
275 ui_key = request.POST.get('new_hook_ui_key')
279 ui_key = request.POST.get('new_hook_ui_key')
276 ui_value = request.POST.get('new_hook_ui_value')
280 ui_value = request.POST.get('new_hook_ui_value')
277 try:
281 try:
278
282
279 if ui_value and ui_key:
283 if ui_value and ui_key:
280 RhodeCodeUi.create_or_update_hook(ui_key, ui_value)
284 RhodeCodeUi.create_or_update_hook(ui_key, ui_value)
281 h.flash(_('Added new hook'),
285 h.flash(_('Added new hook'),
282 category='success')
286 category='success')
283
287
284 # check for edits
288 # check for edits
285 update = False
289 update = False
286 _d = request.POST.dict_of_lists()
290 _d = request.POST.dict_of_lists()
287 for k, v in zip(_d.get('hook_ui_key', []),
291 for k, v in zip(_d.get('hook_ui_key', []),
288 _d.get('hook_ui_value_new', [])):
292 _d.get('hook_ui_value_new', [])):
289 RhodeCodeUi.create_or_update_hook(k, v)
293 RhodeCodeUi.create_or_update_hook(k, v)
290 update = True
294 update = True
291
295
292 if update:
296 if update:
293 h.flash(_('Updated hooks'), category='success')
297 h.flash(_('Updated hooks'), category='success')
294 Session().commit()
298 Session().commit()
295 except Exception:
299 except Exception:
296 log.error(traceback.format_exc())
300 log.error(traceback.format_exc())
297 h.flash(_('error occurred during hook creation'),
301 h.flash(_('error occurred during hook creation'),
298 category='error')
302 category='error')
299
303
300 return redirect(url('admin_edit_setting', setting_id='hooks'))
304 return redirect(url('admin_edit_setting', setting_id='hooks'))
301
305
302 if setting_id == 'email':
306 if setting_id == 'email':
303 test_email = request.POST.get('test_email')
307 test_email = request.POST.get('test_email')
304 test_email_subj = 'RhodeCode TestEmail'
308 test_email_subj = 'RhodeCode TestEmail'
305 test_email_body = 'RhodeCode Email test'
309 test_email_body = 'RhodeCode Email test'
306
310
307 test_email_html_body = EmailNotificationModel()\
311 test_email_html_body = EmailNotificationModel()\
308 .get_email_tmpl(EmailNotificationModel.TYPE_DEFAULT,
312 .get_email_tmpl(EmailNotificationModel.TYPE_DEFAULT,
309 body=test_email_body)
313 body=test_email_body)
310
314
311 recipients = [test_email] if [test_email] else None
315 recipients = [test_email] if [test_email] else None
312
316
313 run_task(tasks.send_email, recipients, test_email_subj,
317 run_task(tasks.send_email, recipients, test_email_subj,
314 test_email_body, test_email_html_body)
318 test_email_body, test_email_html_body)
315
319
316 h.flash(_('Email task created'), category='success')
320 h.flash(_('Email task created'), category='success')
317 return redirect(url('admin_settings'))
321 return redirect(url('admin_settings'))
318
322
319 @HasPermissionAllDecorator('hg.admin')
323 @HasPermissionAllDecorator('hg.admin')
320 def delete(self, setting_id):
324 def delete(self, setting_id):
321 """DELETE /admin/settings/setting_id: Delete an existing item"""
325 """DELETE /admin/settings/setting_id: Delete an existing item"""
322 # Forms posted to this method should contain a hidden field:
326 # Forms posted to this method should contain a hidden field:
323 # <input type="hidden" name="_method" value="DELETE" />
327 # <input type="hidden" name="_method" value="DELETE" />
324 # Or using helpers:
328 # Or using helpers:
325 # h.form(url('admin_setting', setting_id=ID),
329 # h.form(url('admin_setting', setting_id=ID),
326 # method='delete')
330 # method='delete')
327 # url('admin_setting', setting_id=ID)
331 # url('admin_setting', setting_id=ID)
328 if setting_id == 'hooks':
332 if setting_id == 'hooks':
329 hook_id = request.POST.get('hook_id')
333 hook_id = request.POST.get('hook_id')
330 RhodeCodeUi.delete(hook_id)
334 RhodeCodeUi.delete(hook_id)
331 Session().commit()
335 Session().commit()
332
336
333 @HasPermissionAllDecorator('hg.admin')
337 @HasPermissionAllDecorator('hg.admin')
334 def show(self, setting_id, format='html'):
338 def show(self, setting_id, format='html'):
335 """
339 """
336 GET /admin/settings/setting_id: Show a specific item"""
340 GET /admin/settings/setting_id: Show a specific item"""
337 # url('admin_setting', setting_id=ID)
341 # url('admin_setting', setting_id=ID)
338
342
339 @HasPermissionAllDecorator('hg.admin')
343 @HasPermissionAllDecorator('hg.admin')
340 def edit(self, setting_id, format='html'):
344 def edit(self, setting_id, format='html'):
341 """
345 """
342 GET /admin/settings/setting_id/edit: Form to
346 GET /admin/settings/setting_id/edit: Form to
343 edit an existing item"""
347 edit an existing item"""
344 # url('admin_edit_setting', setting_id=ID)
348 # url('admin_edit_setting', setting_id=ID)
345
349
346 c.hooks = RhodeCodeUi.get_builtin_hooks()
350 c.hooks = RhodeCodeUi.get_builtin_hooks()
347 c.custom_hooks = RhodeCodeUi.get_custom_hooks()
351 c.custom_hooks = RhodeCodeUi.get_custom_hooks()
348
352
349 return htmlfill.render(
353 return htmlfill.render(
350 render('admin/settings/hooks.html'),
354 render('admin/settings/hooks.html'),
351 defaults={},
355 defaults={},
352 encoding="UTF-8",
356 encoding="UTF-8",
353 force_defaults=False
357 force_defaults=False
354 )
358 )
355
359
356 @NotAnonymous()
360 @NotAnonymous()
357 def my_account(self):
361 def my_account(self):
358 """
362 """
359 GET /_admin/my_account Displays info about my account
363 GET /_admin/my_account Displays info about my account
360 """
364 """
361 # url('admin_settings_my_account')
365 # url('admin_settings_my_account')
362
366
363 c.user = User.get(self.rhodecode_user.user_id)
367 c.user = User.get(self.rhodecode_user.user_id)
364 all_repos = Session().query(Repository)\
368 all_repos = Session().query(Repository)\
365 .filter(Repository.user_id == c.user.user_id)\
369 .filter(Repository.user_id == c.user.user_id)\
366 .order_by(func.lower(Repository.repo_name)).all()
370 .order_by(func.lower(Repository.repo_name)).all()
367
371
368 c.user_repos = ScmModel().get_repos(all_repos)
372 c.user_repos = ScmModel().get_repos(all_repos)
369
373
370 if c.user.username == 'default':
374 if c.user.username == 'default':
371 h.flash(_("You can't edit this user since it's"
375 h.flash(_("You can't edit this user since it's"
372 " crucial for entire application"), category='warning')
376 " crucial for entire application"), category='warning')
373 return redirect(url('users'))
377 return redirect(url('users'))
374
378
375 defaults = c.user.get_dict()
379 defaults = c.user.get_dict()
376
380
377 c.form = htmlfill.render(
381 c.form = htmlfill.render(
378 render('admin/users/user_edit_my_account_form.html'),
382 render('admin/users/user_edit_my_account_form.html'),
379 defaults=defaults,
383 defaults=defaults,
380 encoding="UTF-8",
384 encoding="UTF-8",
381 force_defaults=False
385 force_defaults=False
382 )
386 )
383 return render('admin/users/user_edit_my_account.html')
387 return render('admin/users/user_edit_my_account.html')
384
388
385 @NotAnonymous()
389 @NotAnonymous()
386 def my_account_update(self):
390 def my_account_update(self):
387 """PUT /_admin/my_account_update: Update an existing item"""
391 """PUT /_admin/my_account_update: Update an existing item"""
388 # Forms posted to this method should contain a hidden field:
392 # Forms posted to this method should contain a hidden field:
389 # <input type="hidden" name="_method" value="PUT" />
393 # <input type="hidden" name="_method" value="PUT" />
390 # Or using helpers:
394 # Or using helpers:
391 # h.form(url('admin_settings_my_account_update'),
395 # h.form(url('admin_settings_my_account_update'),
392 # method='put')
396 # method='put')
393 # url('admin_settings_my_account_update', id=ID)
397 # url('admin_settings_my_account_update', id=ID)
394 uid = self.rhodecode_user.user_id
398 uid = self.rhodecode_user.user_id
395 email = self.rhodecode_user.email
399 email = self.rhodecode_user.email
396 _form = UserForm(edit=True,
400 _form = UserForm(edit=True,
397 old_data={'user_id': uid, 'email': email})()
401 old_data={'user_id': uid, 'email': email})()
398 form_result = {}
402 form_result = {}
399 try:
403 try:
400 form_result = _form.to_python(dict(request.POST))
404 form_result = _form.to_python(dict(request.POST))
401 UserModel().update_my_account(uid, form_result)
405 UserModel().update_my_account(uid, form_result)
402 h.flash(_('Your account was updated successfully'),
406 h.flash(_('Your account was updated successfully'),
403 category='success')
407 category='success')
404 Session().commit()
408 Session().commit()
405 except formencode.Invalid, errors:
409 except formencode.Invalid, errors:
406 c.user = User.get(self.rhodecode_user.user_id)
410 c.user = User.get(self.rhodecode_user.user_id)
407
411
408 c.form = htmlfill.render(
412 c.form = htmlfill.render(
409 render('admin/users/user_edit_my_account_form.html'),
413 render('admin/users/user_edit_my_account_form.html'),
410 defaults=errors.value,
414 defaults=errors.value,
411 errors=errors.error_dict or {},
415 errors=errors.error_dict or {},
412 prefix_error=False,
416 prefix_error=False,
413 encoding="UTF-8")
417 encoding="UTF-8")
414 return render('admin/users/user_edit_my_account.html')
418 return render('admin/users/user_edit_my_account.html')
415 except Exception:
419 except Exception:
416 log.error(traceback.format_exc())
420 log.error(traceback.format_exc())
417 h.flash(_('error occurred during update of user %s') \
421 h.flash(_('error occurred during update of user %s') \
418 % form_result.get('username'), category='error')
422 % form_result.get('username'), category='error')
419
423
420 return redirect(url('my_account'))
424 return redirect(url('my_account'))
421
425
422 @NotAnonymous()
426 @NotAnonymous()
423 def my_account_my_repos(self):
427 def my_account_my_repos(self):
424 all_repos = Session().query(Repository)\
428 all_repos = Session().query(Repository)\
425 .filter(Repository.user_id == self.rhodecode_user.user_id)\
429 .filter(Repository.user_id == self.rhodecode_user.user_id)\
426 .order_by(func.lower(Repository.repo_name))\
430 .order_by(func.lower(Repository.repo_name))\
427 .all()
431 .all()
428 c.user_repos = ScmModel().get_repos(all_repos)
432 c.user_repos = ScmModel().get_repos(all_repos)
429 return render('admin/users/user_edit_my_account_repos.html')
433 return render('admin/users/user_edit_my_account_repos.html')
430
434
431 @NotAnonymous()
435 @NotAnonymous()
432 def my_account_my_pullrequests(self):
436 def my_account_my_pullrequests(self):
433 c.my_pull_requests = PullRequest.query()\
437 c.my_pull_requests = PullRequest.query()\
434 .filter(PullRequest.user_id==
438 .filter(PullRequest.user_id==
435 self.rhodecode_user.user_id)\
439 self.rhodecode_user.user_id)\
436 .all()
440 .all()
437 c.participate_in_pull_requests = \
441 c.participate_in_pull_requests = \
438 [x.pull_request for x in PullRequestReviewers.query()\
442 [x.pull_request for x in PullRequestReviewers.query()\
439 .filter(PullRequestReviewers.user_id==
443 .filter(PullRequestReviewers.user_id==
440 self.rhodecode_user.user_id)\
444 self.rhodecode_user.user_id)\
441 .all()]
445 .all()]
442 return render('admin/users/user_edit_my_account_pullrequests.html')
446 return render('admin/users/user_edit_my_account_pullrequests.html')
443
447
444 @NotAnonymous()
448 @NotAnonymous()
445 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
449 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
446 def create_repository(self):
450 def create_repository(self):
447 """GET /_admin/create_repository: Form to create a new item"""
451 """GET /_admin/create_repository: Form to create a new item"""
448
452
449 c.repo_groups = RepoGroup.groups_choices()
453 c.repo_groups = RepoGroup.groups_choices()
450 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
454 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
451 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
455 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
452
456
453 new_repo = request.GET.get('repo', '')
457 new_repo = request.GET.get('repo', '')
454 c.new_repo = repo_name_slug(new_repo)
458 c.new_repo = repo_name_slug(new_repo)
455
459
456 return render('admin/repos/repo_add_create_repository.html')
460 return render('admin/repos/repo_add_create_repository.html')
457
461
458 @NotAnonymous()
462 def _get_hg_ui_settings(self):
459 def get_hg_ui_settings(self):
460 ret = RhodeCodeUi.query().all()
463 ret = RhodeCodeUi.query().all()
461
464
462 if not ret:
465 if not ret:
463 raise Exception('Could not get application ui settings !')
466 raise Exception('Could not get application ui settings !')
464 settings = {}
467 settings = {}
465 for each in ret:
468 for each in ret:
466 k = each.ui_key
469 k = each.ui_key
467 v = each.ui_value
470 v = each.ui_value
468 if k == '/':
471 if k == '/':
469 k = 'root_path'
472 k = 'root_path'
470
473
471 if k.find('.') != -1:
474 if k.find('.') != -1:
472 k = k.replace('.', '_')
475 k = k.replace('.', '_')
473
476
474 if each.ui_section == 'hooks':
477 if each.ui_section in ['hooks', 'extensions']:
475 v = each.ui_active
478 v = each.ui_active
476
479
477 settings[each.ui_section + '_' + k] = v
480 settings[each.ui_section + '_' + k] = v
478
481
479 return settings
482 return settings
@@ -1,257 +1,256 b''
1 """The base Controller API
1 """The base Controller API
2
2
3 Provides the BaseController class for subclassing.
3 Provides the BaseController class for subclassing.
4 """
4 """
5 import logging
5 import logging
6 import time
6 import time
7 import traceback
7 import traceback
8
8
9 from paste.auth.basic import AuthBasicAuthenticator
9 from paste.auth.basic import AuthBasicAuthenticator
10 from paste.httpexceptions import HTTPUnauthorized, HTTPForbidden
10 from paste.httpexceptions import HTTPUnauthorized, HTTPForbidden
11 from paste.httpheaders import WWW_AUTHENTICATE
11 from paste.httpheaders import WWW_AUTHENTICATE
12
12
13 from pylons import config, tmpl_context as c, request, session, url
13 from pylons import config, tmpl_context as c, request, session, url
14 from pylons.controllers import WSGIController
14 from pylons.controllers import WSGIController
15 from pylons.controllers.util import redirect
15 from pylons.controllers.util import redirect
16 from pylons.templating import render_mako as render
16 from pylons.templating import render_mako as render
17
17
18 from rhodecode import __version__, BACKENDS
18 from rhodecode import __version__, BACKENDS
19
19
20 from rhodecode.lib.utils2 import str2bool, safe_unicode, AttributeDict
20 from rhodecode.lib.utils2 import str2bool, safe_unicode, AttributeDict
21 from rhodecode.lib.auth import AuthUser, get_container_username, authfunc,\
21 from rhodecode.lib.auth import AuthUser, get_container_username, authfunc,\
22 HasPermissionAnyMiddleware, CookieStoreWrapper
22 HasPermissionAnyMiddleware, CookieStoreWrapper
23 from rhodecode.lib.utils import get_repo_slug, invalidate_cache
23 from rhodecode.lib.utils import get_repo_slug, invalidate_cache
24 from rhodecode.model import meta
24 from rhodecode.model import meta
25
25
26 from rhodecode.model.db import Repository, RhodeCodeUi
26 from rhodecode.model.db import Repository, RhodeCodeUi
27 from rhodecode.model.notification import NotificationModel
27 from rhodecode.model.notification import NotificationModel
28 from rhodecode.model.scm import ScmModel
28 from rhodecode.model.scm import ScmModel
29
29
30 log = logging.getLogger(__name__)
30 log = logging.getLogger(__name__)
31
31
32
32
33 def _get_ip_addr(environ):
33 def _get_ip_addr(environ):
34 proxy_key = 'HTTP_X_REAL_IP'
34 proxy_key = 'HTTP_X_REAL_IP'
35 proxy_key2 = 'HTTP_X_FORWARDED_FOR'
35 proxy_key2 = 'HTTP_X_FORWARDED_FOR'
36 def_key = 'REMOTE_ADDR'
36 def_key = 'REMOTE_ADDR'
37
37
38 ip = environ.get(proxy_key2)
38 ip = environ.get(proxy_key2)
39 if ip:
39 if ip:
40 return ip
40 return ip
41
41
42 ip = environ.get(proxy_key)
42 ip = environ.get(proxy_key)
43
43
44 if ip:
44 if ip:
45 return ip
45 return ip
46
46
47 ip = environ.get(def_key, '0.0.0.0')
47 ip = environ.get(def_key, '0.0.0.0')
48 return ip
48 return ip
49
49
50
50
51 def _get_access_path(environ):
51 def _get_access_path(environ):
52 path = environ.get('PATH_INFO')
52 path = environ.get('PATH_INFO')
53 org_req = environ.get('pylons.original_request')
53 org_req = environ.get('pylons.original_request')
54 if org_req:
54 if org_req:
55 path = org_req.environ.get('PATH_INFO')
55 path = org_req.environ.get('PATH_INFO')
56 return path
56 return path
57
57
58
58
59 class BasicAuth(AuthBasicAuthenticator):
59 class BasicAuth(AuthBasicAuthenticator):
60
60
61 def __init__(self, realm, authfunc, auth_http_code=None):
61 def __init__(self, realm, authfunc, auth_http_code=None):
62 self.realm = realm
62 self.realm = realm
63 self.authfunc = authfunc
63 self.authfunc = authfunc
64 self._rc_auth_http_code = auth_http_code
64 self._rc_auth_http_code = auth_http_code
65
65
66 def build_authentication(self):
66 def build_authentication(self):
67 head = WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm)
67 head = WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm)
68 if self._rc_auth_http_code and self._rc_auth_http_code == '403':
68 if self._rc_auth_http_code and self._rc_auth_http_code == '403':
69 # return 403 if alternative http return code is specified in
69 # return 403 if alternative http return code is specified in
70 # RhodeCode config
70 # RhodeCode config
71 return HTTPForbidden(headers=head)
71 return HTTPForbidden(headers=head)
72 return HTTPUnauthorized(headers=head)
72 return HTTPUnauthorized(headers=head)
73
73
74
74
75 class BaseVCSController(object):
75 class BaseVCSController(object):
76
76
77 def __init__(self, application, config):
77 def __init__(self, application, config):
78 self.application = application
78 self.application = application
79 self.config = config
79 self.config = config
80 # base path of repo locations
80 # base path of repo locations
81 self.basepath = self.config['base_path']
81 self.basepath = self.config['base_path']
82 #authenticate this mercurial request using authfunc
82 #authenticate this mercurial request using authfunc
83 self.authenticate = BasicAuth('', authfunc,
83 self.authenticate = BasicAuth('', authfunc,
84 config.get('auth_ret_code'))
84 config.get('auth_ret_code'))
85 self.ipaddr = '0.0.0.0'
85 self.ipaddr = '0.0.0.0'
86
86
87 def _handle_request(self, environ, start_response):
87 def _handle_request(self, environ, start_response):
88 raise NotImplementedError()
88 raise NotImplementedError()
89
89
90 def _get_by_id(self, repo_name):
90 def _get_by_id(self, repo_name):
91 """
91 """
92 Get's a special pattern _<ID> from clone url and tries to replace it
92 Get's a special pattern _<ID> from clone url and tries to replace it
93 with a repository_name for support of _<ID> non changable urls
93 with a repository_name for support of _<ID> non changable urls
94
94
95 :param repo_name:
95 :param repo_name:
96 """
96 """
97 try:
97 try:
98 data = repo_name.split('/')
98 data = repo_name.split('/')
99 if len(data) >= 2:
99 if len(data) >= 2:
100 by_id = data[1].split('_')
100 by_id = data[1].split('_')
101 if len(by_id) == 2 and by_id[1].isdigit():
101 if len(by_id) == 2 and by_id[1].isdigit():
102 _repo_name = Repository.get(by_id[1]).repo_name
102 _repo_name = Repository.get(by_id[1]).repo_name
103 data[1] = _repo_name
103 data[1] = _repo_name
104 except:
104 except:
105 log.debug('Failed to extract repo_name from id %s' % (
105 log.debug('Failed to extract repo_name from id %s' % (
106 traceback.format_exc()
106 traceback.format_exc()
107 )
107 )
108 )
108 )
109
109
110 return '/'.join(data)
110 return '/'.join(data)
111
111
112 def _invalidate_cache(self, repo_name):
112 def _invalidate_cache(self, repo_name):
113 """
113 """
114 Set's cache for this repository for invalidation on next access
114 Set's cache for this repository for invalidation on next access
115
115
116 :param repo_name: full repo name, also a cache key
116 :param repo_name: full repo name, also a cache key
117 """
117 """
118 invalidate_cache('get_repo_cached_%s' % repo_name)
118 invalidate_cache('get_repo_cached_%s' % repo_name)
119
119
120 def _check_permission(self, action, user, repo_name):
120 def _check_permission(self, action, user, repo_name):
121 """
121 """
122 Checks permissions using action (push/pull) user and repository
122 Checks permissions using action (push/pull) user and repository
123 name
123 name
124
124
125 :param action: push or pull action
125 :param action: push or pull action
126 :param user: user instance
126 :param user: user instance
127 :param repo_name: repository name
127 :param repo_name: repository name
128 """
128 """
129 if action == 'push':
129 if action == 'push':
130 if not HasPermissionAnyMiddleware('repository.write',
130 if not HasPermissionAnyMiddleware('repository.write',
131 'repository.admin')(user,
131 'repository.admin')(user,
132 repo_name):
132 repo_name):
133 return False
133 return False
134
134
135 else:
135 else:
136 #any other action need at least read permission
136 #any other action need at least read permission
137 if not HasPermissionAnyMiddleware('repository.read',
137 if not HasPermissionAnyMiddleware('repository.read',
138 'repository.write',
138 'repository.write',
139 'repository.admin')(user,
139 'repository.admin')(user,
140 repo_name):
140 repo_name):
141 return False
141 return False
142
142
143 return True
143 return True
144
144
145 def _get_ip_addr(self, environ):
145 def _get_ip_addr(self, environ):
146 return _get_ip_addr(environ)
146 return _get_ip_addr(environ)
147
147
148 def _check_ssl(self, environ, start_response):
148 def _check_ssl(self, environ, start_response):
149 """
149 """
150 Checks the SSL check flag and returns False if SSL is not present
150 Checks the SSL check flag and returns False if SSL is not present
151 and required True otherwise
151 and required True otherwise
152 """
152 """
153 org_proto = environ['wsgi._org_proto']
153 org_proto = environ['wsgi._org_proto']
154 #check if we have SSL required ! if not it's a bad request !
154 #check if we have SSL required ! if not it's a bad request !
155 require_ssl = str2bool(RhodeCodeUi.get_by_key('push_ssl')\
155 require_ssl = str2bool(RhodeCodeUi.get_by_key('push_ssl').ui_value)
156 .scalar().ui_value)
157 if require_ssl and org_proto == 'http':
156 if require_ssl and org_proto == 'http':
158 log.debug('proto is %s and SSL is required BAD REQUEST !'
157 log.debug('proto is %s and SSL is required BAD REQUEST !'
159 % org_proto)
158 % org_proto)
160 return False
159 return False
161 return True
160 return True
162
161
163 def __call__(self, environ, start_response):
162 def __call__(self, environ, start_response):
164 start = time.time()
163 start = time.time()
165 try:
164 try:
166 return self._handle_request(environ, start_response)
165 return self._handle_request(environ, start_response)
167 finally:
166 finally:
168 log = logging.getLogger('rhodecode.' + self.__class__.__name__)
167 log = logging.getLogger('rhodecode.' + self.__class__.__name__)
169 log.debug('Request time: %.3fs' % (time.time() - start))
168 log.debug('Request time: %.3fs' % (time.time() - start))
170 meta.Session.remove()
169 meta.Session.remove()
171
170
172
171
173 class BaseController(WSGIController):
172 class BaseController(WSGIController):
174
173
175 def __before__(self):
174 def __before__(self):
176 c.rhodecode_version = __version__
175 c.rhodecode_version = __version__
177 c.rhodecode_instanceid = config.get('instance_id')
176 c.rhodecode_instanceid = config.get('instance_id')
178 c.rhodecode_name = config.get('rhodecode_title')
177 c.rhodecode_name = config.get('rhodecode_title')
179 c.use_gravatar = str2bool(config.get('use_gravatar'))
178 c.use_gravatar = str2bool(config.get('use_gravatar'))
180 c.ga_code = config.get('rhodecode_ga_code')
179 c.ga_code = config.get('rhodecode_ga_code')
181 # Visual options
180 # Visual options
182 c.visual = AttributeDict({})
181 c.visual = AttributeDict({})
183 c.visual.show_public_icon = str2bool(config.get('rhodecode_show_public_icon'))
182 c.visual.show_public_icon = str2bool(config.get('rhodecode_show_public_icon'))
184 c.visual.show_private_icon = str2bool(config.get('rhodecode_show_private_icon'))
183 c.visual.show_private_icon = str2bool(config.get('rhodecode_show_private_icon'))
185 c.visual.stylify_metatags = str2bool(config.get('rhodecode_stylify_metatags'))
184 c.visual.stylify_metatags = str2bool(config.get('rhodecode_stylify_metatags'))
186
185
187 c.repo_name = get_repo_slug(request)
186 c.repo_name = get_repo_slug(request)
188 c.backends = BACKENDS.keys()
187 c.backends = BACKENDS.keys()
189 c.unread_notifications = NotificationModel()\
188 c.unread_notifications = NotificationModel()\
190 .get_unread_cnt_for_user(c.rhodecode_user.user_id)
189 .get_unread_cnt_for_user(c.rhodecode_user.user_id)
191 self.cut_off_limit = int(config.get('cut_off_limit'))
190 self.cut_off_limit = int(config.get('cut_off_limit'))
192
191
193 self.sa = meta.Session
192 self.sa = meta.Session
194 self.scm_model = ScmModel(self.sa)
193 self.scm_model = ScmModel(self.sa)
195 self.ip_addr = ''
194 self.ip_addr = ''
196
195
197 def __call__(self, environ, start_response):
196 def __call__(self, environ, start_response):
198 """Invoke the Controller"""
197 """Invoke the Controller"""
199 # WSGIController.__call__ dispatches to the Controller method
198 # WSGIController.__call__ dispatches to the Controller method
200 # the request is routed to. This routing information is
199 # the request is routed to. This routing information is
201 # available in environ['pylons.routes_dict']
200 # available in environ['pylons.routes_dict']
202 start = time.time()
201 start = time.time()
203 try:
202 try:
204 self.ip_addr = _get_ip_addr(environ)
203 self.ip_addr = _get_ip_addr(environ)
205 # make sure that we update permissions each time we call controller
204 # make sure that we update permissions each time we call controller
206 api_key = request.GET.get('api_key')
205 api_key = request.GET.get('api_key')
207 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
206 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
208 user_id = cookie_store.get('user_id', None)
207 user_id = cookie_store.get('user_id', None)
209 username = get_container_username(environ, config)
208 username = get_container_username(environ, config)
210 auth_user = AuthUser(user_id, api_key, username)
209 auth_user = AuthUser(user_id, api_key, username)
211 request.user = auth_user
210 request.user = auth_user
212 self.rhodecode_user = c.rhodecode_user = auth_user
211 self.rhodecode_user = c.rhodecode_user = auth_user
213 if not self.rhodecode_user.is_authenticated and \
212 if not self.rhodecode_user.is_authenticated and \
214 self.rhodecode_user.user_id is not None:
213 self.rhodecode_user.user_id is not None:
215 self.rhodecode_user.set_authenticated(
214 self.rhodecode_user.set_authenticated(
216 cookie_store.get('is_authenticated')
215 cookie_store.get('is_authenticated')
217 )
216 )
218 log.info('IP: %s User: %s accessed %s' % (
217 log.info('IP: %s User: %s accessed %s' % (
219 self.ip_addr, auth_user, safe_unicode(_get_access_path(environ)))
218 self.ip_addr, auth_user, safe_unicode(_get_access_path(environ)))
220 )
219 )
221 return WSGIController.__call__(self, environ, start_response)
220 return WSGIController.__call__(self, environ, start_response)
222 finally:
221 finally:
223 log.info('IP: %s Request to %s time: %.3fs' % (
222 log.info('IP: %s Request to %s time: %.3fs' % (
224 _get_ip_addr(environ),
223 _get_ip_addr(environ),
225 safe_unicode(_get_access_path(environ)), time.time() - start)
224 safe_unicode(_get_access_path(environ)), time.time() - start)
226 )
225 )
227 meta.Session.remove()
226 meta.Session.remove()
228
227
229
228
230 class BaseRepoController(BaseController):
229 class BaseRepoController(BaseController):
231 """
230 """
232 Base class for controllers responsible for loading all needed data for
231 Base class for controllers responsible for loading all needed data for
233 repository loaded items are
232 repository loaded items are
234
233
235 c.rhodecode_repo: instance of scm repository
234 c.rhodecode_repo: instance of scm repository
236 c.rhodecode_db_repo: instance of db
235 c.rhodecode_db_repo: instance of db
237 c.repository_followers: number of followers
236 c.repository_followers: number of followers
238 c.repository_forks: number of forks
237 c.repository_forks: number of forks
239 """
238 """
240
239
241 def __before__(self):
240 def __before__(self):
242 super(BaseRepoController, self).__before__()
241 super(BaseRepoController, self).__before__()
243 if c.repo_name:
242 if c.repo_name:
244
243
245 dbr = c.rhodecode_db_repo = Repository.get_by_repo_name(c.repo_name)
244 dbr = c.rhodecode_db_repo = Repository.get_by_repo_name(c.repo_name)
246 c.rhodecode_repo = c.rhodecode_db_repo.scm_instance
245 c.rhodecode_repo = c.rhodecode_db_repo.scm_instance
247
246
248 if c.rhodecode_repo is None:
247 if c.rhodecode_repo is None:
249 log.error('%s this repository is present in database but it '
248 log.error('%s this repository is present in database but it '
250 'cannot be created as an scm instance', c.repo_name)
249 'cannot be created as an scm instance', c.repo_name)
251
250
252 redirect(url('home'))
251 redirect(url('home'))
253
252
254 # some globals counter for menu
253 # some globals counter for menu
255 c.repository_followers = self.scm_model.get_followers(dbr)
254 c.repository_followers = self.scm_model.get_followers(dbr)
256 c.repository_forks = self.scm_model.get_forks(dbr)
255 c.repository_forks = self.scm_model.get_forks(dbr)
257 c.repository_pull_requests = self.scm_model.get_pull_requests(dbr)
256 c.repository_pull_requests = self.scm_model.get_pull_requests(dbr)
@@ -1,531 +1,539 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.db_manage
3 rhodecode.lib.db_manage
4 ~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Database creation, and setup module for RhodeCode. Used for creation
6 Database creation, and setup module for RhodeCode. Used for creation
7 of database as well as for migration operations
7 of database as well as for migration operations
8
8
9 :created_on: Apr 10, 2010
9 :created_on: Apr 10, 2010
10 :author: marcink
10 :author: marcink
11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
12 :license: GPLv3, see COPYING for more details.
12 :license: GPLv3, see COPYING for more details.
13 """
13 """
14 # This program is free software: you can redistribute it and/or modify
14 # This program is free software: you can redistribute it and/or modify
15 # it under the terms of the GNU General Public License as published by
15 # it under the terms of the GNU General Public License as published by
16 # the Free Software Foundation, either version 3 of the License, or
16 # the Free Software Foundation, either version 3 of the License, or
17 # (at your option) any later version.
17 # (at your option) any later version.
18 #
18 #
19 # This program is distributed in the hope that it will be useful,
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
22 # GNU General Public License for more details.
23 #
23 #
24 # You should have received a copy of the GNU General Public License
24 # You should have received a copy of the GNU General Public License
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26
26
27 import os
27 import os
28 import sys
28 import sys
29 import uuid
29 import uuid
30 import logging
30 import logging
31 from os.path import dirname as dn, join as jn
31 from os.path import dirname as dn, join as jn
32
32
33 from rhodecode import __dbversion__
33 from rhodecode import __dbversion__
34 from rhodecode.model import meta
34 from rhodecode.model import meta
35
35
36 from rhodecode.model.user import UserModel
36 from rhodecode.model.user import UserModel
37 from rhodecode.lib.utils import ask_ok
37 from rhodecode.lib.utils import ask_ok
38 from rhodecode.model import init_model
38 from rhodecode.model import init_model
39 from rhodecode.model.db import User, Permission, RhodeCodeUi, \
39 from rhodecode.model.db import User, Permission, RhodeCodeUi, \
40 RhodeCodeSetting, UserToPerm, DbMigrateVersion, RepoGroup,\
40 RhodeCodeSetting, UserToPerm, DbMigrateVersion, RepoGroup,\
41 UserRepoGroupToPerm
41 UserRepoGroupToPerm
42
42
43 from sqlalchemy.engine import create_engine
43 from sqlalchemy.engine import create_engine
44 from rhodecode.model.repos_group import ReposGroupModel
44 from rhodecode.model.repos_group import ReposGroupModel
45
45
46 log = logging.getLogger(__name__)
46 log = logging.getLogger(__name__)
47
47
48
48
49 class DbManage(object):
49 class DbManage(object):
50 def __init__(self, log_sql, dbconf, root, tests=False):
50 def __init__(self, log_sql, dbconf, root, tests=False):
51 self.dbname = dbconf.split('/')[-1]
51 self.dbname = dbconf.split('/')[-1]
52 self.tests = tests
52 self.tests = tests
53 self.root = root
53 self.root = root
54 self.dburi = dbconf
54 self.dburi = dbconf
55 self.log_sql = log_sql
55 self.log_sql = log_sql
56 self.db_exists = False
56 self.db_exists = False
57 self.init_db()
57 self.init_db()
58
58
59 def init_db(self):
59 def init_db(self):
60 engine = create_engine(self.dburi, echo=self.log_sql)
60 engine = create_engine(self.dburi, echo=self.log_sql)
61 init_model(engine)
61 init_model(engine)
62 self.sa = meta.Session()
62 self.sa = meta.Session()
63
63
64 def create_tables(self, override=False, defaults={}):
64 def create_tables(self, override=False, defaults={}):
65 """
65 """
66 Create a auth database
66 Create a auth database
67 """
67 """
68 quiet = defaults.get('quiet')
68 quiet = defaults.get('quiet')
69 log.info("Any existing database is going to be destroyed")
69 log.info("Any existing database is going to be destroyed")
70 if self.tests or quiet:
70 if self.tests or quiet:
71 destroy = True
71 destroy = True
72 else:
72 else:
73 destroy = ask_ok('Are you sure to destroy old database ? [y/n]')
73 destroy = ask_ok('Are you sure to destroy old database ? [y/n]')
74 if not destroy:
74 if not destroy:
75 sys.exit()
75 sys.exit()
76 if destroy:
76 if destroy:
77 meta.Base.metadata.drop_all()
77 meta.Base.metadata.drop_all()
78
78
79 checkfirst = not override
79 checkfirst = not override
80 meta.Base.metadata.create_all(checkfirst=checkfirst)
80 meta.Base.metadata.create_all(checkfirst=checkfirst)
81 log.info('Created tables for %s' % self.dbname)
81 log.info('Created tables for %s' % self.dbname)
82
82
83 def set_db_version(self):
83 def set_db_version(self):
84 ver = DbMigrateVersion()
84 ver = DbMigrateVersion()
85 ver.version = __dbversion__
85 ver.version = __dbversion__
86 ver.repository_id = 'rhodecode_db_migrations'
86 ver.repository_id = 'rhodecode_db_migrations'
87 ver.repository_path = 'versions'
87 ver.repository_path = 'versions'
88 self.sa.add(ver)
88 self.sa.add(ver)
89 log.info('db version set to: %s' % __dbversion__)
89 log.info('db version set to: %s' % __dbversion__)
90
90
91 def upgrade(self):
91 def upgrade(self):
92 """
92 """
93 Upgrades given database schema to given revision following
93 Upgrades given database schema to given revision following
94 all needed steps, to perform the upgrade
94 all needed steps, to perform the upgrade
95
95
96 """
96 """
97
97
98 from rhodecode.lib.dbmigrate.migrate.versioning import api
98 from rhodecode.lib.dbmigrate.migrate.versioning import api
99 from rhodecode.lib.dbmigrate.migrate.exceptions import \
99 from rhodecode.lib.dbmigrate.migrate.exceptions import \
100 DatabaseNotControlledError
100 DatabaseNotControlledError
101
101
102 if 'sqlite' in self.dburi:
102 if 'sqlite' in self.dburi:
103 print (
103 print (
104 '********************** WARNING **********************\n'
104 '********************** WARNING **********************\n'
105 'Make sure your version of sqlite is at least 3.7.X. \n'
105 'Make sure your version of sqlite is at least 3.7.X. \n'
106 'Earlier versions are known to fail on some migrations\n'
106 'Earlier versions are known to fail on some migrations\n'
107 '*****************************************************\n'
107 '*****************************************************\n'
108 )
108 )
109 upgrade = ask_ok('You are about to perform database upgrade, make '
109 upgrade = ask_ok('You are about to perform database upgrade, make '
110 'sure You backed up your database before. '
110 'sure You backed up your database before. '
111 'Continue ? [y/n]')
111 'Continue ? [y/n]')
112 if not upgrade:
112 if not upgrade:
113 sys.exit('Nothing done')
113 sys.exit('Nothing done')
114
114
115 repository_path = jn(dn(dn(dn(os.path.realpath(__file__)))),
115 repository_path = jn(dn(dn(dn(os.path.realpath(__file__)))),
116 'rhodecode/lib/dbmigrate')
116 'rhodecode/lib/dbmigrate')
117 db_uri = self.dburi
117 db_uri = self.dburi
118
118
119 try:
119 try:
120 curr_version = api.db_version(db_uri, repository_path)
120 curr_version = api.db_version(db_uri, repository_path)
121 msg = ('Found current database under version'
121 msg = ('Found current database under version'
122 ' control with version %s' % curr_version)
122 ' control with version %s' % curr_version)
123
123
124 except (RuntimeError, DatabaseNotControlledError):
124 except (RuntimeError, DatabaseNotControlledError):
125 curr_version = 1
125 curr_version = 1
126 msg = ('Current database is not under version control. Setting'
126 msg = ('Current database is not under version control. Setting'
127 ' as version %s' % curr_version)
127 ' as version %s' % curr_version)
128 api.version_control(db_uri, repository_path, curr_version)
128 api.version_control(db_uri, repository_path, curr_version)
129
129
130 print (msg)
130 print (msg)
131
131
132 if curr_version == __dbversion__:
132 if curr_version == __dbversion__:
133 sys.exit('This database is already at the newest version')
133 sys.exit('This database is already at the newest version')
134
134
135 #======================================================================
135 #======================================================================
136 # UPGRADE STEPS
136 # UPGRADE STEPS
137 #======================================================================
137 #======================================================================
138 class UpgradeSteps(object):
138 class UpgradeSteps(object):
139 """
139 """
140 Those steps follow schema versions so for example schema
140 Those steps follow schema versions so for example schema
141 for example schema with seq 002 == step_2 and so on.
141 for example schema with seq 002 == step_2 and so on.
142 """
142 """
143
143
144 def __init__(self, klass):
144 def __init__(self, klass):
145 self.klass = klass
145 self.klass = klass
146
146
147 def step_0(self):
147 def step_0(self):
148 # step 0 is the schema upgrade, and than follow proper upgrades
148 # step 0 is the schema upgrade, and than follow proper upgrades
149 print ('attempting to do database upgrade to version %s' \
149 print ('attempting to do database upgrade to version %s' \
150 % __dbversion__)
150 % __dbversion__)
151 api.upgrade(db_uri, repository_path, __dbversion__)
151 api.upgrade(db_uri, repository_path, __dbversion__)
152 print ('Schema upgrade completed')
152 print ('Schema upgrade completed')
153
153
154 def step_1(self):
154 def step_1(self):
155 pass
155 pass
156
156
157 def step_2(self):
157 def step_2(self):
158 print ('Patching repo paths for newer version of RhodeCode')
158 print ('Patching repo paths for newer version of RhodeCode')
159 self.klass.fix_repo_paths()
159 self.klass.fix_repo_paths()
160
160
161 print ('Patching default user of RhodeCode')
161 print ('Patching default user of RhodeCode')
162 self.klass.fix_default_user()
162 self.klass.fix_default_user()
163
163
164 log.info('Changing ui settings')
164 log.info('Changing ui settings')
165 self.klass.create_ui_settings()
165 self.klass.create_ui_settings()
166
166
167 def step_3(self):
167 def step_3(self):
168 print ('Adding additional settings into RhodeCode db')
168 print ('Adding additional settings into RhodeCode db')
169 self.klass.fix_settings()
169 self.klass.fix_settings()
170 print ('Adding ldap defaults')
170 print ('Adding ldap defaults')
171 self.klass.create_ldap_options(skip_existing=True)
171 self.klass.create_ldap_options(skip_existing=True)
172
172
173 def step_4(self):
173 def step_4(self):
174 print ('create permissions and fix groups')
174 print ('create permissions and fix groups')
175 self.klass.create_permissions()
175 self.klass.create_permissions()
176 self.klass.fixup_groups()
176 self.klass.fixup_groups()
177
177
178 def step_5(self):
178 def step_5(self):
179 pass
179 pass
180
180
181 def step_6(self):
181 def step_6(self):
182 pass
182 pass
183 upgrade_steps = [0] + range(curr_version + 1, __dbversion__ + 1)
183 upgrade_steps = [0] + range(curr_version + 1, __dbversion__ + 1)
184
184
185 # CALL THE PROPER ORDER OF STEPS TO PERFORM FULL UPGRADE
185 # CALL THE PROPER ORDER OF STEPS TO PERFORM FULL UPGRADE
186 for step in upgrade_steps:
186 for step in upgrade_steps:
187 print ('performing upgrade step %s' % step)
187 print ('performing upgrade step %s' % step)
188 getattr(UpgradeSteps(self), 'step_%s' % step)()
188 getattr(UpgradeSteps(self), 'step_%s' % step)()
189 self.sa.commit()
189 self.sa.commit()
190
190
191 def fix_repo_paths(self):
191 def fix_repo_paths(self):
192 """
192 """
193 Fixes a old rhodecode version path into new one without a '*'
193 Fixes a old rhodecode version path into new one without a '*'
194 """
194 """
195
195
196 paths = self.sa.query(RhodeCodeUi)\
196 paths = self.sa.query(RhodeCodeUi)\
197 .filter(RhodeCodeUi.ui_key == '/')\
197 .filter(RhodeCodeUi.ui_key == '/')\
198 .scalar()
198 .scalar()
199
199
200 paths.ui_value = paths.ui_value.replace('*', '')
200 paths.ui_value = paths.ui_value.replace('*', '')
201
201
202 try:
202 try:
203 self.sa.add(paths)
203 self.sa.add(paths)
204 self.sa.commit()
204 self.sa.commit()
205 except:
205 except:
206 self.sa.rollback()
206 self.sa.rollback()
207 raise
207 raise
208
208
209 def fix_default_user(self):
209 def fix_default_user(self):
210 """
210 """
211 Fixes a old default user with some 'nicer' default values,
211 Fixes a old default user with some 'nicer' default values,
212 used mostly for anonymous access
212 used mostly for anonymous access
213 """
213 """
214 def_user = self.sa.query(User)\
214 def_user = self.sa.query(User)\
215 .filter(User.username == 'default')\
215 .filter(User.username == 'default')\
216 .one()
216 .one()
217
217
218 def_user.name = 'Anonymous'
218 def_user.name = 'Anonymous'
219 def_user.lastname = 'User'
219 def_user.lastname = 'User'
220 def_user.email = 'anonymous@rhodecode.org'
220 def_user.email = 'anonymous@rhodecode.org'
221
221
222 try:
222 try:
223 self.sa.add(def_user)
223 self.sa.add(def_user)
224 self.sa.commit()
224 self.sa.commit()
225 except:
225 except:
226 self.sa.rollback()
226 self.sa.rollback()
227 raise
227 raise
228
228
229 def fix_settings(self):
229 def fix_settings(self):
230 """
230 """
231 Fixes rhodecode settings adds ga_code key for google analytics
231 Fixes rhodecode settings adds ga_code key for google analytics
232 """
232 """
233
233
234 hgsettings3 = RhodeCodeSetting('ga_code', '')
234 hgsettings3 = RhodeCodeSetting('ga_code', '')
235
235
236 try:
236 try:
237 self.sa.add(hgsettings3)
237 self.sa.add(hgsettings3)
238 self.sa.commit()
238 self.sa.commit()
239 except:
239 except:
240 self.sa.rollback()
240 self.sa.rollback()
241 raise
241 raise
242
242
243 def admin_prompt(self, second=False, defaults={}):
243 def admin_prompt(self, second=False, defaults={}):
244 if not self.tests:
244 if not self.tests:
245 import getpass
245 import getpass
246
246
247 # defaults
247 # defaults
248 username = defaults.get('username')
248 username = defaults.get('username')
249 password = defaults.get('password')
249 password = defaults.get('password')
250 email = defaults.get('email')
250 email = defaults.get('email')
251
251
252 def get_password():
252 def get_password():
253 password = getpass.getpass('Specify admin password '
253 password = getpass.getpass('Specify admin password '
254 '(min 6 chars):')
254 '(min 6 chars):')
255 confirm = getpass.getpass('Confirm password:')
255 confirm = getpass.getpass('Confirm password:')
256
256
257 if password != confirm:
257 if password != confirm:
258 log.error('passwords mismatch')
258 log.error('passwords mismatch')
259 return False
259 return False
260 if len(password) < 6:
260 if len(password) < 6:
261 log.error('password is to short use at least 6 characters')
261 log.error('password is to short use at least 6 characters')
262 return False
262 return False
263
263
264 return password
264 return password
265 if username is None:
265 if username is None:
266 username = raw_input('Specify admin username:')
266 username = raw_input('Specify admin username:')
267 if password is None:
267 if password is None:
268 password = get_password()
268 password = get_password()
269 if not password:
269 if not password:
270 #second try
270 #second try
271 password = get_password()
271 password = get_password()
272 if not password:
272 if not password:
273 sys.exit()
273 sys.exit()
274 if email is None:
274 if email is None:
275 email = raw_input('Specify admin email:')
275 email = raw_input('Specify admin email:')
276 self.create_user(username, password, email, True)
276 self.create_user(username, password, email, True)
277 else:
277 else:
278 log.info('creating admin and regular test users')
278 log.info('creating admin and regular test users')
279 from rhodecode.tests import TEST_USER_ADMIN_LOGIN,\
279 from rhodecode.tests import TEST_USER_ADMIN_LOGIN,\
280 TEST_USER_ADMIN_PASS, TEST_USER_ADMIN_EMAIL,\
280 TEST_USER_ADMIN_PASS, TEST_USER_ADMIN_EMAIL,\
281 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS,\
281 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS,\
282 TEST_USER_REGULAR_EMAIL, TEST_USER_REGULAR2_LOGIN, \
282 TEST_USER_REGULAR_EMAIL, TEST_USER_REGULAR2_LOGIN, \
283 TEST_USER_REGULAR2_PASS, TEST_USER_REGULAR2_EMAIL
283 TEST_USER_REGULAR2_PASS, TEST_USER_REGULAR2_EMAIL
284
284
285 self.create_user(TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS,
285 self.create_user(TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS,
286 TEST_USER_ADMIN_EMAIL, True)
286 TEST_USER_ADMIN_EMAIL, True)
287
287
288 self.create_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS,
288 self.create_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS,
289 TEST_USER_REGULAR_EMAIL, False)
289 TEST_USER_REGULAR_EMAIL, False)
290
290
291 self.create_user(TEST_USER_REGULAR2_LOGIN, TEST_USER_REGULAR2_PASS,
291 self.create_user(TEST_USER_REGULAR2_LOGIN, TEST_USER_REGULAR2_PASS,
292 TEST_USER_REGULAR2_EMAIL, False)
292 TEST_USER_REGULAR2_EMAIL, False)
293
293
294 def create_ui_settings(self):
294 def create_ui_settings(self):
295 """
295 """
296 Creates ui settings, fills out hooks
296 Creates ui settings, fills out hooks
297 and disables dotencode
297 and disables dotencode
298 """
298 """
299
299
300 #HOOKS
300 #HOOKS
301 hooks1_key = RhodeCodeUi.HOOK_UPDATE
301 hooks1_key = RhodeCodeUi.HOOK_UPDATE
302 hooks1_ = self.sa.query(RhodeCodeUi)\
302 hooks1_ = self.sa.query(RhodeCodeUi)\
303 .filter(RhodeCodeUi.ui_key == hooks1_key).scalar()
303 .filter(RhodeCodeUi.ui_key == hooks1_key).scalar()
304
304
305 hooks1 = RhodeCodeUi() if hooks1_ is None else hooks1_
305 hooks1 = RhodeCodeUi() if hooks1_ is None else hooks1_
306 hooks1.ui_section = 'hooks'
306 hooks1.ui_section = 'hooks'
307 hooks1.ui_key = hooks1_key
307 hooks1.ui_key = hooks1_key
308 hooks1.ui_value = 'hg update >&2'
308 hooks1.ui_value = 'hg update >&2'
309 hooks1.ui_active = False
309 hooks1.ui_active = False
310
310
311 hooks2_key = RhodeCodeUi.HOOK_REPO_SIZE
311 hooks2_key = RhodeCodeUi.HOOK_REPO_SIZE
312 hooks2_ = self.sa.query(RhodeCodeUi)\
312 hooks2_ = self.sa.query(RhodeCodeUi)\
313 .filter(RhodeCodeUi.ui_key == hooks2_key).scalar()
313 .filter(RhodeCodeUi.ui_key == hooks2_key).scalar()
314
314
315 hooks2 = RhodeCodeUi() if hooks2_ is None else hooks2_
315 hooks2 = RhodeCodeUi() if hooks2_ is None else hooks2_
316 hooks2.ui_section = 'hooks'
316 hooks2.ui_section = 'hooks'
317 hooks2.ui_key = hooks2_key
317 hooks2.ui_key = hooks2_key
318 hooks2.ui_value = 'python:rhodecode.lib.hooks.repo_size'
318 hooks2.ui_value = 'python:rhodecode.lib.hooks.repo_size'
319
319
320 hooks3 = RhodeCodeUi()
320 hooks3 = RhodeCodeUi()
321 hooks3.ui_section = 'hooks'
321 hooks3.ui_section = 'hooks'
322 hooks3.ui_key = RhodeCodeUi.HOOK_PUSH
322 hooks3.ui_key = RhodeCodeUi.HOOK_PUSH
323 hooks3.ui_value = 'python:rhodecode.lib.hooks.log_push_action'
323 hooks3.ui_value = 'python:rhodecode.lib.hooks.log_push_action'
324
324
325 hooks4 = RhodeCodeUi()
325 hooks4 = RhodeCodeUi()
326 hooks4.ui_section = 'hooks'
326 hooks4.ui_section = 'hooks'
327 hooks4.ui_key = RhodeCodeUi.HOOK_PULL
327 hooks4.ui_key = RhodeCodeUi.HOOK_PULL
328 hooks4.ui_value = 'python:rhodecode.lib.hooks.log_pull_action'
328 hooks4.ui_value = 'python:rhodecode.lib.hooks.log_pull_action'
329
329
330 # For mercurial 1.7 set backward comapatibility with format
330 # For mercurial 1.7 set backward comapatibility with format
331 dotencode_disable = RhodeCodeUi()
331 dotencode_disable = RhodeCodeUi()
332 dotencode_disable.ui_section = 'format'
332 dotencode_disable.ui_section = 'format'
333 dotencode_disable.ui_key = 'dotencode'
333 dotencode_disable.ui_key = 'dotencode'
334 dotencode_disable.ui_value = 'false'
334 dotencode_disable.ui_value = 'false'
335
335
336 # enable largefiles
336 # enable largefiles
337 largefiles = RhodeCodeUi()
337 largefiles = RhodeCodeUi()
338 largefiles.ui_section = 'extensions'
338 largefiles.ui_section = 'extensions'
339 largefiles.ui_key = 'largefiles'
339 largefiles.ui_key = 'largefiles'
340 largefiles.ui_value = ''
340 largefiles.ui_value = ''
341
341
342 # enable hgsubversion disabled by default
342 # enable hgsubversion disabled by default
343 hgsubversion = RhodeCodeUi()
343 hgsubversion = RhodeCodeUi()
344 hgsubversion.ui_section = 'extensions'
344 hgsubversion.ui_section = 'extensions'
345 hgsubversion.ui_key = 'hgsubversion'
345 hgsubversion.ui_key = 'hgsubversion'
346 hgsubversion.ui_value = ''
346 hgsubversion.ui_value = ''
347 hgsubversion.ui_active = False
347 hgsubversion.ui_active = False
348
348
349 # enable hggit disabled by default
350 hggit = RhodeCodeUi()
351 hggit.ui_section = 'extensions'
352 hggit.ui_key = 'hggit'
353 hggit.ui_value = ''
354 hggit.ui_active = False
355
349 self.sa.add(hooks1)
356 self.sa.add(hooks1)
350 self.sa.add(hooks2)
357 self.sa.add(hooks2)
351 self.sa.add(hooks3)
358 self.sa.add(hooks3)
352 self.sa.add(hooks4)
359 self.sa.add(hooks4)
353 self.sa.add(largefiles)
360 self.sa.add(largefiles)
354 self.sa.add(hgsubversion)
361 self.sa.add(hgsubversion)
362 self.sa.add(hggit)
355
363
356 def create_ldap_options(self, skip_existing=False):
364 def create_ldap_options(self, skip_existing=False):
357 """Creates ldap settings"""
365 """Creates ldap settings"""
358
366
359 for k, v in [('ldap_active', 'false'), ('ldap_host', ''),
367 for k, v in [('ldap_active', 'false'), ('ldap_host', ''),
360 ('ldap_port', '389'), ('ldap_tls_kind', 'PLAIN'),
368 ('ldap_port', '389'), ('ldap_tls_kind', 'PLAIN'),
361 ('ldap_tls_reqcert', ''), ('ldap_dn_user', ''),
369 ('ldap_tls_reqcert', ''), ('ldap_dn_user', ''),
362 ('ldap_dn_pass', ''), ('ldap_base_dn', ''),
370 ('ldap_dn_pass', ''), ('ldap_base_dn', ''),
363 ('ldap_filter', ''), ('ldap_search_scope', ''),
371 ('ldap_filter', ''), ('ldap_search_scope', ''),
364 ('ldap_attr_login', ''), ('ldap_attr_firstname', ''),
372 ('ldap_attr_login', ''), ('ldap_attr_firstname', ''),
365 ('ldap_attr_lastname', ''), ('ldap_attr_email', '')]:
373 ('ldap_attr_lastname', ''), ('ldap_attr_email', '')]:
366
374
367 if skip_existing and RhodeCodeSetting.get_by_name(k) != None:
375 if skip_existing and RhodeCodeSetting.get_by_name(k) != None:
368 log.debug('Skipping option %s' % k)
376 log.debug('Skipping option %s' % k)
369 continue
377 continue
370 setting = RhodeCodeSetting(k, v)
378 setting = RhodeCodeSetting(k, v)
371 self.sa.add(setting)
379 self.sa.add(setting)
372
380
373 def fixup_groups(self):
381 def fixup_groups(self):
374 def_usr = User.get_by_username('default')
382 def_usr = User.get_by_username('default')
375 for g in RepoGroup.query().all():
383 for g in RepoGroup.query().all():
376 g.group_name = g.get_new_name(g.name)
384 g.group_name = g.get_new_name(g.name)
377 self.sa.add(g)
385 self.sa.add(g)
378 # get default perm
386 # get default perm
379 default = UserRepoGroupToPerm.query()\
387 default = UserRepoGroupToPerm.query()\
380 .filter(UserRepoGroupToPerm.group == g)\
388 .filter(UserRepoGroupToPerm.group == g)\
381 .filter(UserRepoGroupToPerm.user == def_usr)\
389 .filter(UserRepoGroupToPerm.user == def_usr)\
382 .scalar()
390 .scalar()
383
391
384 if default is None:
392 if default is None:
385 log.debug('missing default permission for group %s adding' % g)
393 log.debug('missing default permission for group %s adding' % g)
386 ReposGroupModel()._create_default_perms(g)
394 ReposGroupModel()._create_default_perms(g)
387
395
388 def config_prompt(self, test_repo_path='', retries=3, defaults={}):
396 def config_prompt(self, test_repo_path='', retries=3, defaults={}):
389 _path = defaults.get('repos_location')
397 _path = defaults.get('repos_location')
390 if retries == 3:
398 if retries == 3:
391 log.info('Setting up repositories config')
399 log.info('Setting up repositories config')
392
400
393 if _path is not None:
401 if _path is not None:
394 path = _path
402 path = _path
395 elif not self.tests and not test_repo_path:
403 elif not self.tests and not test_repo_path:
396 path = raw_input(
404 path = raw_input(
397 'Enter a valid absolute path to store repositories. '
405 'Enter a valid absolute path to store repositories. '
398 'All repositories in that path will be added automatically:'
406 'All repositories in that path will be added automatically:'
399 )
407 )
400 else:
408 else:
401 path = test_repo_path
409 path = test_repo_path
402 path_ok = True
410 path_ok = True
403
411
404 # check proper dir
412 # check proper dir
405 if not os.path.isdir(path):
413 if not os.path.isdir(path):
406 path_ok = False
414 path_ok = False
407 log.error('Given path %s is not a valid directory' % path)
415 log.error('Given path %s is not a valid directory' % path)
408
416
409 elif not os.path.isabs(path):
417 elif not os.path.isabs(path):
410 path_ok = False
418 path_ok = False
411 log.error('Given path %s is not an absolute path' % path)
419 log.error('Given path %s is not an absolute path' % path)
412
420
413 # check write access
421 # check write access
414 elif not os.access(path, os.W_OK) and path_ok:
422 elif not os.access(path, os.W_OK) and path_ok:
415 path_ok = False
423 path_ok = False
416 log.error('No write permission to given path %s' % path)
424 log.error('No write permission to given path %s' % path)
417
425
418 if retries == 0:
426 if retries == 0:
419 sys.exit('max retries reached')
427 sys.exit('max retries reached')
420 if path_ok is False:
428 if path_ok is False:
421 retries -= 1
429 retries -= 1
422 return self.config_prompt(test_repo_path, retries)
430 return self.config_prompt(test_repo_path, retries)
423
431
424 return path
432 return path
425
433
426 def create_settings(self, path):
434 def create_settings(self, path):
427
435
428 self.create_ui_settings()
436 self.create_ui_settings()
429
437
430 #HG UI OPTIONS
438 #HG UI OPTIONS
431 web1 = RhodeCodeUi()
439 web1 = RhodeCodeUi()
432 web1.ui_section = 'web'
440 web1.ui_section = 'web'
433 web1.ui_key = 'push_ssl'
441 web1.ui_key = 'push_ssl'
434 web1.ui_value = 'false'
442 web1.ui_value = 'false'
435
443
436 web2 = RhodeCodeUi()
444 web2 = RhodeCodeUi()
437 web2.ui_section = 'web'
445 web2.ui_section = 'web'
438 web2.ui_key = 'allow_archive'
446 web2.ui_key = 'allow_archive'
439 web2.ui_value = 'gz zip bz2'
447 web2.ui_value = 'gz zip bz2'
440
448
441 web3 = RhodeCodeUi()
449 web3 = RhodeCodeUi()
442 web3.ui_section = 'web'
450 web3.ui_section = 'web'
443 web3.ui_key = 'allow_push'
451 web3.ui_key = 'allow_push'
444 web3.ui_value = '*'
452 web3.ui_value = '*'
445
453
446 web4 = RhodeCodeUi()
454 web4 = RhodeCodeUi()
447 web4.ui_section = 'web'
455 web4.ui_section = 'web'
448 web4.ui_key = 'baseurl'
456 web4.ui_key = 'baseurl'
449 web4.ui_value = '/'
457 web4.ui_value = '/'
450
458
451 paths = RhodeCodeUi()
459 paths = RhodeCodeUi()
452 paths.ui_section = 'paths'
460 paths.ui_section = 'paths'
453 paths.ui_key = '/'
461 paths.ui_key = '/'
454 paths.ui_value = path
462 paths.ui_value = path
455
463
456 sett1 = RhodeCodeSetting('realm', 'RhodeCode authentication')
464 sett1 = RhodeCodeSetting('realm', 'RhodeCode authentication')
457 sett2 = RhodeCodeSetting('title', 'RhodeCode')
465 sett2 = RhodeCodeSetting('title', 'RhodeCode')
458 sett3 = RhodeCodeSetting('ga_code', '')
466 sett3 = RhodeCodeSetting('ga_code', '')
459
467
460 sett4 = RhodeCodeSetting('show_public_icon', True)
468 sett4 = RhodeCodeSetting('show_public_icon', True)
461 sett5 = RhodeCodeSetting('show_private_icon', True)
469 sett5 = RhodeCodeSetting('show_private_icon', True)
462 sett6 = RhodeCodeSetting('stylify_metatags', False)
470 sett6 = RhodeCodeSetting('stylify_metatags', False)
463
471
464 self.sa.add(web1)
472 self.sa.add(web1)
465 self.sa.add(web2)
473 self.sa.add(web2)
466 self.sa.add(web3)
474 self.sa.add(web3)
467 self.sa.add(web4)
475 self.sa.add(web4)
468 self.sa.add(paths)
476 self.sa.add(paths)
469 self.sa.add(sett1)
477 self.sa.add(sett1)
470 self.sa.add(sett2)
478 self.sa.add(sett2)
471 self.sa.add(sett3)
479 self.sa.add(sett3)
472 self.sa.add(sett4)
480 self.sa.add(sett4)
473 self.sa.add(sett5)
481 self.sa.add(sett5)
474 self.sa.add(sett6)
482 self.sa.add(sett6)
475
483
476 self.create_ldap_options()
484 self.create_ldap_options()
477
485
478 log.info('created ui config')
486 log.info('created ui config')
479
487
480 def create_user(self, username, password, email='', admin=False):
488 def create_user(self, username, password, email='', admin=False):
481 log.info('creating user %s' % username)
489 log.info('creating user %s' % username)
482 UserModel().create_or_update(username, password, email,
490 UserModel().create_or_update(username, password, email,
483 firstname='RhodeCode', lastname='Admin',
491 firstname='RhodeCode', lastname='Admin',
484 active=True, admin=admin)
492 active=True, admin=admin)
485
493
486 def create_default_user(self):
494 def create_default_user(self):
487 log.info('creating default user')
495 log.info('creating default user')
488 # create default user for handling default permissions.
496 # create default user for handling default permissions.
489 UserModel().create_or_update(username='default',
497 UserModel().create_or_update(username='default',
490 password=str(uuid.uuid1())[:8],
498 password=str(uuid.uuid1())[:8],
491 email='anonymous@rhodecode.org',
499 email='anonymous@rhodecode.org',
492 firstname='Anonymous', lastname='User')
500 firstname='Anonymous', lastname='User')
493
501
494 def create_permissions(self):
502 def create_permissions(self):
495 # module.(access|create|change|delete)_[name]
503 # module.(access|create|change|delete)_[name]
496 # module.(none|read|write|admin)
504 # module.(none|read|write|admin)
497
505
498 for p in Permission.PERMS:
506 for p in Permission.PERMS:
499 if not Permission.get_by_key(p[0]):
507 if not Permission.get_by_key(p[0]):
500 new_perm = Permission()
508 new_perm = Permission()
501 new_perm.permission_name = p[0]
509 new_perm.permission_name = p[0]
502 new_perm.permission_longname = p[0]
510 new_perm.permission_longname = p[0]
503 self.sa.add(new_perm)
511 self.sa.add(new_perm)
504
512
505 def populate_default_permissions(self):
513 def populate_default_permissions(self):
506 log.info('creating default user permissions')
514 log.info('creating default user permissions')
507
515
508 default_user = self.sa.query(User)\
516 default_user = self.sa.query(User)\
509 .filter(User.username == 'default').scalar()
517 .filter(User.username == 'default').scalar()
510
518
511 reg_perm = UserToPerm()
519 reg_perm = UserToPerm()
512 reg_perm.user = default_user
520 reg_perm.user = default_user
513 reg_perm.permission = self.sa.query(Permission)\
521 reg_perm.permission = self.sa.query(Permission)\
514 .filter(Permission.permission_name == 'hg.register.manual_activate')\
522 .filter(Permission.permission_name == 'hg.register.manual_activate')\
515 .scalar()
523 .scalar()
516
524
517 create_repo_perm = UserToPerm()
525 create_repo_perm = UserToPerm()
518 create_repo_perm.user = default_user
526 create_repo_perm.user = default_user
519 create_repo_perm.permission = self.sa.query(Permission)\
527 create_repo_perm.permission = self.sa.query(Permission)\
520 .filter(Permission.permission_name == 'hg.create.repository')\
528 .filter(Permission.permission_name == 'hg.create.repository')\
521 .scalar()
529 .scalar()
522
530
523 default_repo_perm = UserToPerm()
531 default_repo_perm = UserToPerm()
524 default_repo_perm.user = default_user
532 default_repo_perm.user = default_user
525 default_repo_perm.permission = self.sa.query(Permission)\
533 default_repo_perm.permission = self.sa.query(Permission)\
526 .filter(Permission.permission_name == 'repository.read')\
534 .filter(Permission.permission_name == 'repository.read')\
527 .scalar()
535 .scalar()
528
536
529 self.sa.add(reg_perm)
537 self.sa.add(reg_perm)
530 self.sa.add(create_repo_perm)
538 self.sa.add(create_repo_perm)
531 self.sa.add(default_repo_perm)
539 self.sa.add(default_repo_perm)
@@ -1,1683 +1,1683 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 BaseModel(object):
64 class BaseModel(object):
65 """
65 """
66 Base Model for all classess
66 Base Model for all classess
67 """
67 """
68
68
69 @classmethod
69 @classmethod
70 def _get_keys(cls):
70 def _get_keys(cls):
71 """return column names for this model """
71 """return column names for this model """
72 return class_mapper(cls).c.keys()
72 return class_mapper(cls).c.keys()
73
73
74 def get_dict(self):
74 def get_dict(self):
75 """
75 """
76 return dict with keys and values corresponding
76 return dict with keys and values corresponding
77 to this model data """
77 to this model data """
78
78
79 d = {}
79 d = {}
80 for k in self._get_keys():
80 for k in self._get_keys():
81 d[k] = getattr(self, k)
81 d[k] = getattr(self, k)
82
82
83 # also use __json__() if present to get additional fields
83 # also use __json__() if present to get additional fields
84 _json_attr = getattr(self, '__json__', None)
84 _json_attr = getattr(self, '__json__', None)
85 if _json_attr:
85 if _json_attr:
86 # update with attributes from __json__
86 # update with attributes from __json__
87 if callable(_json_attr):
87 if callable(_json_attr):
88 _json_attr = _json_attr()
88 _json_attr = _json_attr()
89 for k, val in _json_attr.iteritems():
89 for k, val in _json_attr.iteritems():
90 d[k] = val
90 d[k] = val
91 return d
91 return d
92
92
93 def get_appstruct(self):
93 def get_appstruct(self):
94 """return list with keys and values tupples corresponding
94 """return list with keys and values tupples corresponding
95 to this model data """
95 to this model data """
96
96
97 l = []
97 l = []
98 for k in self._get_keys():
98 for k in self._get_keys():
99 l.append((k, getattr(self, k),))
99 l.append((k, getattr(self, k),))
100 return l
100 return l
101
101
102 def populate_obj(self, populate_dict):
102 def populate_obj(self, populate_dict):
103 """populate model with data from given populate_dict"""
103 """populate model with data from given populate_dict"""
104
104
105 for k in self._get_keys():
105 for k in self._get_keys():
106 if k in populate_dict:
106 if k in populate_dict:
107 setattr(self, k, populate_dict[k])
107 setattr(self, k, populate_dict[k])
108
108
109 @classmethod
109 @classmethod
110 def query(cls):
110 def query(cls):
111 return Session().query(cls)
111 return Session().query(cls)
112
112
113 @classmethod
113 @classmethod
114 def get(cls, id_):
114 def get(cls, id_):
115 if id_:
115 if id_:
116 return cls.query().get(id_)
116 return cls.query().get(id_)
117
117
118 @classmethod
118 @classmethod
119 def get_or_404(cls, id_):
119 def get_or_404(cls, id_):
120 if id_:
120 if id_:
121 res = cls.query().get(id_)
121 res = cls.query().get(id_)
122 if not res:
122 if not res:
123 raise HTTPNotFound
123 raise HTTPNotFound
124 return res
124 return res
125
125
126 @classmethod
126 @classmethod
127 def getAll(cls):
127 def getAll(cls):
128 return cls.query().all()
128 return cls.query().all()
129
129
130 @classmethod
130 @classmethod
131 def delete(cls, id_):
131 def delete(cls, id_):
132 obj = cls.query().get(id_)
132 obj = cls.query().get(id_)
133 Session().delete(obj)
133 Session().delete(obj)
134
134
135 def __repr__(self):
135 def __repr__(self):
136 if hasattr(self, '__unicode__'):
136 if hasattr(self, '__unicode__'):
137 # python repr needs to return str
137 # python repr needs to return str
138 return safe_str(self.__unicode__())
138 return safe_str(self.__unicode__())
139 return '<DB:%s>' % (self.__class__.__name__)
139 return '<DB:%s>' % (self.__class__.__name__)
140
140
141
141
142 class RhodeCodeSetting(Base, BaseModel):
142 class RhodeCodeSetting(Base, BaseModel):
143 __tablename__ = 'rhodecode_settings'
143 __tablename__ = 'rhodecode_settings'
144 __table_args__ = (
144 __table_args__ = (
145 UniqueConstraint('app_settings_name'),
145 UniqueConstraint('app_settings_name'),
146 {'extend_existing': True, 'mysql_engine': 'InnoDB',
146 {'extend_existing': True, 'mysql_engine': 'InnoDB',
147 'mysql_charset': 'utf8'}
147 'mysql_charset': 'utf8'}
148 )
148 )
149 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
149 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
150 app_settings_name = Column("app_settings_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
150 app_settings_name = Column("app_settings_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
151 _app_settings_value = Column("app_settings_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
151 _app_settings_value = Column("app_settings_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
152
152
153 def __init__(self, k='', v=''):
153 def __init__(self, k='', v=''):
154 self.app_settings_name = k
154 self.app_settings_name = k
155 self.app_settings_value = v
155 self.app_settings_value = v
156
156
157 @validates('_app_settings_value')
157 @validates('_app_settings_value')
158 def validate_settings_value(self, key, val):
158 def validate_settings_value(self, key, val):
159 assert type(val) == unicode
159 assert type(val) == unicode
160 return val
160 return val
161
161
162 @hybrid_property
162 @hybrid_property
163 def app_settings_value(self):
163 def app_settings_value(self):
164 v = self._app_settings_value
164 v = self._app_settings_value
165 if self.app_settings_name == 'ldap_active':
165 if self.app_settings_name == 'ldap_active':
166 v = str2bool(v)
166 v = str2bool(v)
167 return v
167 return v
168
168
169 @app_settings_value.setter
169 @app_settings_value.setter
170 def app_settings_value(self, val):
170 def app_settings_value(self, val):
171 """
171 """
172 Setter that will always make sure we use unicode in app_settings_value
172 Setter that will always make sure we use unicode in app_settings_value
173
173
174 :param val:
174 :param val:
175 """
175 """
176 self._app_settings_value = safe_unicode(val)
176 self._app_settings_value = safe_unicode(val)
177
177
178 def __unicode__(self):
178 def __unicode__(self):
179 return u"<%s('%s:%s')>" % (
179 return u"<%s('%s:%s')>" % (
180 self.__class__.__name__,
180 self.__class__.__name__,
181 self.app_settings_name, self.app_settings_value
181 self.app_settings_name, self.app_settings_value
182 )
182 )
183
183
184 @classmethod
184 @classmethod
185 def get_by_name(cls, key):
185 def get_by_name(cls, key):
186 return cls.query()\
186 return cls.query()\
187 .filter(cls.app_settings_name == key).scalar()
187 .filter(cls.app_settings_name == key).scalar()
188
188
189 @classmethod
189 @classmethod
190 def get_by_name_or_create(cls, key):
190 def get_by_name_or_create(cls, key):
191 res = cls.get_by_name(key)
191 res = cls.get_by_name(key)
192 if not res:
192 if not res:
193 res = cls(key)
193 res = cls(key)
194 return res
194 return res
195
195
196 @classmethod
196 @classmethod
197 def get_app_settings(cls, cache=False):
197 def get_app_settings(cls, cache=False):
198
198
199 ret = cls.query()
199 ret = cls.query()
200
200
201 if cache:
201 if cache:
202 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
202 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
203
203
204 if not ret:
204 if not ret:
205 raise Exception('Could not get application settings !')
205 raise Exception('Could not get application settings !')
206 settings = {}
206 settings = {}
207 for each in ret:
207 for each in ret:
208 settings['rhodecode_' + each.app_settings_name] = \
208 settings['rhodecode_' + each.app_settings_name] = \
209 each.app_settings_value
209 each.app_settings_value
210
210
211 return settings
211 return settings
212
212
213 @classmethod
213 @classmethod
214 def get_ldap_settings(cls, cache=False):
214 def get_ldap_settings(cls, cache=False):
215 ret = cls.query()\
215 ret = cls.query()\
216 .filter(cls.app_settings_name.startswith('ldap_')).all()
216 .filter(cls.app_settings_name.startswith('ldap_')).all()
217 fd = {}
217 fd = {}
218 for row in ret:
218 for row in ret:
219 fd.update({row.app_settings_name: row.app_settings_value})
219 fd.update({row.app_settings_name: row.app_settings_value})
220
220
221 return fd
221 return fd
222
222
223
223
224 class RhodeCodeUi(Base, BaseModel):
224 class RhodeCodeUi(Base, BaseModel):
225 __tablename__ = 'rhodecode_ui'
225 __tablename__ = 'rhodecode_ui'
226 __table_args__ = (
226 __table_args__ = (
227 UniqueConstraint('ui_key'),
227 UniqueConstraint('ui_key'),
228 {'extend_existing': True, 'mysql_engine': 'InnoDB',
228 {'extend_existing': True, 'mysql_engine': 'InnoDB',
229 'mysql_charset': 'utf8'}
229 'mysql_charset': 'utf8'}
230 )
230 )
231
231
232 HOOK_UPDATE = 'changegroup.update'
232 HOOK_UPDATE = 'changegroup.update'
233 HOOK_REPO_SIZE = 'changegroup.repo_size'
233 HOOK_REPO_SIZE = 'changegroup.repo_size'
234 HOOK_PUSH = 'changegroup.push_logger'
234 HOOK_PUSH = 'changegroup.push_logger'
235 HOOK_PULL = 'preoutgoing.pull_logger'
235 HOOK_PULL = 'preoutgoing.pull_logger'
236
236
237 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
237 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
238 ui_section = Column("ui_section", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
238 ui_section = Column("ui_section", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
239 ui_key = Column("ui_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
239 ui_key = Column("ui_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
240 ui_value = Column("ui_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
240 ui_value = Column("ui_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
241 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
241 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
242
242
243 @classmethod
243 @classmethod
244 def get_by_key(cls, key):
244 def get_by_key(cls, key):
245 return cls.query().filter(cls.ui_key == key)
245 return cls.query().filter(cls.ui_key == key).scalar()
246
246
247 @classmethod
247 @classmethod
248 def get_builtin_hooks(cls):
248 def get_builtin_hooks(cls):
249 q = cls.query()
249 q = cls.query()
250 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
250 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
251 cls.HOOK_REPO_SIZE,
251 cls.HOOK_REPO_SIZE,
252 cls.HOOK_PUSH, cls.HOOK_PULL]))
252 cls.HOOK_PUSH, cls.HOOK_PULL]))
253 return q.all()
253 return q.all()
254
254
255 @classmethod
255 @classmethod
256 def get_custom_hooks(cls):
256 def get_custom_hooks(cls):
257 q = cls.query()
257 q = cls.query()
258 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
258 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
259 cls.HOOK_REPO_SIZE,
259 cls.HOOK_REPO_SIZE,
260 cls.HOOK_PUSH, cls.HOOK_PULL]))
260 cls.HOOK_PUSH, cls.HOOK_PULL]))
261 q = q.filter(cls.ui_section == 'hooks')
261 q = q.filter(cls.ui_section == 'hooks')
262 return q.all()
262 return q.all()
263
263
264 @classmethod
264 @classmethod
265 def get_repos_location(cls):
265 def get_repos_location(cls):
266 return cls.get_by_key('/').one().ui_value
266 return cls.get_by_key('/').ui_value
267
267
268 @classmethod
268 @classmethod
269 def create_or_update_hook(cls, key, val):
269 def create_or_update_hook(cls, key, val):
270 new_ui = cls.get_by_key(key).scalar() or cls()
270 new_ui = cls.get_by_key(key) or cls()
271 new_ui.ui_section = 'hooks'
271 new_ui.ui_section = 'hooks'
272 new_ui.ui_active = True
272 new_ui.ui_active = True
273 new_ui.ui_key = key
273 new_ui.ui_key = key
274 new_ui.ui_value = val
274 new_ui.ui_value = val
275
275
276 Session().add(new_ui)
276 Session().add(new_ui)
277
277
278
278
279 class User(Base, BaseModel):
279 class User(Base, BaseModel):
280 __tablename__ = 'users'
280 __tablename__ = 'users'
281 __table_args__ = (
281 __table_args__ = (
282 UniqueConstraint('username'), UniqueConstraint('email'),
282 UniqueConstraint('username'), UniqueConstraint('email'),
283 {'extend_existing': True, 'mysql_engine': 'InnoDB',
283 {'extend_existing': True, 'mysql_engine': 'InnoDB',
284 'mysql_charset': 'utf8'}
284 'mysql_charset': 'utf8'}
285 )
285 )
286 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
286 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
287 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
287 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
288 password = Column("password", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
288 password = Column("password", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
289 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
289 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
290 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
290 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
291 name = Column("firstname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
291 name = Column("firstname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
292 lastname = Column("lastname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
292 lastname = Column("lastname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
293 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
293 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
294 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
294 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
295 ldap_dn = Column("ldap_dn", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
295 ldap_dn = Column("ldap_dn", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
296 api_key = Column("api_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
296 api_key = Column("api_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
297
297
298 user_log = relationship('UserLog', cascade='all')
298 user_log = relationship('UserLog', cascade='all')
299 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
299 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
300
300
301 repositories = relationship('Repository')
301 repositories = relationship('Repository')
302 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
302 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
303 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
303 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
304 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
304 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
305
305
306 group_member = relationship('UsersGroupMember', cascade='all')
306 group_member = relationship('UsersGroupMember', cascade='all')
307
307
308 notifications = relationship('UserNotification', cascade='all')
308 notifications = relationship('UserNotification', cascade='all')
309 # notifications assigned to this user
309 # notifications assigned to this user
310 user_created_notifications = relationship('Notification', cascade='all')
310 user_created_notifications = relationship('Notification', cascade='all')
311 # comments created by this user
311 # comments created by this user
312 user_comments = relationship('ChangesetComment', cascade='all')
312 user_comments = relationship('ChangesetComment', cascade='all')
313 #extra emails for this user
313 #extra emails for this user
314 user_emails = relationship('UserEmailMap', cascade='all')
314 user_emails = relationship('UserEmailMap', cascade='all')
315
315
316 @hybrid_property
316 @hybrid_property
317 def email(self):
317 def email(self):
318 return self._email
318 return self._email
319
319
320 @email.setter
320 @email.setter
321 def email(self, val):
321 def email(self, val):
322 self._email = val.lower() if val else None
322 self._email = val.lower() if val else None
323
323
324 @property
324 @property
325 def emails(self):
325 def emails(self):
326 other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
326 other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
327 return [self.email] + [x.email for x in other]
327 return [self.email] + [x.email for x in other]
328
328
329 @property
329 @property
330 def full_name(self):
330 def full_name(self):
331 return '%s %s' % (self.name, self.lastname)
331 return '%s %s' % (self.name, self.lastname)
332
332
333 @property
333 @property
334 def full_name_or_username(self):
334 def full_name_or_username(self):
335 return ('%s %s' % (self.name, self.lastname)
335 return ('%s %s' % (self.name, self.lastname)
336 if (self.name and self.lastname) else self.username)
336 if (self.name and self.lastname) else self.username)
337
337
338 @property
338 @property
339 def full_contact(self):
339 def full_contact(self):
340 return '%s %s <%s>' % (self.name, self.lastname, self.email)
340 return '%s %s <%s>' % (self.name, self.lastname, self.email)
341
341
342 @property
342 @property
343 def short_contact(self):
343 def short_contact(self):
344 return '%s %s' % (self.name, self.lastname)
344 return '%s %s' % (self.name, self.lastname)
345
345
346 @property
346 @property
347 def is_admin(self):
347 def is_admin(self):
348 return self.admin
348 return self.admin
349
349
350 def __unicode__(self):
350 def __unicode__(self):
351 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
351 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
352 self.user_id, self.username)
352 self.user_id, self.username)
353
353
354 @classmethod
354 @classmethod
355 def get_by_username(cls, username, case_insensitive=False, cache=False):
355 def get_by_username(cls, username, case_insensitive=False, cache=False):
356 if case_insensitive:
356 if case_insensitive:
357 q = cls.query().filter(cls.username.ilike(username))
357 q = cls.query().filter(cls.username.ilike(username))
358 else:
358 else:
359 q = cls.query().filter(cls.username == username)
359 q = cls.query().filter(cls.username == username)
360
360
361 if cache:
361 if cache:
362 q = q.options(FromCache(
362 q = q.options(FromCache(
363 "sql_cache_short",
363 "sql_cache_short",
364 "get_user_%s" % _hash_key(username)
364 "get_user_%s" % _hash_key(username)
365 )
365 )
366 )
366 )
367 return q.scalar()
367 return q.scalar()
368
368
369 @classmethod
369 @classmethod
370 def get_by_api_key(cls, api_key, cache=False):
370 def get_by_api_key(cls, api_key, cache=False):
371 q = cls.query().filter(cls.api_key == api_key)
371 q = cls.query().filter(cls.api_key == api_key)
372
372
373 if cache:
373 if cache:
374 q = q.options(FromCache("sql_cache_short",
374 q = q.options(FromCache("sql_cache_short",
375 "get_api_key_%s" % api_key))
375 "get_api_key_%s" % api_key))
376 return q.scalar()
376 return q.scalar()
377
377
378 @classmethod
378 @classmethod
379 def get_by_email(cls, email, case_insensitive=False, cache=False):
379 def get_by_email(cls, email, case_insensitive=False, cache=False):
380 if case_insensitive:
380 if case_insensitive:
381 q = cls.query().filter(cls.email.ilike(email))
381 q = cls.query().filter(cls.email.ilike(email))
382 else:
382 else:
383 q = cls.query().filter(cls.email == email)
383 q = cls.query().filter(cls.email == email)
384
384
385 if cache:
385 if cache:
386 q = q.options(FromCache("sql_cache_short",
386 q = q.options(FromCache("sql_cache_short",
387 "get_email_key_%s" % email))
387 "get_email_key_%s" % email))
388
388
389 ret = q.scalar()
389 ret = q.scalar()
390 if ret is None:
390 if ret is None:
391 q = UserEmailMap.query()
391 q = UserEmailMap.query()
392 # try fetching in alternate email map
392 # try fetching in alternate email map
393 if case_insensitive:
393 if case_insensitive:
394 q = q.filter(UserEmailMap.email.ilike(email))
394 q = q.filter(UserEmailMap.email.ilike(email))
395 else:
395 else:
396 q = q.filter(UserEmailMap.email == email)
396 q = q.filter(UserEmailMap.email == email)
397 q = q.options(joinedload(UserEmailMap.user))
397 q = q.options(joinedload(UserEmailMap.user))
398 if cache:
398 if cache:
399 q = q.options(FromCache("sql_cache_short",
399 q = q.options(FromCache("sql_cache_short",
400 "get_email_map_key_%s" % email))
400 "get_email_map_key_%s" % email))
401 ret = getattr(q.scalar(), 'user', None)
401 ret = getattr(q.scalar(), 'user', None)
402
402
403 return ret
403 return ret
404
404
405 def update_lastlogin(self):
405 def update_lastlogin(self):
406 """Update user lastlogin"""
406 """Update user lastlogin"""
407 self.last_login = datetime.datetime.now()
407 self.last_login = datetime.datetime.now()
408 Session().add(self)
408 Session().add(self)
409 log.debug('updated user %s lastlogin' % self.username)
409 log.debug('updated user %s lastlogin' % self.username)
410
410
411 def get_api_data(self):
411 def get_api_data(self):
412 """
412 """
413 Common function for generating user related data for API
413 Common function for generating user related data for API
414 """
414 """
415 user = self
415 user = self
416 data = dict(
416 data = dict(
417 user_id=user.user_id,
417 user_id=user.user_id,
418 username=user.username,
418 username=user.username,
419 firstname=user.name,
419 firstname=user.name,
420 lastname=user.lastname,
420 lastname=user.lastname,
421 email=user.email,
421 email=user.email,
422 emails=user.emails,
422 emails=user.emails,
423 api_key=user.api_key,
423 api_key=user.api_key,
424 active=user.active,
424 active=user.active,
425 admin=user.admin,
425 admin=user.admin,
426 ldap_dn=user.ldap_dn,
426 ldap_dn=user.ldap_dn,
427 last_login=user.last_login,
427 last_login=user.last_login,
428 )
428 )
429 return data
429 return data
430
430
431 def __json__(self):
431 def __json__(self):
432 data = dict(
432 data = dict(
433 full_name=self.full_name,
433 full_name=self.full_name,
434 full_name_or_username=self.full_name_or_username,
434 full_name_or_username=self.full_name_or_username,
435 short_contact=self.short_contact,
435 short_contact=self.short_contact,
436 full_contact=self.full_contact
436 full_contact=self.full_contact
437 )
437 )
438 data.update(self.get_api_data())
438 data.update(self.get_api_data())
439 return data
439 return data
440
440
441
441
442 class UserEmailMap(Base, BaseModel):
442 class UserEmailMap(Base, BaseModel):
443 __tablename__ = 'user_email_map'
443 __tablename__ = 'user_email_map'
444 __table_args__ = (
444 __table_args__ = (
445 Index('uem_email_idx', 'email'),
445 Index('uem_email_idx', 'email'),
446 UniqueConstraint('email'),
446 UniqueConstraint('email'),
447 {'extend_existing': True, 'mysql_engine': 'InnoDB',
447 {'extend_existing': True, 'mysql_engine': 'InnoDB',
448 'mysql_charset': 'utf8'}
448 'mysql_charset': 'utf8'}
449 )
449 )
450 __mapper_args__ = {}
450 __mapper_args__ = {}
451
451
452 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
452 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
453 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
453 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
454 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
454 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
455
455
456 user = relationship('User', lazy='joined')
456 user = relationship('User', lazy='joined')
457
457
458 @validates('_email')
458 @validates('_email')
459 def validate_email(self, key, email):
459 def validate_email(self, key, email):
460 # check if this email is not main one
460 # check if this email is not main one
461 main_email = Session().query(User).filter(User.email == email).scalar()
461 main_email = Session().query(User).filter(User.email == email).scalar()
462 if main_email is not None:
462 if main_email is not None:
463 raise AttributeError('email %s is present is user table' % email)
463 raise AttributeError('email %s is present is user table' % email)
464 return email
464 return email
465
465
466 @hybrid_property
466 @hybrid_property
467 def email(self):
467 def email(self):
468 return self._email
468 return self._email
469
469
470 @email.setter
470 @email.setter
471 def email(self, val):
471 def email(self, val):
472 self._email = val.lower() if val else None
472 self._email = val.lower() if val else None
473
473
474
474
475 class UserLog(Base, BaseModel):
475 class UserLog(Base, BaseModel):
476 __tablename__ = 'user_logs'
476 __tablename__ = 'user_logs'
477 __table_args__ = (
477 __table_args__ = (
478 {'extend_existing': True, 'mysql_engine': 'InnoDB',
478 {'extend_existing': True, 'mysql_engine': 'InnoDB',
479 'mysql_charset': 'utf8'},
479 'mysql_charset': 'utf8'},
480 )
480 )
481 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
481 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
482 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
482 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
483 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
483 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
484 repository_name = Column("repository_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
484 repository_name = Column("repository_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
485 user_ip = Column("user_ip", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
485 user_ip = Column("user_ip", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
486 action = Column("action", UnicodeText(1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
486 action = Column("action", UnicodeText(1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
487 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
487 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
488
488
489 @property
489 @property
490 def action_as_day(self):
490 def action_as_day(self):
491 return datetime.date(*self.action_date.timetuple()[:3])
491 return datetime.date(*self.action_date.timetuple()[:3])
492
492
493 user = relationship('User')
493 user = relationship('User')
494 repository = relationship('Repository', cascade='')
494 repository = relationship('Repository', cascade='')
495
495
496
496
497 class UsersGroup(Base, BaseModel):
497 class UsersGroup(Base, BaseModel):
498 __tablename__ = 'users_groups'
498 __tablename__ = 'users_groups'
499 __table_args__ = (
499 __table_args__ = (
500 {'extend_existing': True, 'mysql_engine': 'InnoDB',
500 {'extend_existing': True, 'mysql_engine': 'InnoDB',
501 'mysql_charset': 'utf8'},
501 'mysql_charset': 'utf8'},
502 )
502 )
503
503
504 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
504 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
505 users_group_name = Column("users_group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
505 users_group_name = Column("users_group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
506 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
506 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
507
507
508 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
508 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
509 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
509 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
510 users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
510 users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
511
511
512 def __unicode__(self):
512 def __unicode__(self):
513 return u'<userGroup(%s)>' % (self.users_group_name)
513 return u'<userGroup(%s)>' % (self.users_group_name)
514
514
515 @classmethod
515 @classmethod
516 def get_by_group_name(cls, group_name, cache=False,
516 def get_by_group_name(cls, group_name, cache=False,
517 case_insensitive=False):
517 case_insensitive=False):
518 if case_insensitive:
518 if case_insensitive:
519 q = cls.query().filter(cls.users_group_name.ilike(group_name))
519 q = cls.query().filter(cls.users_group_name.ilike(group_name))
520 else:
520 else:
521 q = cls.query().filter(cls.users_group_name == group_name)
521 q = cls.query().filter(cls.users_group_name == group_name)
522 if cache:
522 if cache:
523 q = q.options(FromCache(
523 q = q.options(FromCache(
524 "sql_cache_short",
524 "sql_cache_short",
525 "get_user_%s" % _hash_key(group_name)
525 "get_user_%s" % _hash_key(group_name)
526 )
526 )
527 )
527 )
528 return q.scalar()
528 return q.scalar()
529
529
530 @classmethod
530 @classmethod
531 def get(cls, users_group_id, cache=False):
531 def get(cls, users_group_id, cache=False):
532 users_group = cls.query()
532 users_group = cls.query()
533 if cache:
533 if cache:
534 users_group = users_group.options(FromCache("sql_cache_short",
534 users_group = users_group.options(FromCache("sql_cache_short",
535 "get_users_group_%s" % users_group_id))
535 "get_users_group_%s" % users_group_id))
536 return users_group.get(users_group_id)
536 return users_group.get(users_group_id)
537
537
538 def get_api_data(self):
538 def get_api_data(self):
539 users_group = self
539 users_group = self
540
540
541 data = dict(
541 data = dict(
542 users_group_id=users_group.users_group_id,
542 users_group_id=users_group.users_group_id,
543 group_name=users_group.users_group_name,
543 group_name=users_group.users_group_name,
544 active=users_group.users_group_active,
544 active=users_group.users_group_active,
545 )
545 )
546
546
547 return data
547 return data
548
548
549
549
550 class UsersGroupMember(Base, BaseModel):
550 class UsersGroupMember(Base, BaseModel):
551 __tablename__ = 'users_groups_members'
551 __tablename__ = 'users_groups_members'
552 __table_args__ = (
552 __table_args__ = (
553 {'extend_existing': True, 'mysql_engine': 'InnoDB',
553 {'extend_existing': True, 'mysql_engine': 'InnoDB',
554 'mysql_charset': 'utf8'},
554 'mysql_charset': 'utf8'},
555 )
555 )
556
556
557 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
557 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
558 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
558 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
559 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
559 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
560
560
561 user = relationship('User', lazy='joined')
561 user = relationship('User', lazy='joined')
562 users_group = relationship('UsersGroup')
562 users_group = relationship('UsersGroup')
563
563
564 def __init__(self, gr_id='', u_id=''):
564 def __init__(self, gr_id='', u_id=''):
565 self.users_group_id = gr_id
565 self.users_group_id = gr_id
566 self.user_id = u_id
566 self.user_id = u_id
567
567
568
568
569 class Repository(Base, BaseModel):
569 class Repository(Base, BaseModel):
570 __tablename__ = 'repositories'
570 __tablename__ = 'repositories'
571 __table_args__ = (
571 __table_args__ = (
572 UniqueConstraint('repo_name'),
572 UniqueConstraint('repo_name'),
573 {'extend_existing': True, 'mysql_engine': 'InnoDB',
573 {'extend_existing': True, 'mysql_engine': 'InnoDB',
574 'mysql_charset': 'utf8'},
574 'mysql_charset': 'utf8'},
575 )
575 )
576
576
577 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
577 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
578 repo_name = Column("repo_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
578 repo_name = Column("repo_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
579 clone_uri = Column("clone_uri", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
579 clone_uri = Column("clone_uri", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
580 repo_type = Column("repo_type", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
580 repo_type = Column("repo_type", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
581 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
581 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
582 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
582 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
583 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
583 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
584 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
584 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
585 description = Column("description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
585 description = Column("description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
586 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
586 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
587 landing_rev = Column("landing_revision", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
587 landing_rev = Column("landing_revision", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
588
588
589 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
589 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
590 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
590 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
591
591
592 user = relationship('User')
592 user = relationship('User')
593 fork = relationship('Repository', remote_side=repo_id)
593 fork = relationship('Repository', remote_side=repo_id)
594 group = relationship('RepoGroup')
594 group = relationship('RepoGroup')
595 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
595 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
596 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
596 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
597 stats = relationship('Statistics', cascade='all', uselist=False)
597 stats = relationship('Statistics', cascade='all', uselist=False)
598
598
599 followers = relationship('UserFollowing',
599 followers = relationship('UserFollowing',
600 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
600 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
601 cascade='all')
601 cascade='all')
602
602
603 logs = relationship('UserLog')
603 logs = relationship('UserLog')
604 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
604 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
605
605
606 pull_requests_org = relationship('PullRequest',
606 pull_requests_org = relationship('PullRequest',
607 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
607 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
608 cascade="all, delete, delete-orphan")
608 cascade="all, delete, delete-orphan")
609
609
610 pull_requests_other = relationship('PullRequest',
610 pull_requests_other = relationship('PullRequest',
611 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
611 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
612 cascade="all, delete, delete-orphan")
612 cascade="all, delete, delete-orphan")
613
613
614 def __unicode__(self):
614 def __unicode__(self):
615 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
615 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
616 self.repo_name)
616 self.repo_name)
617
617
618 @classmethod
618 @classmethod
619 def url_sep(cls):
619 def url_sep(cls):
620 return URL_SEP
620 return URL_SEP
621
621
622 @classmethod
622 @classmethod
623 def get_by_repo_name(cls, repo_name):
623 def get_by_repo_name(cls, repo_name):
624 q = Session().query(cls).filter(cls.repo_name == repo_name)
624 q = Session().query(cls).filter(cls.repo_name == repo_name)
625 q = q.options(joinedload(Repository.fork))\
625 q = q.options(joinedload(Repository.fork))\
626 .options(joinedload(Repository.user))\
626 .options(joinedload(Repository.user))\
627 .options(joinedload(Repository.group))
627 .options(joinedload(Repository.group))
628 return q.scalar()
628 return q.scalar()
629
629
630 @classmethod
630 @classmethod
631 def get_by_full_path(cls, repo_full_path):
631 def get_by_full_path(cls, repo_full_path):
632 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
632 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
633 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
633 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
634
634
635 @classmethod
635 @classmethod
636 def get_repo_forks(cls, repo_id):
636 def get_repo_forks(cls, repo_id):
637 return cls.query().filter(Repository.fork_id == repo_id)
637 return cls.query().filter(Repository.fork_id == repo_id)
638
638
639 @classmethod
639 @classmethod
640 def base_path(cls):
640 def base_path(cls):
641 """
641 """
642 Returns base path when all repos are stored
642 Returns base path when all repos are stored
643
643
644 :param cls:
644 :param cls:
645 """
645 """
646 q = Session().query(RhodeCodeUi)\
646 q = Session().query(RhodeCodeUi)\
647 .filter(RhodeCodeUi.ui_key == cls.url_sep())
647 .filter(RhodeCodeUi.ui_key == cls.url_sep())
648 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
648 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
649 return q.one().ui_value
649 return q.one().ui_value
650
650
651 @property
651 @property
652 def forks(self):
652 def forks(self):
653 """
653 """
654 Return forks of this repo
654 Return forks of this repo
655 """
655 """
656 return Repository.get_repo_forks(self.repo_id)
656 return Repository.get_repo_forks(self.repo_id)
657
657
658 @property
658 @property
659 def parent(self):
659 def parent(self):
660 """
660 """
661 Returns fork parent
661 Returns fork parent
662 """
662 """
663 return self.fork
663 return self.fork
664
664
665 @property
665 @property
666 def just_name(self):
666 def just_name(self):
667 return self.repo_name.split(Repository.url_sep())[-1]
667 return self.repo_name.split(Repository.url_sep())[-1]
668
668
669 @property
669 @property
670 def groups_with_parents(self):
670 def groups_with_parents(self):
671 groups = []
671 groups = []
672 if self.group is None:
672 if self.group is None:
673 return groups
673 return groups
674
674
675 cur_gr = self.group
675 cur_gr = self.group
676 groups.insert(0, cur_gr)
676 groups.insert(0, cur_gr)
677 while 1:
677 while 1:
678 gr = getattr(cur_gr, 'parent_group', None)
678 gr = getattr(cur_gr, 'parent_group', None)
679 cur_gr = cur_gr.parent_group
679 cur_gr = cur_gr.parent_group
680 if gr is None:
680 if gr is None:
681 break
681 break
682 groups.insert(0, gr)
682 groups.insert(0, gr)
683
683
684 return groups
684 return groups
685
685
686 @property
686 @property
687 def groups_and_repo(self):
687 def groups_and_repo(self):
688 return self.groups_with_parents, self.just_name
688 return self.groups_with_parents, self.just_name
689
689
690 @LazyProperty
690 @LazyProperty
691 def repo_path(self):
691 def repo_path(self):
692 """
692 """
693 Returns base full path for that repository means where it actually
693 Returns base full path for that repository means where it actually
694 exists on a filesystem
694 exists on a filesystem
695 """
695 """
696 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
696 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
697 Repository.url_sep())
697 Repository.url_sep())
698 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
698 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
699 return q.one().ui_value
699 return q.one().ui_value
700
700
701 @property
701 @property
702 def repo_full_path(self):
702 def repo_full_path(self):
703 p = [self.repo_path]
703 p = [self.repo_path]
704 # we need to split the name by / since this is how we store the
704 # we need to split the name by / since this is how we store the
705 # names in the database, but that eventually needs to be converted
705 # names in the database, but that eventually needs to be converted
706 # into a valid system path
706 # into a valid system path
707 p += self.repo_name.split(Repository.url_sep())
707 p += self.repo_name.split(Repository.url_sep())
708 return os.path.join(*p)
708 return os.path.join(*p)
709
709
710 def get_new_name(self, repo_name):
710 def get_new_name(self, repo_name):
711 """
711 """
712 returns new full repository name based on assigned group and new new
712 returns new full repository name based on assigned group and new new
713
713
714 :param group_name:
714 :param group_name:
715 """
715 """
716 path_prefix = self.group.full_path_splitted if self.group else []
716 path_prefix = self.group.full_path_splitted if self.group else []
717 return Repository.url_sep().join(path_prefix + [repo_name])
717 return Repository.url_sep().join(path_prefix + [repo_name])
718
718
719 @property
719 @property
720 def _ui(self):
720 def _ui(self):
721 """
721 """
722 Creates an db based ui object for this repository
722 Creates an db based ui object for this repository
723 """
723 """
724 from mercurial import ui
724 from mercurial import ui
725 from mercurial import config
725 from mercurial import config
726 baseui = ui.ui()
726 baseui = ui.ui()
727
727
728 #clean the baseui object
728 #clean the baseui object
729 baseui._ocfg = config.config()
729 baseui._ocfg = config.config()
730 baseui._ucfg = config.config()
730 baseui._ucfg = config.config()
731 baseui._tcfg = config.config()
731 baseui._tcfg = config.config()
732
732
733 ret = RhodeCodeUi.query()\
733 ret = RhodeCodeUi.query()\
734 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
734 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
735
735
736 hg_ui = ret
736 hg_ui = ret
737 for ui_ in hg_ui:
737 for ui_ in hg_ui:
738 if ui_.ui_active:
738 if ui_.ui_active:
739 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
739 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
740 ui_.ui_key, ui_.ui_value)
740 ui_.ui_key, ui_.ui_value)
741 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
741 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
742 if ui_.ui_key == 'push_ssl':
742 if ui_.ui_key == 'push_ssl':
743 # force set push_ssl requirement to False, rhodecode
743 # force set push_ssl requirement to False, rhodecode
744 # handles that
744 # handles that
745 baseui.setconfig(ui_.ui_section, ui_.ui_key, False)
745 baseui.setconfig(ui_.ui_section, ui_.ui_key, False)
746
746
747 return baseui
747 return baseui
748
748
749 @classmethod
749 @classmethod
750 def inject_ui(cls, repo, extras={}):
750 def inject_ui(cls, repo, extras={}):
751 from rhodecode.lib.vcs.backends.hg import MercurialRepository
751 from rhodecode.lib.vcs.backends.hg import MercurialRepository
752 from rhodecode.lib.vcs.backends.git import GitRepository
752 from rhodecode.lib.vcs.backends.git import GitRepository
753 required = (MercurialRepository, GitRepository)
753 required = (MercurialRepository, GitRepository)
754 if not isinstance(repo, required):
754 if not isinstance(repo, required):
755 raise Exception('repo must be instance of %s' % required)
755 raise Exception('repo must be instance of %s' % required)
756
756
757 # inject ui extra param to log this action via push logger
757 # inject ui extra param to log this action via push logger
758 for k, v in extras.items():
758 for k, v in extras.items():
759 repo._repo.ui.setconfig('rhodecode_extras', k, v)
759 repo._repo.ui.setconfig('rhodecode_extras', k, v)
760
760
761 @classmethod
761 @classmethod
762 def is_valid(cls, repo_name):
762 def is_valid(cls, repo_name):
763 """
763 """
764 returns True if given repo name is a valid filesystem repository
764 returns True if given repo name is a valid filesystem repository
765
765
766 :param cls:
766 :param cls:
767 :param repo_name:
767 :param repo_name:
768 """
768 """
769 from rhodecode.lib.utils import is_valid_repo
769 from rhodecode.lib.utils import is_valid_repo
770
770
771 return is_valid_repo(repo_name, cls.base_path())
771 return is_valid_repo(repo_name, cls.base_path())
772
772
773 def get_api_data(self):
773 def get_api_data(self):
774 """
774 """
775 Common function for generating repo api data
775 Common function for generating repo api data
776
776
777 """
777 """
778 repo = self
778 repo = self
779 data = dict(
779 data = dict(
780 repo_id=repo.repo_id,
780 repo_id=repo.repo_id,
781 repo_name=repo.repo_name,
781 repo_name=repo.repo_name,
782 repo_type=repo.repo_type,
782 repo_type=repo.repo_type,
783 clone_uri=repo.clone_uri,
783 clone_uri=repo.clone_uri,
784 private=repo.private,
784 private=repo.private,
785 created_on=repo.created_on,
785 created_on=repo.created_on,
786 description=repo.description,
786 description=repo.description,
787 landing_rev=repo.landing_rev,
787 landing_rev=repo.landing_rev,
788 owner=repo.user.username,
788 owner=repo.user.username,
789 fork_of=repo.fork.repo_name if repo.fork else None
789 fork_of=repo.fork.repo_name if repo.fork else None
790 )
790 )
791
791
792 return data
792 return data
793
793
794 #==========================================================================
794 #==========================================================================
795 # SCM PROPERTIES
795 # SCM PROPERTIES
796 #==========================================================================
796 #==========================================================================
797
797
798 def get_changeset(self, rev=None):
798 def get_changeset(self, rev=None):
799 return get_changeset_safe(self.scm_instance, rev)
799 return get_changeset_safe(self.scm_instance, rev)
800
800
801 def get_landing_changeset(self):
801 def get_landing_changeset(self):
802 """
802 """
803 Returns landing changeset, or if that doesn't exist returns the tip
803 Returns landing changeset, or if that doesn't exist returns the tip
804 """
804 """
805 cs = self.get_changeset(self.landing_rev) or self.get_changeset()
805 cs = self.get_changeset(self.landing_rev) or self.get_changeset()
806 return cs
806 return cs
807
807
808 @property
808 @property
809 def tip(self):
809 def tip(self):
810 return self.get_changeset('tip')
810 return self.get_changeset('tip')
811
811
812 @property
812 @property
813 def author(self):
813 def author(self):
814 return self.tip.author
814 return self.tip.author
815
815
816 @property
816 @property
817 def last_change(self):
817 def last_change(self):
818 return self.scm_instance.last_change
818 return self.scm_instance.last_change
819
819
820 def get_comments(self, revisions=None):
820 def get_comments(self, revisions=None):
821 """
821 """
822 Returns comments for this repository grouped by revisions
822 Returns comments for this repository grouped by revisions
823
823
824 :param revisions: filter query by revisions only
824 :param revisions: filter query by revisions only
825 """
825 """
826 cmts = ChangesetComment.query()\
826 cmts = ChangesetComment.query()\
827 .filter(ChangesetComment.repo == self)
827 .filter(ChangesetComment.repo == self)
828 if revisions:
828 if revisions:
829 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
829 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
830 grouped = defaultdict(list)
830 grouped = defaultdict(list)
831 for cmt in cmts.all():
831 for cmt in cmts.all():
832 grouped[cmt.revision].append(cmt)
832 grouped[cmt.revision].append(cmt)
833 return grouped
833 return grouped
834
834
835 def statuses(self, revisions=None):
835 def statuses(self, revisions=None):
836 """
836 """
837 Returns statuses for this repository
837 Returns statuses for this repository
838
838
839 :param revisions: list of revisions to get statuses for
839 :param revisions: list of revisions to get statuses for
840 :type revisions: list
840 :type revisions: list
841 """
841 """
842
842
843 statuses = ChangesetStatus.query()\
843 statuses = ChangesetStatus.query()\
844 .filter(ChangesetStatus.repo == self)\
844 .filter(ChangesetStatus.repo == self)\
845 .filter(ChangesetStatus.version == 0)
845 .filter(ChangesetStatus.version == 0)
846 if revisions:
846 if revisions:
847 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
847 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
848 grouped = {}
848 grouped = {}
849
849
850 #maybe we have open new pullrequest without a status ?
850 #maybe we have open new pullrequest without a status ?
851 stat = ChangesetStatus.STATUS_UNDER_REVIEW
851 stat = ChangesetStatus.STATUS_UNDER_REVIEW
852 status_lbl = ChangesetStatus.get_status_lbl(stat)
852 status_lbl = ChangesetStatus.get_status_lbl(stat)
853 for pr in PullRequest.query().filter(PullRequest.org_repo == self).all():
853 for pr in PullRequest.query().filter(PullRequest.org_repo == self).all():
854 for rev in pr.revisions:
854 for rev in pr.revisions:
855 pr_id = pr.pull_request_id
855 pr_id = pr.pull_request_id
856 pr_repo = pr.other_repo.repo_name
856 pr_repo = pr.other_repo.repo_name
857 grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
857 grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
858
858
859 for stat in statuses.all():
859 for stat in statuses.all():
860 pr_id = pr_repo = None
860 pr_id = pr_repo = None
861 if stat.pull_request:
861 if stat.pull_request:
862 pr_id = stat.pull_request.pull_request_id
862 pr_id = stat.pull_request.pull_request_id
863 pr_repo = stat.pull_request.other_repo.repo_name
863 pr_repo = stat.pull_request.other_repo.repo_name
864 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
864 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
865 pr_id, pr_repo]
865 pr_id, pr_repo]
866 return grouped
866 return grouped
867
867
868 #==========================================================================
868 #==========================================================================
869 # SCM CACHE INSTANCE
869 # SCM CACHE INSTANCE
870 #==========================================================================
870 #==========================================================================
871
871
872 @property
872 @property
873 def invalidate(self):
873 def invalidate(self):
874 return CacheInvalidation.invalidate(self.repo_name)
874 return CacheInvalidation.invalidate(self.repo_name)
875
875
876 def set_invalidate(self):
876 def set_invalidate(self):
877 """
877 """
878 set a cache for invalidation for this instance
878 set a cache for invalidation for this instance
879 """
879 """
880 CacheInvalidation.set_invalidate(self.repo_name)
880 CacheInvalidation.set_invalidate(self.repo_name)
881
881
882 @LazyProperty
882 @LazyProperty
883 def scm_instance(self):
883 def scm_instance(self):
884 return self.__get_instance()
884 return self.__get_instance()
885
885
886 def scm_instance_cached(self, cache_map=None):
886 def scm_instance_cached(self, cache_map=None):
887 @cache_region('long_term')
887 @cache_region('long_term')
888 def _c(repo_name):
888 def _c(repo_name):
889 return self.__get_instance()
889 return self.__get_instance()
890 rn = self.repo_name
890 rn = self.repo_name
891 log.debug('Getting cached instance of repo')
891 log.debug('Getting cached instance of repo')
892
892
893 if cache_map:
893 if cache_map:
894 # get using prefilled cache_map
894 # get using prefilled cache_map
895 invalidate_repo = cache_map[self.repo_name]
895 invalidate_repo = cache_map[self.repo_name]
896 if invalidate_repo:
896 if invalidate_repo:
897 invalidate_repo = (None if invalidate_repo.cache_active
897 invalidate_repo = (None if invalidate_repo.cache_active
898 else invalidate_repo)
898 else invalidate_repo)
899 else:
899 else:
900 # get from invalidate
900 # get from invalidate
901 invalidate_repo = self.invalidate
901 invalidate_repo = self.invalidate
902
902
903 if invalidate_repo is not None:
903 if invalidate_repo is not None:
904 region_invalidate(_c, None, rn)
904 region_invalidate(_c, None, rn)
905 # update our cache
905 # update our cache
906 CacheInvalidation.set_valid(invalidate_repo.cache_key)
906 CacheInvalidation.set_valid(invalidate_repo.cache_key)
907 return _c(rn)
907 return _c(rn)
908
908
909 def __get_instance(self):
909 def __get_instance(self):
910 repo_full_path = self.repo_full_path
910 repo_full_path = self.repo_full_path
911 try:
911 try:
912 alias = get_scm(repo_full_path)[0]
912 alias = get_scm(repo_full_path)[0]
913 log.debug('Creating instance of %s repository' % alias)
913 log.debug('Creating instance of %s repository' % alias)
914 backend = get_backend(alias)
914 backend = get_backend(alias)
915 except VCSError:
915 except VCSError:
916 log.error(traceback.format_exc())
916 log.error(traceback.format_exc())
917 log.error('Perhaps this repository is in db and not in '
917 log.error('Perhaps this repository is in db and not in '
918 'filesystem run rescan repositories with '
918 'filesystem run rescan repositories with '
919 '"destroy old data " option from admin panel')
919 '"destroy old data " option from admin panel')
920 return
920 return
921
921
922 if alias == 'hg':
922 if alias == 'hg':
923
923
924 repo = backend(safe_str(repo_full_path), create=False,
924 repo = backend(safe_str(repo_full_path), create=False,
925 baseui=self._ui)
925 baseui=self._ui)
926 # skip hidden web repository
926 # skip hidden web repository
927 if repo._get_hidden():
927 if repo._get_hidden():
928 return
928 return
929 else:
929 else:
930 repo = backend(repo_full_path, create=False)
930 repo = backend(repo_full_path, create=False)
931
931
932 return repo
932 return repo
933
933
934
934
935 class RepoGroup(Base, BaseModel):
935 class RepoGroup(Base, BaseModel):
936 __tablename__ = 'groups'
936 __tablename__ = 'groups'
937 __table_args__ = (
937 __table_args__ = (
938 UniqueConstraint('group_name', 'group_parent_id'),
938 UniqueConstraint('group_name', 'group_parent_id'),
939 CheckConstraint('group_id != group_parent_id'),
939 CheckConstraint('group_id != group_parent_id'),
940 {'extend_existing': True, 'mysql_engine': 'InnoDB',
940 {'extend_existing': True, 'mysql_engine': 'InnoDB',
941 'mysql_charset': 'utf8'},
941 'mysql_charset': 'utf8'},
942 )
942 )
943 __mapper_args__ = {'order_by': 'group_name'}
943 __mapper_args__ = {'order_by': 'group_name'}
944
944
945 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
945 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
946 group_name = Column("group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
946 group_name = Column("group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
947 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
947 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
948 group_description = Column("group_description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
948 group_description = Column("group_description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
949
949
950 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
950 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
951 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
951 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
952
952
953 parent_group = relationship('RepoGroup', remote_side=group_id)
953 parent_group = relationship('RepoGroup', remote_side=group_id)
954
954
955 def __init__(self, group_name='', parent_group=None):
955 def __init__(self, group_name='', parent_group=None):
956 self.group_name = group_name
956 self.group_name = group_name
957 self.parent_group = parent_group
957 self.parent_group = parent_group
958
958
959 def __unicode__(self):
959 def __unicode__(self):
960 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
960 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
961 self.group_name)
961 self.group_name)
962
962
963 @classmethod
963 @classmethod
964 def groups_choices(cls):
964 def groups_choices(cls):
965 from webhelpers.html import literal as _literal
965 from webhelpers.html import literal as _literal
966 repo_groups = [('', '')]
966 repo_groups = [('', '')]
967 sep = ' &raquo; '
967 sep = ' &raquo; '
968 _name = lambda k: _literal(sep.join(k))
968 _name = lambda k: _literal(sep.join(k))
969
969
970 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
970 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
971 for x in cls.query().all()])
971 for x in cls.query().all()])
972
972
973 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
973 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
974 return repo_groups
974 return repo_groups
975
975
976 @classmethod
976 @classmethod
977 def url_sep(cls):
977 def url_sep(cls):
978 return URL_SEP
978 return URL_SEP
979
979
980 @classmethod
980 @classmethod
981 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
981 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
982 if case_insensitive:
982 if case_insensitive:
983 gr = cls.query()\
983 gr = cls.query()\
984 .filter(cls.group_name.ilike(group_name))
984 .filter(cls.group_name.ilike(group_name))
985 else:
985 else:
986 gr = cls.query()\
986 gr = cls.query()\
987 .filter(cls.group_name == group_name)
987 .filter(cls.group_name == group_name)
988 if cache:
988 if cache:
989 gr = gr.options(FromCache(
989 gr = gr.options(FromCache(
990 "sql_cache_short",
990 "sql_cache_short",
991 "get_group_%s" % _hash_key(group_name)
991 "get_group_%s" % _hash_key(group_name)
992 )
992 )
993 )
993 )
994 return gr.scalar()
994 return gr.scalar()
995
995
996 @property
996 @property
997 def parents(self):
997 def parents(self):
998 parents_recursion_limit = 5
998 parents_recursion_limit = 5
999 groups = []
999 groups = []
1000 if self.parent_group is None:
1000 if self.parent_group is None:
1001 return groups
1001 return groups
1002 cur_gr = self.parent_group
1002 cur_gr = self.parent_group
1003 groups.insert(0, cur_gr)
1003 groups.insert(0, cur_gr)
1004 cnt = 0
1004 cnt = 0
1005 while 1:
1005 while 1:
1006 cnt += 1
1006 cnt += 1
1007 gr = getattr(cur_gr, 'parent_group', None)
1007 gr = getattr(cur_gr, 'parent_group', None)
1008 cur_gr = cur_gr.parent_group
1008 cur_gr = cur_gr.parent_group
1009 if gr is None:
1009 if gr is None:
1010 break
1010 break
1011 if cnt == parents_recursion_limit:
1011 if cnt == parents_recursion_limit:
1012 # this will prevent accidental infinit loops
1012 # this will prevent accidental infinit loops
1013 log.error('group nested more than %s' %
1013 log.error('group nested more than %s' %
1014 parents_recursion_limit)
1014 parents_recursion_limit)
1015 break
1015 break
1016
1016
1017 groups.insert(0, gr)
1017 groups.insert(0, gr)
1018 return groups
1018 return groups
1019
1019
1020 @property
1020 @property
1021 def children(self):
1021 def children(self):
1022 return RepoGroup.query().filter(RepoGroup.parent_group == self)
1022 return RepoGroup.query().filter(RepoGroup.parent_group == self)
1023
1023
1024 @property
1024 @property
1025 def name(self):
1025 def name(self):
1026 return self.group_name.split(RepoGroup.url_sep())[-1]
1026 return self.group_name.split(RepoGroup.url_sep())[-1]
1027
1027
1028 @property
1028 @property
1029 def full_path(self):
1029 def full_path(self):
1030 return self.group_name
1030 return self.group_name
1031
1031
1032 @property
1032 @property
1033 def full_path_splitted(self):
1033 def full_path_splitted(self):
1034 return self.group_name.split(RepoGroup.url_sep())
1034 return self.group_name.split(RepoGroup.url_sep())
1035
1035
1036 @property
1036 @property
1037 def repositories(self):
1037 def repositories(self):
1038 return Repository.query()\
1038 return Repository.query()\
1039 .filter(Repository.group == self)\
1039 .filter(Repository.group == self)\
1040 .order_by(Repository.repo_name)
1040 .order_by(Repository.repo_name)
1041
1041
1042 @property
1042 @property
1043 def repositories_recursive_count(self):
1043 def repositories_recursive_count(self):
1044 cnt = self.repositories.count()
1044 cnt = self.repositories.count()
1045
1045
1046 def children_count(group):
1046 def children_count(group):
1047 cnt = 0
1047 cnt = 0
1048 for child in group.children:
1048 for child in group.children:
1049 cnt += child.repositories.count()
1049 cnt += child.repositories.count()
1050 cnt += children_count(child)
1050 cnt += children_count(child)
1051 return cnt
1051 return cnt
1052
1052
1053 return cnt + children_count(self)
1053 return cnt + children_count(self)
1054
1054
1055 def get_new_name(self, group_name):
1055 def get_new_name(self, group_name):
1056 """
1056 """
1057 returns new full group name based on parent and new name
1057 returns new full group name based on parent and new name
1058
1058
1059 :param group_name:
1059 :param group_name:
1060 """
1060 """
1061 path_prefix = (self.parent_group.full_path_splitted if
1061 path_prefix = (self.parent_group.full_path_splitted if
1062 self.parent_group else [])
1062 self.parent_group else [])
1063 return RepoGroup.url_sep().join(path_prefix + [group_name])
1063 return RepoGroup.url_sep().join(path_prefix + [group_name])
1064
1064
1065
1065
1066 class Permission(Base, BaseModel):
1066 class Permission(Base, BaseModel):
1067 __tablename__ = 'permissions'
1067 __tablename__ = 'permissions'
1068 __table_args__ = (
1068 __table_args__ = (
1069 Index('p_perm_name_idx', 'permission_name'),
1069 Index('p_perm_name_idx', 'permission_name'),
1070 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1070 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1071 'mysql_charset': 'utf8'},
1071 'mysql_charset': 'utf8'},
1072 )
1072 )
1073 PERMS = [
1073 PERMS = [
1074 ('repository.none', _('Repository no access')),
1074 ('repository.none', _('Repository no access')),
1075 ('repository.read', _('Repository read access')),
1075 ('repository.read', _('Repository read access')),
1076 ('repository.write', _('Repository write access')),
1076 ('repository.write', _('Repository write access')),
1077 ('repository.admin', _('Repository admin access')),
1077 ('repository.admin', _('Repository admin access')),
1078
1078
1079 ('group.none', _('Repositories Group no access')),
1079 ('group.none', _('Repositories Group no access')),
1080 ('group.read', _('Repositories Group read access')),
1080 ('group.read', _('Repositories Group read access')),
1081 ('group.write', _('Repositories Group write access')),
1081 ('group.write', _('Repositories Group write access')),
1082 ('group.admin', _('Repositories Group admin access')),
1082 ('group.admin', _('Repositories Group admin access')),
1083
1083
1084 ('hg.admin', _('RhodeCode Administrator')),
1084 ('hg.admin', _('RhodeCode Administrator')),
1085 ('hg.create.none', _('Repository creation disabled')),
1085 ('hg.create.none', _('Repository creation disabled')),
1086 ('hg.create.repository', _('Repository creation enabled')),
1086 ('hg.create.repository', _('Repository creation enabled')),
1087 ('hg.register.none', _('Register disabled')),
1087 ('hg.register.none', _('Register disabled')),
1088 ('hg.register.manual_activate', _('Register new user with RhodeCode '
1088 ('hg.register.manual_activate', _('Register new user with RhodeCode '
1089 'with manual activation')),
1089 'with manual activation')),
1090
1090
1091 ('hg.register.auto_activate', _('Register new user with RhodeCode '
1091 ('hg.register.auto_activate', _('Register new user with RhodeCode '
1092 'with auto activation')),
1092 'with auto activation')),
1093 ]
1093 ]
1094
1094
1095 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1095 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1096 permission_name = Column("permission_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1096 permission_name = Column("permission_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1097 permission_longname = Column("permission_longname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1097 permission_longname = Column("permission_longname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1098
1098
1099 def __unicode__(self):
1099 def __unicode__(self):
1100 return u"<%s('%s:%s')>" % (
1100 return u"<%s('%s:%s')>" % (
1101 self.__class__.__name__, self.permission_id, self.permission_name
1101 self.__class__.__name__, self.permission_id, self.permission_name
1102 )
1102 )
1103
1103
1104 @classmethod
1104 @classmethod
1105 def get_by_key(cls, key):
1105 def get_by_key(cls, key):
1106 return cls.query().filter(cls.permission_name == key).scalar()
1106 return cls.query().filter(cls.permission_name == key).scalar()
1107
1107
1108 @classmethod
1108 @classmethod
1109 def get_default_perms(cls, default_user_id):
1109 def get_default_perms(cls, default_user_id):
1110 q = Session().query(UserRepoToPerm, Repository, cls)\
1110 q = Session().query(UserRepoToPerm, Repository, cls)\
1111 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
1111 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
1112 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
1112 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
1113 .filter(UserRepoToPerm.user_id == default_user_id)
1113 .filter(UserRepoToPerm.user_id == default_user_id)
1114
1114
1115 return q.all()
1115 return q.all()
1116
1116
1117 @classmethod
1117 @classmethod
1118 def get_default_group_perms(cls, default_user_id):
1118 def get_default_group_perms(cls, default_user_id):
1119 q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
1119 q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
1120 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
1120 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
1121 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
1121 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
1122 .filter(UserRepoGroupToPerm.user_id == default_user_id)
1122 .filter(UserRepoGroupToPerm.user_id == default_user_id)
1123
1123
1124 return q.all()
1124 return q.all()
1125
1125
1126
1126
1127 class UserRepoToPerm(Base, BaseModel):
1127 class UserRepoToPerm(Base, BaseModel):
1128 __tablename__ = 'repo_to_perm'
1128 __tablename__ = 'repo_to_perm'
1129 __table_args__ = (
1129 __table_args__ = (
1130 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
1130 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
1131 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1131 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1132 'mysql_charset': 'utf8'}
1132 'mysql_charset': 'utf8'}
1133 )
1133 )
1134 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1134 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1135 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1135 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1136 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1136 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1137 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1137 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1138
1138
1139 user = relationship('User')
1139 user = relationship('User')
1140 repository = relationship('Repository')
1140 repository = relationship('Repository')
1141 permission = relationship('Permission')
1141 permission = relationship('Permission')
1142
1142
1143 @classmethod
1143 @classmethod
1144 def create(cls, user, repository, permission):
1144 def create(cls, user, repository, permission):
1145 n = cls()
1145 n = cls()
1146 n.user = user
1146 n.user = user
1147 n.repository = repository
1147 n.repository = repository
1148 n.permission = permission
1148 n.permission = permission
1149 Session().add(n)
1149 Session().add(n)
1150 return n
1150 return n
1151
1151
1152 def __unicode__(self):
1152 def __unicode__(self):
1153 return u'<user:%s => %s >' % (self.user, self.repository)
1153 return u'<user:%s => %s >' % (self.user, self.repository)
1154
1154
1155
1155
1156 class UserToPerm(Base, BaseModel):
1156 class UserToPerm(Base, BaseModel):
1157 __tablename__ = 'user_to_perm'
1157 __tablename__ = 'user_to_perm'
1158 __table_args__ = (
1158 __table_args__ = (
1159 UniqueConstraint('user_id', 'permission_id'),
1159 UniqueConstraint('user_id', 'permission_id'),
1160 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1160 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1161 'mysql_charset': 'utf8'}
1161 'mysql_charset': 'utf8'}
1162 )
1162 )
1163 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1163 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1164 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1164 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1165 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1165 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1166
1166
1167 user = relationship('User')
1167 user = relationship('User')
1168 permission = relationship('Permission', lazy='joined')
1168 permission = relationship('Permission', lazy='joined')
1169
1169
1170
1170
1171 class UsersGroupRepoToPerm(Base, BaseModel):
1171 class UsersGroupRepoToPerm(Base, BaseModel):
1172 __tablename__ = 'users_group_repo_to_perm'
1172 __tablename__ = 'users_group_repo_to_perm'
1173 __table_args__ = (
1173 __table_args__ = (
1174 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1174 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1175 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1175 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1176 'mysql_charset': 'utf8'}
1176 'mysql_charset': 'utf8'}
1177 )
1177 )
1178 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1178 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1179 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1179 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1180 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1180 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1181 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1181 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1182
1182
1183 users_group = relationship('UsersGroup')
1183 users_group = relationship('UsersGroup')
1184 permission = relationship('Permission')
1184 permission = relationship('Permission')
1185 repository = relationship('Repository')
1185 repository = relationship('Repository')
1186
1186
1187 @classmethod
1187 @classmethod
1188 def create(cls, users_group, repository, permission):
1188 def create(cls, users_group, repository, permission):
1189 n = cls()
1189 n = cls()
1190 n.users_group = users_group
1190 n.users_group = users_group
1191 n.repository = repository
1191 n.repository = repository
1192 n.permission = permission
1192 n.permission = permission
1193 Session().add(n)
1193 Session().add(n)
1194 return n
1194 return n
1195
1195
1196 def __unicode__(self):
1196 def __unicode__(self):
1197 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
1197 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
1198
1198
1199
1199
1200 class UsersGroupToPerm(Base, BaseModel):
1200 class UsersGroupToPerm(Base, BaseModel):
1201 __tablename__ = 'users_group_to_perm'
1201 __tablename__ = 'users_group_to_perm'
1202 __table_args__ = (
1202 __table_args__ = (
1203 UniqueConstraint('users_group_id', 'permission_id',),
1203 UniqueConstraint('users_group_id', 'permission_id',),
1204 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1204 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1205 'mysql_charset': 'utf8'}
1205 'mysql_charset': 'utf8'}
1206 )
1206 )
1207 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1207 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1208 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1208 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1209 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1209 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1210
1210
1211 users_group = relationship('UsersGroup')
1211 users_group = relationship('UsersGroup')
1212 permission = relationship('Permission')
1212 permission = relationship('Permission')
1213
1213
1214
1214
1215 class UserRepoGroupToPerm(Base, BaseModel):
1215 class UserRepoGroupToPerm(Base, BaseModel):
1216 __tablename__ = 'user_repo_group_to_perm'
1216 __tablename__ = 'user_repo_group_to_perm'
1217 __table_args__ = (
1217 __table_args__ = (
1218 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1218 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1219 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1219 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1220 'mysql_charset': 'utf8'}
1220 'mysql_charset': 'utf8'}
1221 )
1221 )
1222
1222
1223 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1223 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1224 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1224 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1225 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1225 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1226 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1226 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1227
1227
1228 user = relationship('User')
1228 user = relationship('User')
1229 group = relationship('RepoGroup')
1229 group = relationship('RepoGroup')
1230 permission = relationship('Permission')
1230 permission = relationship('Permission')
1231
1231
1232
1232
1233 class UsersGroupRepoGroupToPerm(Base, BaseModel):
1233 class UsersGroupRepoGroupToPerm(Base, BaseModel):
1234 __tablename__ = 'users_group_repo_group_to_perm'
1234 __tablename__ = 'users_group_repo_group_to_perm'
1235 __table_args__ = (
1235 __table_args__ = (
1236 UniqueConstraint('users_group_id', 'group_id'),
1236 UniqueConstraint('users_group_id', 'group_id'),
1237 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1237 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1238 'mysql_charset': 'utf8'}
1238 'mysql_charset': 'utf8'}
1239 )
1239 )
1240
1240
1241 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)
1241 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)
1242 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1242 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1243 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1243 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1244 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1244 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1245
1245
1246 users_group = relationship('UsersGroup')
1246 users_group = relationship('UsersGroup')
1247 permission = relationship('Permission')
1247 permission = relationship('Permission')
1248 group = relationship('RepoGroup')
1248 group = relationship('RepoGroup')
1249
1249
1250
1250
1251 class Statistics(Base, BaseModel):
1251 class Statistics(Base, BaseModel):
1252 __tablename__ = 'statistics'
1252 __tablename__ = 'statistics'
1253 __table_args__ = (
1253 __table_args__ = (
1254 UniqueConstraint('repository_id'),
1254 UniqueConstraint('repository_id'),
1255 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1255 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1256 'mysql_charset': 'utf8'}
1256 'mysql_charset': 'utf8'}
1257 )
1257 )
1258 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1258 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1259 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1259 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1260 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1260 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1261 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1261 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1262 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1262 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1263 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1263 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1264
1264
1265 repository = relationship('Repository', single_parent=True)
1265 repository = relationship('Repository', single_parent=True)
1266
1266
1267
1267
1268 class UserFollowing(Base, BaseModel):
1268 class UserFollowing(Base, BaseModel):
1269 __tablename__ = 'user_followings'
1269 __tablename__ = 'user_followings'
1270 __table_args__ = (
1270 __table_args__ = (
1271 UniqueConstraint('user_id', 'follows_repository_id'),
1271 UniqueConstraint('user_id', 'follows_repository_id'),
1272 UniqueConstraint('user_id', 'follows_user_id'),
1272 UniqueConstraint('user_id', 'follows_user_id'),
1273 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1273 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1274 'mysql_charset': 'utf8'}
1274 'mysql_charset': 'utf8'}
1275 )
1275 )
1276
1276
1277 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1277 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1278 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1278 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1279 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1279 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1280 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1280 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1281 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1281 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1282
1282
1283 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1283 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1284
1284
1285 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1285 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1286 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1286 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1287
1287
1288 @classmethod
1288 @classmethod
1289 def get_repo_followers(cls, repo_id):
1289 def get_repo_followers(cls, repo_id):
1290 return cls.query().filter(cls.follows_repo_id == repo_id)
1290 return cls.query().filter(cls.follows_repo_id == repo_id)
1291
1291
1292
1292
1293 class CacheInvalidation(Base, BaseModel):
1293 class CacheInvalidation(Base, BaseModel):
1294 __tablename__ = 'cache_invalidation'
1294 __tablename__ = 'cache_invalidation'
1295 __table_args__ = (
1295 __table_args__ = (
1296 UniqueConstraint('cache_key'),
1296 UniqueConstraint('cache_key'),
1297 Index('key_idx', 'cache_key'),
1297 Index('key_idx', 'cache_key'),
1298 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1298 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1299 'mysql_charset': 'utf8'},
1299 'mysql_charset': 'utf8'},
1300 )
1300 )
1301 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1301 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1302 cache_key = Column("cache_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1302 cache_key = Column("cache_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1303 cache_args = Column("cache_args", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1303 cache_args = Column("cache_args", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1304 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1304 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1305
1305
1306 def __init__(self, cache_key, cache_args=''):
1306 def __init__(self, cache_key, cache_args=''):
1307 self.cache_key = cache_key
1307 self.cache_key = cache_key
1308 self.cache_args = cache_args
1308 self.cache_args = cache_args
1309 self.cache_active = False
1309 self.cache_active = False
1310
1310
1311 def __unicode__(self):
1311 def __unicode__(self):
1312 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1312 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1313 self.cache_id, self.cache_key)
1313 self.cache_id, self.cache_key)
1314
1314
1315 @classmethod
1315 @classmethod
1316 def clear_cache(cls):
1316 def clear_cache(cls):
1317 cls.query().delete()
1317 cls.query().delete()
1318
1318
1319 @classmethod
1319 @classmethod
1320 def _get_key(cls, key):
1320 def _get_key(cls, key):
1321 """
1321 """
1322 Wrapper for generating a key, together with a prefix
1322 Wrapper for generating a key, together with a prefix
1323
1323
1324 :param key:
1324 :param key:
1325 """
1325 """
1326 import rhodecode
1326 import rhodecode
1327 prefix = ''
1327 prefix = ''
1328 iid = rhodecode.CONFIG.get('instance_id')
1328 iid = rhodecode.CONFIG.get('instance_id')
1329 if iid:
1329 if iid:
1330 prefix = iid
1330 prefix = iid
1331 return "%s%s" % (prefix, key), prefix, key.rstrip('_README')
1331 return "%s%s" % (prefix, key), prefix, key.rstrip('_README')
1332
1332
1333 @classmethod
1333 @classmethod
1334 def get_by_key(cls, key):
1334 def get_by_key(cls, key):
1335 return cls.query().filter(cls.cache_key == key).scalar()
1335 return cls.query().filter(cls.cache_key == key).scalar()
1336
1336
1337 @classmethod
1337 @classmethod
1338 def _get_or_create_key(cls, key, prefix, org_key):
1338 def _get_or_create_key(cls, key, prefix, org_key):
1339 inv_obj = Session().query(cls).filter(cls.cache_key == key).scalar()
1339 inv_obj = Session().query(cls).filter(cls.cache_key == key).scalar()
1340 if not inv_obj:
1340 if not inv_obj:
1341 try:
1341 try:
1342 inv_obj = CacheInvalidation(key, org_key)
1342 inv_obj = CacheInvalidation(key, org_key)
1343 Session().add(inv_obj)
1343 Session().add(inv_obj)
1344 Session().commit()
1344 Session().commit()
1345 except Exception:
1345 except Exception:
1346 log.error(traceback.format_exc())
1346 log.error(traceback.format_exc())
1347 Session().rollback()
1347 Session().rollback()
1348 return inv_obj
1348 return inv_obj
1349
1349
1350 @classmethod
1350 @classmethod
1351 def invalidate(cls, key):
1351 def invalidate(cls, key):
1352 """
1352 """
1353 Returns Invalidation object if this given key should be invalidated
1353 Returns Invalidation object if this given key should be invalidated
1354 None otherwise. `cache_active = False` means that this cache
1354 None otherwise. `cache_active = False` means that this cache
1355 state is not valid and needs to be invalidated
1355 state is not valid and needs to be invalidated
1356
1356
1357 :param key:
1357 :param key:
1358 """
1358 """
1359
1359
1360 key, _prefix, _org_key = cls._get_key(key)
1360 key, _prefix, _org_key = cls._get_key(key)
1361 inv = cls._get_or_create_key(key, _prefix, _org_key)
1361 inv = cls._get_or_create_key(key, _prefix, _org_key)
1362
1362
1363 if inv and inv.cache_active is False:
1363 if inv and inv.cache_active is False:
1364 return inv
1364 return inv
1365
1365
1366 @classmethod
1366 @classmethod
1367 def set_invalidate(cls, key):
1367 def set_invalidate(cls, key):
1368 """
1368 """
1369 Mark this Cache key for invalidation
1369 Mark this Cache key for invalidation
1370
1370
1371 :param key:
1371 :param key:
1372 """
1372 """
1373
1373
1374 key, _prefix, _org_key = cls._get_key(key)
1374 key, _prefix, _org_key = cls._get_key(key)
1375 inv_objs = Session().query(cls).filter(cls.cache_args == _org_key).all()
1375 inv_objs = Session().query(cls).filter(cls.cache_args == _org_key).all()
1376 log.debug('marking %s key[s] %s for invalidation' % (len(inv_objs),
1376 log.debug('marking %s key[s] %s for invalidation' % (len(inv_objs),
1377 _org_key))
1377 _org_key))
1378 try:
1378 try:
1379 for inv_obj in inv_objs:
1379 for inv_obj in inv_objs:
1380 if inv_obj:
1380 if inv_obj:
1381 inv_obj.cache_active = False
1381 inv_obj.cache_active = False
1382
1382
1383 Session().add(inv_obj)
1383 Session().add(inv_obj)
1384 Session().commit()
1384 Session().commit()
1385 except Exception:
1385 except Exception:
1386 log.error(traceback.format_exc())
1386 log.error(traceback.format_exc())
1387 Session().rollback()
1387 Session().rollback()
1388
1388
1389 @classmethod
1389 @classmethod
1390 def set_valid(cls, key):
1390 def set_valid(cls, key):
1391 """
1391 """
1392 Mark this cache key as active and currently cached
1392 Mark this cache key as active and currently cached
1393
1393
1394 :param key:
1394 :param key:
1395 """
1395 """
1396 inv_obj = cls.get_by_key(key)
1396 inv_obj = cls.get_by_key(key)
1397 inv_obj.cache_active = True
1397 inv_obj.cache_active = True
1398 Session().add(inv_obj)
1398 Session().add(inv_obj)
1399 Session().commit()
1399 Session().commit()
1400
1400
1401 @classmethod
1401 @classmethod
1402 def get_cache_map(cls):
1402 def get_cache_map(cls):
1403
1403
1404 class cachemapdict(dict):
1404 class cachemapdict(dict):
1405
1405
1406 def __init__(self, *args, **kwargs):
1406 def __init__(self, *args, **kwargs):
1407 fixkey = kwargs.get('fixkey')
1407 fixkey = kwargs.get('fixkey')
1408 if fixkey:
1408 if fixkey:
1409 del kwargs['fixkey']
1409 del kwargs['fixkey']
1410 self.fixkey = fixkey
1410 self.fixkey = fixkey
1411 super(cachemapdict, self).__init__(*args, **kwargs)
1411 super(cachemapdict, self).__init__(*args, **kwargs)
1412
1412
1413 def __getattr__(self, name):
1413 def __getattr__(self, name):
1414 key = name
1414 key = name
1415 if self.fixkey:
1415 if self.fixkey:
1416 key, _prefix, _org_key = cls._get_key(key)
1416 key, _prefix, _org_key = cls._get_key(key)
1417 if key in self.__dict__:
1417 if key in self.__dict__:
1418 return self.__dict__[key]
1418 return self.__dict__[key]
1419 else:
1419 else:
1420 return self[key]
1420 return self[key]
1421
1421
1422 def __getitem__(self, key):
1422 def __getitem__(self, key):
1423 if self.fixkey:
1423 if self.fixkey:
1424 key, _prefix, _org_key = cls._get_key(key)
1424 key, _prefix, _org_key = cls._get_key(key)
1425 try:
1425 try:
1426 return super(cachemapdict, self).__getitem__(key)
1426 return super(cachemapdict, self).__getitem__(key)
1427 except KeyError:
1427 except KeyError:
1428 return
1428 return
1429
1429
1430 cache_map = cachemapdict(fixkey=True)
1430 cache_map = cachemapdict(fixkey=True)
1431 for obj in cls.query().all():
1431 for obj in cls.query().all():
1432 cache_map[obj.cache_key] = cachemapdict(obj.get_dict())
1432 cache_map[obj.cache_key] = cachemapdict(obj.get_dict())
1433 return cache_map
1433 return cache_map
1434
1434
1435
1435
1436 class ChangesetComment(Base, BaseModel):
1436 class ChangesetComment(Base, BaseModel):
1437 __tablename__ = 'changeset_comments'
1437 __tablename__ = 'changeset_comments'
1438 __table_args__ = (
1438 __table_args__ = (
1439 Index('cc_revision_idx', 'revision'),
1439 Index('cc_revision_idx', 'revision'),
1440 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1440 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1441 'mysql_charset': 'utf8'},
1441 'mysql_charset': 'utf8'},
1442 )
1442 )
1443 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1443 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1444 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1444 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1445 revision = Column('revision', String(40), nullable=True)
1445 revision = Column('revision', String(40), nullable=True)
1446 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1446 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1447 line_no = Column('line_no', Unicode(10), nullable=True)
1447 line_no = Column('line_no', Unicode(10), nullable=True)
1448 f_path = Column('f_path', Unicode(1000), nullable=True)
1448 f_path = Column('f_path', Unicode(1000), nullable=True)
1449 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1449 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1450 text = Column('text', Unicode(25000), nullable=False)
1450 text = Column('text', Unicode(25000), nullable=False)
1451 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1451 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1452 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1452 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1453
1453
1454 author = relationship('User', lazy='joined')
1454 author = relationship('User', lazy='joined')
1455 repo = relationship('Repository')
1455 repo = relationship('Repository')
1456 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1456 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1457 pull_request = relationship('PullRequest', lazy='joined')
1457 pull_request = relationship('PullRequest', lazy='joined')
1458
1458
1459 @classmethod
1459 @classmethod
1460 def get_users(cls, revision=None, pull_request_id=None):
1460 def get_users(cls, revision=None, pull_request_id=None):
1461 """
1461 """
1462 Returns user associated with this ChangesetComment. ie those
1462 Returns user associated with this ChangesetComment. ie those
1463 who actually commented
1463 who actually commented
1464
1464
1465 :param cls:
1465 :param cls:
1466 :param revision:
1466 :param revision:
1467 """
1467 """
1468 q = Session().query(User)\
1468 q = Session().query(User)\
1469 .join(ChangesetComment.author)
1469 .join(ChangesetComment.author)
1470 if revision:
1470 if revision:
1471 q = q.filter(cls.revision == revision)
1471 q = q.filter(cls.revision == revision)
1472 elif pull_request_id:
1472 elif pull_request_id:
1473 q = q.filter(cls.pull_request_id == pull_request_id)
1473 q = q.filter(cls.pull_request_id == pull_request_id)
1474 return q.all()
1474 return q.all()
1475
1475
1476
1476
1477 class ChangesetStatus(Base, BaseModel):
1477 class ChangesetStatus(Base, BaseModel):
1478 __tablename__ = 'changeset_statuses'
1478 __tablename__ = 'changeset_statuses'
1479 __table_args__ = (
1479 __table_args__ = (
1480 Index('cs_revision_idx', 'revision'),
1480 Index('cs_revision_idx', 'revision'),
1481 Index('cs_version_idx', 'version'),
1481 Index('cs_version_idx', 'version'),
1482 UniqueConstraint('repo_id', 'revision', 'version'),
1482 UniqueConstraint('repo_id', 'revision', 'version'),
1483 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1483 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1484 'mysql_charset': 'utf8'}
1484 'mysql_charset': 'utf8'}
1485 )
1485 )
1486 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1486 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1487 STATUS_APPROVED = 'approved'
1487 STATUS_APPROVED = 'approved'
1488 STATUS_REJECTED = 'rejected'
1488 STATUS_REJECTED = 'rejected'
1489 STATUS_UNDER_REVIEW = 'under_review'
1489 STATUS_UNDER_REVIEW = 'under_review'
1490
1490
1491 STATUSES = [
1491 STATUSES = [
1492 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1492 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1493 (STATUS_APPROVED, _("Approved")),
1493 (STATUS_APPROVED, _("Approved")),
1494 (STATUS_REJECTED, _("Rejected")),
1494 (STATUS_REJECTED, _("Rejected")),
1495 (STATUS_UNDER_REVIEW, _("Under Review")),
1495 (STATUS_UNDER_REVIEW, _("Under Review")),
1496 ]
1496 ]
1497
1497
1498 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1498 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1499 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1499 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1500 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1500 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1501 revision = Column('revision', String(40), nullable=False)
1501 revision = Column('revision', String(40), nullable=False)
1502 status = Column('status', String(128), nullable=False, default=DEFAULT)
1502 status = Column('status', String(128), nullable=False, default=DEFAULT)
1503 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1503 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1504 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1504 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1505 version = Column('version', Integer(), nullable=False, default=0)
1505 version = Column('version', Integer(), nullable=False, default=0)
1506 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1506 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1507
1507
1508 author = relationship('User', lazy='joined')
1508 author = relationship('User', lazy='joined')
1509 repo = relationship('Repository')
1509 repo = relationship('Repository')
1510 comment = relationship('ChangesetComment', lazy='joined')
1510 comment = relationship('ChangesetComment', lazy='joined')
1511 pull_request = relationship('PullRequest', lazy='joined')
1511 pull_request = relationship('PullRequest', lazy='joined')
1512
1512
1513 def __unicode__(self):
1513 def __unicode__(self):
1514 return u"<%s('%s:%s')>" % (
1514 return u"<%s('%s:%s')>" % (
1515 self.__class__.__name__,
1515 self.__class__.__name__,
1516 self.status, self.author
1516 self.status, self.author
1517 )
1517 )
1518
1518
1519 @classmethod
1519 @classmethod
1520 def get_status_lbl(cls, value):
1520 def get_status_lbl(cls, value):
1521 return dict(cls.STATUSES).get(value)
1521 return dict(cls.STATUSES).get(value)
1522
1522
1523 @property
1523 @property
1524 def status_lbl(self):
1524 def status_lbl(self):
1525 return ChangesetStatus.get_status_lbl(self.status)
1525 return ChangesetStatus.get_status_lbl(self.status)
1526
1526
1527
1527
1528 class PullRequest(Base, BaseModel):
1528 class PullRequest(Base, BaseModel):
1529 __tablename__ = 'pull_requests'
1529 __tablename__ = 'pull_requests'
1530 __table_args__ = (
1530 __table_args__ = (
1531 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1531 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1532 'mysql_charset': 'utf8'},
1532 'mysql_charset': 'utf8'},
1533 )
1533 )
1534
1534
1535 STATUS_NEW = u'new'
1535 STATUS_NEW = u'new'
1536 STATUS_OPEN = u'open'
1536 STATUS_OPEN = u'open'
1537 STATUS_CLOSED = u'closed'
1537 STATUS_CLOSED = u'closed'
1538
1538
1539 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1539 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1540 title = Column('title', Unicode(256), nullable=True)
1540 title = Column('title', Unicode(256), nullable=True)
1541 description = Column('description', UnicodeText(10240), nullable=True)
1541 description = Column('description', UnicodeText(10240), nullable=True)
1542 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1542 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1543 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1543 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1544 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1544 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1545 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1545 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1546 _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
1546 _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
1547 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1547 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1548 org_ref = Column('org_ref', Unicode(256), nullable=False)
1548 org_ref = Column('org_ref', Unicode(256), nullable=False)
1549 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1549 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1550 other_ref = Column('other_ref', Unicode(256), nullable=False)
1550 other_ref = Column('other_ref', Unicode(256), nullable=False)
1551
1551
1552 @hybrid_property
1552 @hybrid_property
1553 def revisions(self):
1553 def revisions(self):
1554 return self._revisions.split(':')
1554 return self._revisions.split(':')
1555
1555
1556 @revisions.setter
1556 @revisions.setter
1557 def revisions(self, val):
1557 def revisions(self, val):
1558 self._revisions = ':'.join(val)
1558 self._revisions = ':'.join(val)
1559
1559
1560 author = relationship('User', lazy='joined')
1560 author = relationship('User', lazy='joined')
1561 reviewers = relationship('PullRequestReviewers',
1561 reviewers = relationship('PullRequestReviewers',
1562 cascade="all, delete, delete-orphan")
1562 cascade="all, delete, delete-orphan")
1563 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1563 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1564 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1564 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1565 statuses = relationship('ChangesetStatus')
1565 statuses = relationship('ChangesetStatus')
1566 comments = relationship('ChangesetComment',
1566 comments = relationship('ChangesetComment',
1567 cascade="all, delete, delete-orphan")
1567 cascade="all, delete, delete-orphan")
1568
1568
1569 def is_closed(self):
1569 def is_closed(self):
1570 return self.status == self.STATUS_CLOSED
1570 return self.status == self.STATUS_CLOSED
1571
1571
1572 def __json__(self):
1572 def __json__(self):
1573 return dict(
1573 return dict(
1574 revisions=self.revisions
1574 revisions=self.revisions
1575 )
1575 )
1576
1576
1577
1577
1578 class PullRequestReviewers(Base, BaseModel):
1578 class PullRequestReviewers(Base, BaseModel):
1579 __tablename__ = 'pull_request_reviewers'
1579 __tablename__ = 'pull_request_reviewers'
1580 __table_args__ = (
1580 __table_args__ = (
1581 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1581 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1582 'mysql_charset': 'utf8'},
1582 'mysql_charset': 'utf8'},
1583 )
1583 )
1584
1584
1585 def __init__(self, user=None, pull_request=None):
1585 def __init__(self, user=None, pull_request=None):
1586 self.user = user
1586 self.user = user
1587 self.pull_request = pull_request
1587 self.pull_request = pull_request
1588
1588
1589 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1589 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1590 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1590 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1591 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1591 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1592
1592
1593 user = relationship('User')
1593 user = relationship('User')
1594 pull_request = relationship('PullRequest')
1594 pull_request = relationship('PullRequest')
1595
1595
1596
1596
1597 class Notification(Base, BaseModel):
1597 class Notification(Base, BaseModel):
1598 __tablename__ = 'notifications'
1598 __tablename__ = 'notifications'
1599 __table_args__ = (
1599 __table_args__ = (
1600 Index('notification_type_idx', 'type'),
1600 Index('notification_type_idx', 'type'),
1601 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1601 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1602 'mysql_charset': 'utf8'},
1602 'mysql_charset': 'utf8'},
1603 )
1603 )
1604
1604
1605 TYPE_CHANGESET_COMMENT = u'cs_comment'
1605 TYPE_CHANGESET_COMMENT = u'cs_comment'
1606 TYPE_MESSAGE = u'message'
1606 TYPE_MESSAGE = u'message'
1607 TYPE_MENTION = u'mention'
1607 TYPE_MENTION = u'mention'
1608 TYPE_REGISTRATION = u'registration'
1608 TYPE_REGISTRATION = u'registration'
1609 TYPE_PULL_REQUEST = u'pull_request'
1609 TYPE_PULL_REQUEST = u'pull_request'
1610 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1610 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1611
1611
1612 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1612 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1613 subject = Column('subject', Unicode(512), nullable=True)
1613 subject = Column('subject', Unicode(512), nullable=True)
1614 body = Column('body', UnicodeText(50000), nullable=True)
1614 body = Column('body', UnicodeText(50000), nullable=True)
1615 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1615 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1616 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1616 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1617 type_ = Column('type', Unicode(256))
1617 type_ = Column('type', Unicode(256))
1618
1618
1619 created_by_user = relationship('User')
1619 created_by_user = relationship('User')
1620 notifications_to_users = relationship('UserNotification', lazy='joined',
1620 notifications_to_users = relationship('UserNotification', lazy='joined',
1621 cascade="all, delete, delete-orphan")
1621 cascade="all, delete, delete-orphan")
1622
1622
1623 @property
1623 @property
1624 def recipients(self):
1624 def recipients(self):
1625 return [x.user for x in UserNotification.query()\
1625 return [x.user for x in UserNotification.query()\
1626 .filter(UserNotification.notification == self)\
1626 .filter(UserNotification.notification == self)\
1627 .order_by(UserNotification.user_id.asc()).all()]
1627 .order_by(UserNotification.user_id.asc()).all()]
1628
1628
1629 @classmethod
1629 @classmethod
1630 def create(cls, created_by, subject, body, recipients, type_=None):
1630 def create(cls, created_by, subject, body, recipients, type_=None):
1631 if type_ is None:
1631 if type_ is None:
1632 type_ = Notification.TYPE_MESSAGE
1632 type_ = Notification.TYPE_MESSAGE
1633
1633
1634 notification = cls()
1634 notification = cls()
1635 notification.created_by_user = created_by
1635 notification.created_by_user = created_by
1636 notification.subject = subject
1636 notification.subject = subject
1637 notification.body = body
1637 notification.body = body
1638 notification.type_ = type_
1638 notification.type_ = type_
1639 notification.created_on = datetime.datetime.now()
1639 notification.created_on = datetime.datetime.now()
1640
1640
1641 for u in recipients:
1641 for u in recipients:
1642 assoc = UserNotification()
1642 assoc = UserNotification()
1643 assoc.notification = notification
1643 assoc.notification = notification
1644 u.notifications.append(assoc)
1644 u.notifications.append(assoc)
1645 Session().add(notification)
1645 Session().add(notification)
1646 return notification
1646 return notification
1647
1647
1648 @property
1648 @property
1649 def description(self):
1649 def description(self):
1650 from rhodecode.model.notification import NotificationModel
1650 from rhodecode.model.notification import NotificationModel
1651 return NotificationModel().make_description(self)
1651 return NotificationModel().make_description(self)
1652
1652
1653
1653
1654 class UserNotification(Base, BaseModel):
1654 class UserNotification(Base, BaseModel):
1655 __tablename__ = 'user_to_notification'
1655 __tablename__ = 'user_to_notification'
1656 __table_args__ = (
1656 __table_args__ = (
1657 UniqueConstraint('user_id', 'notification_id'),
1657 UniqueConstraint('user_id', 'notification_id'),
1658 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1658 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1659 'mysql_charset': 'utf8'}
1659 'mysql_charset': 'utf8'}
1660 )
1660 )
1661 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1661 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1662 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1662 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1663 read = Column('read', Boolean, default=False)
1663 read = Column('read', Boolean, default=False)
1664 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1664 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1665
1665
1666 user = relationship('User', lazy="joined")
1666 user = relationship('User', lazy="joined")
1667 notification = relationship('Notification', lazy="joined",
1667 notification = relationship('Notification', lazy="joined",
1668 order_by=lambda: Notification.created_on.desc(),)
1668 order_by=lambda: Notification.created_on.desc(),)
1669
1669
1670 def mark_as_read(self):
1670 def mark_as_read(self):
1671 self.read = True
1671 self.read = True
1672 Session().add(self)
1672 Session().add(self)
1673
1673
1674
1674
1675 class DbMigrateVersion(Base, BaseModel):
1675 class DbMigrateVersion(Base, BaseModel):
1676 __tablename__ = 'db_migrate_version'
1676 __tablename__ = 'db_migrate_version'
1677 __table_args__ = (
1677 __table_args__ = (
1678 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1678 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1679 'mysql_charset': 'utf8'},
1679 'mysql_charset': 'utf8'},
1680 )
1680 )
1681 repository_id = Column('repository_id', String(250), primary_key=True)
1681 repository_id = Column('repository_id', String(250), primary_key=True)
1682 repository_path = Column('repository_path', Text)
1682 repository_path = Column('repository_path', Text)
1683 version = Column('version', Integer)
1683 version = Column('version', Integer)
@@ -1,317 +1,321 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.ValidPassword(),
70 v.UnicodeString(strip=False, min=6, not_empty=False)
70 v.UnicodeString(strip=False, min=6, not_empty=False)
71 )
71 )
72 password_confirmation = All(
72 password_confirmation = All(
73 v.ValidPassword(),
73 v.ValidPassword(),
74 v.UnicodeString(strip=False, min=6, not_empty=False),
74 v.UnicodeString(strip=False, min=6, not_empty=False),
75 )
75 )
76 admin = v.StringBoolean(if_missing=False)
76 admin = v.StringBoolean(if_missing=False)
77 else:
77 else:
78 password = All(
78 password = All(
79 v.ValidPassword(),
79 v.ValidPassword(),
80 v.UnicodeString(strip=False, min=6, not_empty=True)
80 v.UnicodeString(strip=False, min=6, not_empty=True)
81 )
81 )
82 password_confirmation = All(
82 password_confirmation = All(
83 v.ValidPassword(),
83 v.ValidPassword(),
84 v.UnicodeString(strip=False, min=6, not_empty=False)
84 v.UnicodeString(strip=False, min=6, not_empty=False)
85 )
85 )
86
86
87 active = v.StringBoolean(if_missing=False)
87 active = v.StringBoolean(if_missing=False)
88 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
88 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
89 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
89 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
90 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
90 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
91
91
92 chained_validators = [v.ValidPasswordsMatch()]
92 chained_validators = [v.ValidPasswordsMatch()]
93
93
94 return _UserForm
94 return _UserForm
95
95
96
96
97 def UsersGroupForm(edit=False, old_data={}, available_members=[]):
97 def UsersGroupForm(edit=False, old_data={}, available_members=[]):
98 class _UsersGroupForm(formencode.Schema):
98 class _UsersGroupForm(formencode.Schema):
99 allow_extra_fields = True
99 allow_extra_fields = True
100 filter_extra_fields = True
100 filter_extra_fields = True
101
101
102 users_group_name = All(
102 users_group_name = All(
103 v.UnicodeString(strip=True, min=1, not_empty=True),
103 v.UnicodeString(strip=True, min=1, not_empty=True),
104 v.ValidUsersGroup(edit, old_data)
104 v.ValidUsersGroup(edit, old_data)
105 )
105 )
106
106
107 users_group_active = v.StringBoolean(if_missing=False)
107 users_group_active = v.StringBoolean(if_missing=False)
108
108
109 if edit:
109 if edit:
110 users_group_members = v.OneOf(
110 users_group_members = v.OneOf(
111 available_members, hideList=False, testValueList=True,
111 available_members, hideList=False, testValueList=True,
112 if_missing=None, not_empty=False
112 if_missing=None, not_empty=False
113 )
113 )
114
114
115 return _UsersGroupForm
115 return _UsersGroupForm
116
116
117
117
118 def ReposGroupForm(edit=False, old_data={}, available_groups=[]):
118 def ReposGroupForm(edit=False, old_data={}, available_groups=[]):
119 class _ReposGroupForm(formencode.Schema):
119 class _ReposGroupForm(formencode.Schema):
120 allow_extra_fields = True
120 allow_extra_fields = True
121 filter_extra_fields = False
121 filter_extra_fields = False
122
122
123 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),
124 v.SlugifyName())
124 v.SlugifyName())
125 group_description = v.UnicodeString(strip=True, min=1,
125 group_description = v.UnicodeString(strip=True, min=1,
126 not_empty=True)
126 not_empty=True)
127 group_parent_id = v.OneOf(available_groups, hideList=False,
127 group_parent_id = v.OneOf(available_groups, hideList=False,
128 testValueList=True,
128 testValueList=True,
129 if_missing=None, not_empty=False)
129 if_missing=None, not_empty=False)
130
130
131 chained_validators = [v.ValidReposGroup(edit, old_data),
131 chained_validators = [v.ValidReposGroup(edit, old_data),
132 v.ValidPerms('group')]
132 v.ValidPerms('group')]
133
133
134 return _ReposGroupForm
134 return _ReposGroupForm
135
135
136
136
137 def RegisterForm(edit=False, old_data={}):
137 def RegisterForm(edit=False, old_data={}):
138 class _RegisterForm(formencode.Schema):
138 class _RegisterForm(formencode.Schema):
139 allow_extra_fields = True
139 allow_extra_fields = True
140 filter_extra_fields = True
140 filter_extra_fields = True
141 username = All(
141 username = All(
142 v.ValidUsername(edit, old_data),
142 v.ValidUsername(edit, old_data),
143 v.UnicodeString(strip=True, min=1, not_empty=True)
143 v.UnicodeString(strip=True, min=1, not_empty=True)
144 )
144 )
145 password = All(
145 password = All(
146 v.ValidPassword(),
146 v.ValidPassword(),
147 v.UnicodeString(strip=False, min=6, not_empty=True)
147 v.UnicodeString(strip=False, min=6, not_empty=True)
148 )
148 )
149 password_confirmation = All(
149 password_confirmation = All(
150 v.ValidPassword(),
150 v.ValidPassword(),
151 v.UnicodeString(strip=False, min=6, not_empty=True)
151 v.UnicodeString(strip=False, min=6, not_empty=True)
152 )
152 )
153 active = v.StringBoolean(if_missing=False)
153 active = v.StringBoolean(if_missing=False)
154 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
154 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
155 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
155 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
156 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
156 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
157
157
158 chained_validators = [v.ValidPasswordsMatch()]
158 chained_validators = [v.ValidPasswordsMatch()]
159
159
160 return _RegisterForm
160 return _RegisterForm
161
161
162
162
163 def PasswordResetForm():
163 def PasswordResetForm():
164 class _PasswordResetForm(formencode.Schema):
164 class _PasswordResetForm(formencode.Schema):
165 allow_extra_fields = True
165 allow_extra_fields = True
166 filter_extra_fields = True
166 filter_extra_fields = True
167 email = All(v.ValidSystemEmail(), v.Email(not_empty=True))
167 email = All(v.ValidSystemEmail(), v.Email(not_empty=True))
168 return _PasswordResetForm
168 return _PasswordResetForm
169
169
170
170
171 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
171 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
172 repo_groups=[], landing_revs=[]):
172 repo_groups=[], landing_revs=[]):
173 class _RepoForm(formencode.Schema):
173 class _RepoForm(formencode.Schema):
174 allow_extra_fields = True
174 allow_extra_fields = True
175 filter_extra_fields = False
175 filter_extra_fields = False
176 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),
177 v.SlugifyName())
177 v.SlugifyName())
178 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))
179 repo_group = v.OneOf(repo_groups, hideList=True)
179 repo_group = v.OneOf(repo_groups, hideList=True)
180 repo_type = v.OneOf(supported_backends)
180 repo_type = v.OneOf(supported_backends)
181 description = v.UnicodeString(strip=True, min=1, not_empty=False)
181 description = v.UnicodeString(strip=True, min=1, not_empty=False)
182 private = v.StringBoolean(if_missing=False)
182 private = v.StringBoolean(if_missing=False)
183 enable_statistics = v.StringBoolean(if_missing=False)
183 enable_statistics = v.StringBoolean(if_missing=False)
184 enable_downloads = v.StringBoolean(if_missing=False)
184 enable_downloads = v.StringBoolean(if_missing=False)
185 landing_rev = v.OneOf(landing_revs, hideList=True)
185 landing_rev = v.OneOf(landing_revs, hideList=True)
186
186
187 if edit:
187 if edit:
188 #this is repo owner
188 #this is repo owner
189 user = All(v.UnicodeString(not_empty=True), v.ValidRepoUser())
189 user = All(v.UnicodeString(not_empty=True), v.ValidRepoUser())
190
190
191 chained_validators = [v.ValidCloneUri(),
191 chained_validators = [v.ValidCloneUri(),
192 v.ValidRepoName(edit, old_data),
192 v.ValidRepoName(edit, old_data),
193 v.ValidPerms()]
193 v.ValidPerms()]
194 return _RepoForm
194 return _RepoForm
195
195
196
196
197 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
197 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
198 repo_groups=[], landing_revs=[]):
198 repo_groups=[], landing_revs=[]):
199 class _RepoForkForm(formencode.Schema):
199 class _RepoForkForm(formencode.Schema):
200 allow_extra_fields = True
200 allow_extra_fields = True
201 filter_extra_fields = False
201 filter_extra_fields = False
202 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),
203 v.SlugifyName())
203 v.SlugifyName())
204 repo_group = v.OneOf(repo_groups, hideList=True)
204 repo_group = v.OneOf(repo_groups, hideList=True)
205 repo_type = All(v.ValidForkType(old_data), v.OneOf(supported_backends))
205 repo_type = All(v.ValidForkType(old_data), v.OneOf(supported_backends))
206 description = v.UnicodeString(strip=True, min=1, not_empty=True)
206 description = v.UnicodeString(strip=True, min=1, not_empty=True)
207 private = v.StringBoolean(if_missing=False)
207 private = v.StringBoolean(if_missing=False)
208 copy_permissions = v.StringBoolean(if_missing=False)
208 copy_permissions = v.StringBoolean(if_missing=False)
209 update_after_clone = v.StringBoolean(if_missing=False)
209 update_after_clone = v.StringBoolean(if_missing=False)
210 fork_parent_id = v.UnicodeString()
210 fork_parent_id = v.UnicodeString()
211 chained_validators = [v.ValidForkName(edit, old_data)]
211 chained_validators = [v.ValidForkName(edit, old_data)]
212 landing_rev = v.OneOf(landing_revs, hideList=True)
212 landing_rev = v.OneOf(landing_revs, hideList=True)
213
213
214 return _RepoForkForm
214 return _RepoForkForm
215
215
216
216
217 def RepoSettingsForm(edit=False, old_data={},
217 def RepoSettingsForm(edit=False, old_data={},
218 supported_backends=BACKENDS.keys(), repo_groups=[],
218 supported_backends=BACKENDS.keys(), repo_groups=[],
219 landing_revs=[]):
219 landing_revs=[]):
220 class _RepoForm(formencode.Schema):
220 class _RepoForm(formencode.Schema):
221 allow_extra_fields = True
221 allow_extra_fields = True
222 filter_extra_fields = False
222 filter_extra_fields = False
223 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),
224 v.SlugifyName())
224 v.SlugifyName())
225 description = v.UnicodeString(strip=True, min=1, not_empty=True)
225 description = v.UnicodeString(strip=True, min=1, not_empty=True)
226 repo_group = v.OneOf(repo_groups, hideList=True)
226 repo_group = v.OneOf(repo_groups, hideList=True)
227 private = v.StringBoolean(if_missing=False)
227 private = v.StringBoolean(if_missing=False)
228 landing_rev = v.OneOf(landing_revs, hideList=True)
228 landing_rev = v.OneOf(landing_revs, hideList=True)
229 chained_validators = [v.ValidRepoName(edit, old_data), v.ValidPerms(),
229 chained_validators = [v.ValidRepoName(edit, old_data), v.ValidPerms(),
230 v.ValidSettings()]
230 v.ValidSettings()]
231 return _RepoForm
231 return _RepoForm
232
232
233
233
234 def ApplicationSettingsForm():
234 def ApplicationSettingsForm():
235 class _ApplicationSettingsForm(formencode.Schema):
235 class _ApplicationSettingsForm(formencode.Schema):
236 allow_extra_fields = True
236 allow_extra_fields = True
237 filter_extra_fields = False
237 filter_extra_fields = False
238 rhodecode_title = v.UnicodeString(strip=True, min=1, not_empty=True)
238 rhodecode_title = v.UnicodeString(strip=True, min=1, not_empty=True)
239 rhodecode_realm = v.UnicodeString(strip=True, min=1, not_empty=True)
239 rhodecode_realm = v.UnicodeString(strip=True, min=1, not_empty=True)
240 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)
241
241
242 return _ApplicationSettingsForm
242 return _ApplicationSettingsForm
243
243
244
244
245 def ApplicationVisualisationForm():
245 def ApplicationVisualisationForm():
246 class _ApplicationVisualisationForm(formencode.Schema):
246 class _ApplicationVisualisationForm(formencode.Schema):
247 allow_extra_fields = True
247 allow_extra_fields = True
248 filter_extra_fields = False
248 filter_extra_fields = False
249 rhodecode_show_public_icon = v.StringBoolean(if_missing=False)
249 rhodecode_show_public_icon = v.StringBoolean(if_missing=False)
250 rhodecode_show_private_icon = v.StringBoolean(if_missing=False)
250 rhodecode_show_private_icon = v.StringBoolean(if_missing=False)
251 rhodecode_stylify_metatags = v.StringBoolean(if_missing=False)
251 rhodecode_stylify_metatags = v.StringBoolean(if_missing=False)
252
252
253 return _ApplicationVisualisationForm
253 return _ApplicationVisualisationForm
254
254
255
255
256 def ApplicationUiSettingsForm():
256 def ApplicationUiSettingsForm():
257 class _ApplicationUiSettingsForm(formencode.Schema):
257 class _ApplicationUiSettingsForm(formencode.Schema):
258 allow_extra_fields = True
258 allow_extra_fields = True
259 filter_extra_fields = False
259 filter_extra_fields = False
260 web_push_ssl = v.StringBoolean(if_missing=False)
260 web_push_ssl = v.StringBoolean(if_missing=False)
261 paths_root_path = All(
261 paths_root_path = All(
262 v.ValidPath(),
262 v.ValidPath(),
263 v.UnicodeString(strip=True, min=1, not_empty=True)
263 v.UnicodeString(strip=True, min=1, not_empty=True)
264 )
264 )
265 hooks_changegroup_update = v.StringBoolean(if_missing=False)
265 hooks_changegroup_update = v.StringBoolean(if_missing=False)
266 hooks_changegroup_repo_size = v.StringBoolean(if_missing=False)
266 hooks_changegroup_repo_size = v.StringBoolean(if_missing=False)
267 hooks_changegroup_push_logger = v.StringBoolean(if_missing=False)
267 hooks_changegroup_push_logger = v.StringBoolean(if_missing=False)
268 hooks_preoutgoing_pull_logger = v.StringBoolean(if_missing=False)
268 hooks_preoutgoing_pull_logger = v.StringBoolean(if_missing=False)
269
269
270 extensions_largefiles = v.StringBoolean(if_missing=False)
271 extensions_hgsubversion = v.StringBoolean(if_missing=False)
272 extensions_hggit = v.StringBoolean(if_missing=False)
273
270 return _ApplicationUiSettingsForm
274 return _ApplicationUiSettingsForm
271
275
272
276
273 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
277 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
274 class _DefaultPermissionsForm(formencode.Schema):
278 class _DefaultPermissionsForm(formencode.Schema):
275 allow_extra_fields = True
279 allow_extra_fields = True
276 filter_extra_fields = True
280 filter_extra_fields = True
277 overwrite_default = v.StringBoolean(if_missing=False)
281 overwrite_default = v.StringBoolean(if_missing=False)
278 anonymous = v.StringBoolean(if_missing=False)
282 anonymous = v.StringBoolean(if_missing=False)
279 default_perm = v.OneOf(perms_choices)
283 default_perm = v.OneOf(perms_choices)
280 default_register = v.OneOf(register_choices)
284 default_register = v.OneOf(register_choices)
281 default_create = v.OneOf(create_choices)
285 default_create = v.OneOf(create_choices)
282
286
283 return _DefaultPermissionsForm
287 return _DefaultPermissionsForm
284
288
285
289
286 def LdapSettingsForm(tls_reqcert_choices, search_scope_choices,
290 def LdapSettingsForm(tls_reqcert_choices, search_scope_choices,
287 tls_kind_choices):
291 tls_kind_choices):
288 class _LdapSettingsForm(formencode.Schema):
292 class _LdapSettingsForm(formencode.Schema):
289 allow_extra_fields = True
293 allow_extra_fields = True
290 filter_extra_fields = True
294 filter_extra_fields = True
291 #pre_validators = [LdapLibValidator]
295 #pre_validators = [LdapLibValidator]
292 ldap_active = v.StringBoolean(if_missing=False)
296 ldap_active = v.StringBoolean(if_missing=False)
293 ldap_host = v.UnicodeString(strip=True,)
297 ldap_host = v.UnicodeString(strip=True,)
294 ldap_port = v.Number(strip=True,)
298 ldap_port = v.Number(strip=True,)
295 ldap_tls_kind = v.OneOf(tls_kind_choices)
299 ldap_tls_kind = v.OneOf(tls_kind_choices)
296 ldap_tls_reqcert = v.OneOf(tls_reqcert_choices)
300 ldap_tls_reqcert = v.OneOf(tls_reqcert_choices)
297 ldap_dn_user = v.UnicodeString(strip=True,)
301 ldap_dn_user = v.UnicodeString(strip=True,)
298 ldap_dn_pass = v.UnicodeString(strip=True,)
302 ldap_dn_pass = v.UnicodeString(strip=True,)
299 ldap_base_dn = v.UnicodeString(strip=True,)
303 ldap_base_dn = v.UnicodeString(strip=True,)
300 ldap_filter = v.UnicodeString(strip=True,)
304 ldap_filter = v.UnicodeString(strip=True,)
301 ldap_search_scope = v.OneOf(search_scope_choices)
305 ldap_search_scope = v.OneOf(search_scope_choices)
302 ldap_attr_login = All(
306 ldap_attr_login = All(
303 v.AttrLoginValidator(),
307 v.AttrLoginValidator(),
304 v.UnicodeString(strip=True,)
308 v.UnicodeString(strip=True,)
305 )
309 )
306 ldap_attr_firstname = v.UnicodeString(strip=True,)
310 ldap_attr_firstname = v.UnicodeString(strip=True,)
307 ldap_attr_lastname = v.UnicodeString(strip=True,)
311 ldap_attr_lastname = v.UnicodeString(strip=True,)
308 ldap_attr_email = v.UnicodeString(strip=True,)
312 ldap_attr_email = v.UnicodeString(strip=True,)
309
313
310 return _LdapSettingsForm
314 return _LdapSettingsForm
311
315
312
316
313 def UserExtraEmailForm():
317 def UserExtraEmailForm():
314 class _UserExtraEmailForm(formencode.Schema):
318 class _UserExtraEmailForm(formencode.Schema):
315 email = All(v.UniqSystemEmail(), v.Email)
319 email = All(v.UniqSystemEmail(), v.Email)
316
320
317 return _UserExtraEmailForm
321 return _UserExtraEmailForm
@@ -1,305 +1,305 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.user_group
3 rhodecode.model.user_group
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 users groups model for RhodeCode
6 users groups model for RhodeCode
7
7
8 :created_on: Jan 25, 2011
8 :created_on: Jan 25, 2011
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import os
26 import os
27 import logging
27 import logging
28 import traceback
28 import traceback
29 import shutil
29 import shutil
30
30
31 from rhodecode.lib.utils2 import LazyProperty
31 from rhodecode.lib.utils2 import LazyProperty
32
32
33 from rhodecode.model import BaseModel
33 from rhodecode.model import BaseModel
34 from rhodecode.model.db import RepoGroup, RhodeCodeUi, UserRepoGroupToPerm, \
34 from rhodecode.model.db import RepoGroup, RhodeCodeUi, UserRepoGroupToPerm, \
35 User, Permission, UsersGroupRepoGroupToPerm, UsersGroup
35 User, Permission, UsersGroupRepoGroupToPerm, UsersGroup
36
36
37 log = logging.getLogger(__name__)
37 log = logging.getLogger(__name__)
38
38
39
39
40 class ReposGroupModel(BaseModel):
40 class ReposGroupModel(BaseModel):
41
41
42 cls = RepoGroup
42 cls = RepoGroup
43
43
44 def __get_users_group(self, users_group):
44 def __get_users_group(self, users_group):
45 return self._get_instance(UsersGroup, users_group,
45 return self._get_instance(UsersGroup, users_group,
46 callback=UsersGroup.get_by_group_name)
46 callback=UsersGroup.get_by_group_name)
47
47
48 def _get_repos_group(self, repos_group):
48 def _get_repos_group(self, repos_group):
49 return self._get_instance(RepoGroup, repos_group,
49 return self._get_instance(RepoGroup, repos_group,
50 callback=RepoGroup.get_by_group_name)
50 callback=RepoGroup.get_by_group_name)
51
51
52 @LazyProperty
52 @LazyProperty
53 def repos_path(self):
53 def repos_path(self):
54 """
54 """
55 Get's the repositories root path from database
55 Get's the repositories root path from database
56 """
56 """
57
57
58 q = RhodeCodeUi.get_by_key('/').one()
58 q = RhodeCodeUi.get_by_key('/')
59 return q.ui_value
59 return q.ui_value
60
60
61 def _create_default_perms(self, new_group):
61 def _create_default_perms(self, new_group):
62 # create default permission
62 # create default permission
63 repo_group_to_perm = UserRepoGroupToPerm()
63 repo_group_to_perm = UserRepoGroupToPerm()
64 default_perm = 'group.read'
64 default_perm = 'group.read'
65 for p in User.get_by_username('default').user_perms:
65 for p in User.get_by_username('default').user_perms:
66 if p.permission.permission_name.startswith('group.'):
66 if p.permission.permission_name.startswith('group.'):
67 default_perm = p.permission.permission_name
67 default_perm = p.permission.permission_name
68 break
68 break
69
69
70 repo_group_to_perm.permission_id = self.sa.query(Permission)\
70 repo_group_to_perm.permission_id = self.sa.query(Permission)\
71 .filter(Permission.permission_name == default_perm)\
71 .filter(Permission.permission_name == default_perm)\
72 .one().permission_id
72 .one().permission_id
73
73
74 repo_group_to_perm.group = new_group
74 repo_group_to_perm.group = new_group
75 repo_group_to_perm.user_id = User.get_by_username('default').user_id
75 repo_group_to_perm.user_id = User.get_by_username('default').user_id
76
76
77 self.sa.add(repo_group_to_perm)
77 self.sa.add(repo_group_to_perm)
78
78
79 def __create_group(self, group_name):
79 def __create_group(self, group_name):
80 """
80 """
81 makes repositories group on filesystem
81 makes repositories group on filesystem
82
82
83 :param repo_name:
83 :param repo_name:
84 :param parent_id:
84 :param parent_id:
85 """
85 """
86
86
87 create_path = os.path.join(self.repos_path, group_name)
87 create_path = os.path.join(self.repos_path, group_name)
88 log.debug('creating new group in %s' % create_path)
88 log.debug('creating new group in %s' % create_path)
89
89
90 if os.path.isdir(create_path):
90 if os.path.isdir(create_path):
91 raise Exception('That directory already exists !')
91 raise Exception('That directory already exists !')
92
92
93 os.makedirs(create_path)
93 os.makedirs(create_path)
94
94
95 def __rename_group(self, old, new):
95 def __rename_group(self, old, new):
96 """
96 """
97 Renames a group on filesystem
97 Renames a group on filesystem
98
98
99 :param group_name:
99 :param group_name:
100 """
100 """
101
101
102 if old == new:
102 if old == new:
103 log.debug('skipping group rename')
103 log.debug('skipping group rename')
104 return
104 return
105
105
106 log.debug('renaming repos group from %s to %s' % (old, new))
106 log.debug('renaming repos group from %s to %s' % (old, new))
107
107
108 old_path = os.path.join(self.repos_path, old)
108 old_path = os.path.join(self.repos_path, old)
109 new_path = os.path.join(self.repos_path, new)
109 new_path = os.path.join(self.repos_path, new)
110
110
111 log.debug('renaming repos paths from %s to %s' % (old_path, new_path))
111 log.debug('renaming repos paths from %s to %s' % (old_path, new_path))
112
112
113 if os.path.isdir(new_path):
113 if os.path.isdir(new_path):
114 raise Exception('Was trying to rename to already '
114 raise Exception('Was trying to rename to already '
115 'existing dir %s' % new_path)
115 'existing dir %s' % new_path)
116 shutil.move(old_path, new_path)
116 shutil.move(old_path, new_path)
117
117
118 def __delete_group(self, group):
118 def __delete_group(self, group):
119 """
119 """
120 Deletes a group from a filesystem
120 Deletes a group from a filesystem
121
121
122 :param group: instance of group from database
122 :param group: instance of group from database
123 """
123 """
124 paths = group.full_path.split(RepoGroup.url_sep())
124 paths = group.full_path.split(RepoGroup.url_sep())
125 paths = os.sep.join(paths)
125 paths = os.sep.join(paths)
126
126
127 rm_path = os.path.join(self.repos_path, paths)
127 rm_path = os.path.join(self.repos_path, paths)
128 if os.path.isdir(rm_path):
128 if os.path.isdir(rm_path):
129 # delete only if that path really exists
129 # delete only if that path really exists
130 os.rmdir(rm_path)
130 os.rmdir(rm_path)
131
131
132 def create(self, group_name, group_description, parent=None, just_db=False):
132 def create(self, group_name, group_description, parent=None, just_db=False):
133 try:
133 try:
134 new_repos_group = RepoGroup()
134 new_repos_group = RepoGroup()
135 new_repos_group.group_description = group_description
135 new_repos_group.group_description = group_description
136 new_repos_group.parent_group = self._get_repos_group(parent)
136 new_repos_group.parent_group = self._get_repos_group(parent)
137 new_repos_group.group_name = new_repos_group.get_new_name(group_name)
137 new_repos_group.group_name = new_repos_group.get_new_name(group_name)
138
138
139 self.sa.add(new_repos_group)
139 self.sa.add(new_repos_group)
140 self._create_default_perms(new_repos_group)
140 self._create_default_perms(new_repos_group)
141
141
142 if not just_db:
142 if not just_db:
143 # we need to flush here, in order to check if database won't
143 # we need to flush here, in order to check if database won't
144 # throw any exceptions, create filesystem dirs at the very end
144 # throw any exceptions, create filesystem dirs at the very end
145 self.sa.flush()
145 self.sa.flush()
146 self.__create_group(new_repos_group.group_name)
146 self.__create_group(new_repos_group.group_name)
147
147
148 return new_repos_group
148 return new_repos_group
149 except:
149 except:
150 log.error(traceback.format_exc())
150 log.error(traceback.format_exc())
151 raise
151 raise
152
152
153 def update(self, repos_group_id, form_data):
153 def update(self, repos_group_id, form_data):
154
154
155 try:
155 try:
156 repos_group = RepoGroup.get(repos_group_id)
156 repos_group = RepoGroup.get(repos_group_id)
157
157
158 # update permissions
158 # update permissions
159 for member, perm, member_type in form_data['perms_updates']:
159 for member, perm, member_type in form_data['perms_updates']:
160 if member_type == 'user':
160 if member_type == 'user':
161 # this updates also current one if found
161 # this updates also current one if found
162 ReposGroupModel().grant_user_permission(
162 ReposGroupModel().grant_user_permission(
163 repos_group=repos_group, user=member, perm=perm
163 repos_group=repos_group, user=member, perm=perm
164 )
164 )
165 else:
165 else:
166 ReposGroupModel().grant_users_group_permission(
166 ReposGroupModel().grant_users_group_permission(
167 repos_group=repos_group, group_name=member, perm=perm
167 repos_group=repos_group, group_name=member, perm=perm
168 )
168 )
169 # set new permissions
169 # set new permissions
170 for member, perm, member_type in form_data['perms_new']:
170 for member, perm, member_type in form_data['perms_new']:
171 if member_type == 'user':
171 if member_type == 'user':
172 ReposGroupModel().grant_user_permission(
172 ReposGroupModel().grant_user_permission(
173 repos_group=repos_group, user=member, perm=perm
173 repos_group=repos_group, user=member, perm=perm
174 )
174 )
175 else:
175 else:
176 ReposGroupModel().grant_users_group_permission(
176 ReposGroupModel().grant_users_group_permission(
177 repos_group=repos_group, group_name=member, perm=perm
177 repos_group=repos_group, group_name=member, perm=perm
178 )
178 )
179
179
180 old_path = repos_group.full_path
180 old_path = repos_group.full_path
181
181
182 # change properties
182 # change properties
183 repos_group.group_description = form_data['group_description']
183 repos_group.group_description = form_data['group_description']
184 repos_group.parent_group = RepoGroup.get(form_data['group_parent_id'])
184 repos_group.parent_group = RepoGroup.get(form_data['group_parent_id'])
185 repos_group.group_parent_id = form_data['group_parent_id']
185 repos_group.group_parent_id = form_data['group_parent_id']
186 repos_group.group_name = repos_group.get_new_name(form_data['group_name'])
186 repos_group.group_name = repos_group.get_new_name(form_data['group_name'])
187 new_path = repos_group.full_path
187 new_path = repos_group.full_path
188
188
189 self.sa.add(repos_group)
189 self.sa.add(repos_group)
190
190
191 # we need to get all repositories from this new group and
191 # we need to get all repositories from this new group and
192 # rename them accordingly to new group path
192 # rename them accordingly to new group path
193 for r in repos_group.repositories:
193 for r in repos_group.repositories:
194 r.repo_name = r.get_new_name(r.just_name)
194 r.repo_name = r.get_new_name(r.just_name)
195 self.sa.add(r)
195 self.sa.add(r)
196
196
197 self.__rename_group(old_path, new_path)
197 self.__rename_group(old_path, new_path)
198
198
199 return repos_group
199 return repos_group
200 except:
200 except:
201 log.error(traceback.format_exc())
201 log.error(traceback.format_exc())
202 raise
202 raise
203
203
204 def delete(self, repos_group):
204 def delete(self, repos_group):
205 repos_group = self._get_repos_group(repos_group)
205 repos_group = self._get_repos_group(repos_group)
206 try:
206 try:
207 self.sa.delete(repos_group)
207 self.sa.delete(repos_group)
208 self.__delete_group(repos_group)
208 self.__delete_group(repos_group)
209 except:
209 except:
210 log.exception('Error removing repos_group %s' % repos_group)
210 log.exception('Error removing repos_group %s' % repos_group)
211 raise
211 raise
212
212
213 def grant_user_permission(self, repos_group, user, perm):
213 def grant_user_permission(self, repos_group, user, perm):
214 """
214 """
215 Grant permission for user on given repositories group, or update
215 Grant permission for user on given repositories group, or update
216 existing one if found
216 existing one if found
217
217
218 :param repos_group: Instance of ReposGroup, repositories_group_id,
218 :param repos_group: Instance of ReposGroup, repositories_group_id,
219 or repositories_group name
219 or repositories_group name
220 :param user: Instance of User, user_id or username
220 :param user: Instance of User, user_id or username
221 :param perm: Instance of Permission, or permission_name
221 :param perm: Instance of Permission, or permission_name
222 """
222 """
223
223
224 repos_group = self._get_repos_group(repos_group)
224 repos_group = self._get_repos_group(repos_group)
225 user = self._get_user(user)
225 user = self._get_user(user)
226 permission = self._get_perm(perm)
226 permission = self._get_perm(perm)
227
227
228 # check if we have that permission already
228 # check if we have that permission already
229 obj = self.sa.query(UserRepoGroupToPerm)\
229 obj = self.sa.query(UserRepoGroupToPerm)\
230 .filter(UserRepoGroupToPerm.user == user)\
230 .filter(UserRepoGroupToPerm.user == user)\
231 .filter(UserRepoGroupToPerm.group == repos_group)\
231 .filter(UserRepoGroupToPerm.group == repos_group)\
232 .scalar()
232 .scalar()
233 if obj is None:
233 if obj is None:
234 # create new !
234 # create new !
235 obj = UserRepoGroupToPerm()
235 obj = UserRepoGroupToPerm()
236 obj.group = repos_group
236 obj.group = repos_group
237 obj.user = user
237 obj.user = user
238 obj.permission = permission
238 obj.permission = permission
239 self.sa.add(obj)
239 self.sa.add(obj)
240
240
241 def revoke_user_permission(self, repos_group, user):
241 def revoke_user_permission(self, repos_group, user):
242 """
242 """
243 Revoke permission for user on given repositories group
243 Revoke permission for user on given repositories group
244
244
245 :param repos_group: Instance of ReposGroup, repositories_group_id,
245 :param repos_group: Instance of ReposGroup, repositories_group_id,
246 or repositories_group name
246 or repositories_group name
247 :param user: Instance of User, user_id or username
247 :param user: Instance of User, user_id or username
248 """
248 """
249
249
250 repos_group = self._get_repos_group(repos_group)
250 repos_group = self._get_repos_group(repos_group)
251 user = self._get_user(user)
251 user = self._get_user(user)
252
252
253 obj = self.sa.query(UserRepoGroupToPerm)\
253 obj = self.sa.query(UserRepoGroupToPerm)\
254 .filter(UserRepoGroupToPerm.user == user)\
254 .filter(UserRepoGroupToPerm.user == user)\
255 .filter(UserRepoGroupToPerm.group == repos_group)\
255 .filter(UserRepoGroupToPerm.group == repos_group)\
256 .one()
256 .one()
257 self.sa.delete(obj)
257 self.sa.delete(obj)
258
258
259 def grant_users_group_permission(self, repos_group, group_name, perm):
259 def grant_users_group_permission(self, repos_group, group_name, perm):
260 """
260 """
261 Grant permission for users group on given repositories group, or update
261 Grant permission for users group on given repositories group, or update
262 existing one if found
262 existing one if found
263
263
264 :param repos_group: Instance of ReposGroup, repositories_group_id,
264 :param repos_group: Instance of ReposGroup, repositories_group_id,
265 or repositories_group name
265 or repositories_group name
266 :param group_name: Instance of UserGroup, users_group_id,
266 :param group_name: Instance of UserGroup, users_group_id,
267 or users group name
267 or users group name
268 :param perm: Instance of Permission, or permission_name
268 :param perm: Instance of Permission, or permission_name
269 """
269 """
270 repos_group = self._get_repos_group(repos_group)
270 repos_group = self._get_repos_group(repos_group)
271 group_name = self.__get_users_group(group_name)
271 group_name = self.__get_users_group(group_name)
272 permission = self._get_perm(perm)
272 permission = self._get_perm(perm)
273
273
274 # check if we have that permission already
274 # check if we have that permission already
275 obj = self.sa.query(UsersGroupRepoGroupToPerm)\
275 obj = self.sa.query(UsersGroupRepoGroupToPerm)\
276 .filter(UsersGroupRepoGroupToPerm.group == repos_group)\
276 .filter(UsersGroupRepoGroupToPerm.group == repos_group)\
277 .filter(UsersGroupRepoGroupToPerm.users_group == group_name)\
277 .filter(UsersGroupRepoGroupToPerm.users_group == group_name)\
278 .scalar()
278 .scalar()
279
279
280 if obj is None:
280 if obj is None:
281 # create new
281 # create new
282 obj = UsersGroupRepoGroupToPerm()
282 obj = UsersGroupRepoGroupToPerm()
283
283
284 obj.group = repos_group
284 obj.group = repos_group
285 obj.users_group = group_name
285 obj.users_group = group_name
286 obj.permission = permission
286 obj.permission = permission
287 self.sa.add(obj)
287 self.sa.add(obj)
288
288
289 def revoke_users_group_permission(self, repos_group, group_name):
289 def revoke_users_group_permission(self, repos_group, group_name):
290 """
290 """
291 Revoke permission for users group on given repositories group
291 Revoke permission for users group on given repositories group
292
292
293 :param repos_group: Instance of ReposGroup, repositories_group_id,
293 :param repos_group: Instance of ReposGroup, repositories_group_id,
294 or repositories_group name
294 or repositories_group name
295 :param group_name: Instance of UserGroup, users_group_id,
295 :param group_name: Instance of UserGroup, users_group_id,
296 or users group name
296 or users group name
297 """
297 """
298 repos_group = self._get_repos_group(repos_group)
298 repos_group = self._get_repos_group(repos_group)
299 group_name = self.__get_users_group(group_name)
299 group_name = self.__get_users_group(group_name)
300
300
301 obj = self.sa.query(UsersGroupRepoGroupToPerm)\
301 obj = self.sa.query(UsersGroupRepoGroupToPerm)\
302 .filter(UsersGroupRepoGroupToPerm.group == repos_group)\
302 .filter(UsersGroupRepoGroupToPerm.group == repos_group)\
303 .filter(UsersGroupRepoGroupToPerm.users_group == group_name)\
303 .filter(UsersGroupRepoGroupToPerm.users_group == group_name)\
304 .one()
304 .one()
305 self.sa.delete(obj)
305 self.sa.delete(obj)
@@ -1,306 +1,327 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 ${_('Settings administration')} - ${c.rhodecode_name}
5 ${_('Settings administration')} - ${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'))} &raquo; ${_('Settings')}
9 ${h.link_to(_('Admin'),h.url('admin_home'))} &raquo; ${_('Settings')}
10 </%def>
10 </%def>
11
11
12 <%def name="page_nav()">
12 <%def name="page_nav()">
13 ${self.menu('admin')}
13 ${self.menu('admin')}
14 </%def>
14 </%def>
15
15
16 <%def name="main()">
16 <%def name="main()">
17 <div class="box">
17 <div class="box">
18 <!-- box / title -->
18 <!-- box / title -->
19 <div class="title">
19 <div class="title">
20 ${self.breadcrumbs()}
20 ${self.breadcrumbs()}
21 </div>
21 </div>
22 <!-- end box / title -->
22 <!-- end box / title -->
23
23
24 <h3>${_('Remap and rescan repositories')}</h3>
24 <h3>${_('Remap and rescan repositories')}</h3>
25 ${h.form(url('admin_setting', setting_id='mapping'),method='put')}
25 ${h.form(url('admin_setting', setting_id='mapping'),method='put')}
26 <div class="form">
26 <div class="form">
27 <!-- fields -->
27 <!-- fields -->
28
28
29 <div class="fields">
29 <div class="fields">
30 <div class="field">
30 <div class="field">
31 <div class="label label-checkbox">
31 <div class="label label-checkbox">
32 <label for="destroy">${_('rescan option')}:</label>
32 <label for="destroy">${_('rescan option')}:</label>
33 </div>
33 </div>
34 <div class="checkboxes">
34 <div class="checkboxes">
35 <div class="checkbox">
35 <div class="checkbox">
36 ${h.checkbox('destroy',True)}
36 ${h.checkbox('destroy',True)}
37 <label for="destroy">
37 <label for="destroy">
38 <span class="tooltip" title="${h.tooltip(_('In case a repository was deleted from filesystem and there are leftovers in the database check this option to scan obsolete data in database and remove it.'))}">
38 <span class="tooltip" title="${h.tooltip(_('In case a repository was deleted from filesystem and there are leftovers in the database check this option to scan obsolete data in database and remove it.'))}">
39 ${_('destroy old data')}</span> </label>
39 ${_('destroy old data')}</span> </label>
40 </div>
40 </div>
41 <span class="help-block">${_('Rescan repositories location for new repositories. Also deletes obsolete if `destroy` flag is checked ')}</span>
41 <span class="help-block">${_('Rescan repositories location for new repositories. Also deletes obsolete if `destroy` flag is checked ')}</span>
42 </div>
42 </div>
43 </div>
43 </div>
44
44
45 <div class="buttons">
45 <div class="buttons">
46 ${h.submit('rescan',_('Rescan repositories'),class_="ui-btn large")}
46 ${h.submit('rescan',_('Rescan repositories'),class_="ui-btn large")}
47 </div>
47 </div>
48 </div>
48 </div>
49 </div>
49 </div>
50 ${h.end_form()}
50 ${h.end_form()}
51
51
52 <h3>${_('Whoosh indexing')}</h3>
52 <h3>${_('Whoosh indexing')}</h3>
53 ${h.form(url('admin_setting', setting_id='whoosh'),method='put')}
53 ${h.form(url('admin_setting', setting_id='whoosh'),method='put')}
54 <div class="form">
54 <div class="form">
55 <!-- fields -->
55 <!-- fields -->
56
56
57 <div class="fields">
57 <div class="fields">
58 <div class="field">
58 <div class="field">
59 <div class="label label-checkbox">
59 <div class="label label-checkbox">
60 <label>${_('index build option')}:</label>
60 <label>${_('index build option')}:</label>
61 </div>
61 </div>
62 <div class="checkboxes">
62 <div class="checkboxes">
63 <div class="checkbox">
63 <div class="checkbox">
64 ${h.checkbox('full_index',True)}
64 ${h.checkbox('full_index',True)}
65 <label for="full_index">${_('build from scratch')}</label>
65 <label for="full_index">${_('build from scratch')}</label>
66 </div>
66 </div>
67 </div>
67 </div>
68 </div>
68 </div>
69
69
70 <div class="buttons">
70 <div class="buttons">
71 ${h.submit('reindex',_('Reindex'),class_="ui-btn large")}
71 ${h.submit('reindex',_('Reindex'),class_="ui-btn large")}
72 </div>
72 </div>
73 </div>
73 </div>
74 </div>
74 </div>
75 ${h.end_form()}
75 ${h.end_form()}
76
76
77 <h3>${_('Global application settings')}</h3>
77 <h3>${_('Global application settings')}</h3>
78 ${h.form(url('admin_setting', setting_id='global'),method='put')}
78 ${h.form(url('admin_setting', setting_id='global'),method='put')}
79 <div class="form">
79 <div class="form">
80 <!-- fields -->
80 <!-- fields -->
81
81
82 <div class="fields">
82 <div class="fields">
83
83
84 <div class="field">
84 <div class="field">
85 <div class="label">
85 <div class="label">
86 <label for="rhodecode_title">${_('Application name')}:</label>
86 <label for="rhodecode_title">${_('Application name')}:</label>
87 </div>
87 </div>
88 <div class="input">
88 <div class="input">
89 ${h.text('rhodecode_title',size=30)}
89 ${h.text('rhodecode_title',size=30)}
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="rhodecode_realm">${_('Realm text')}:</label>
95 <label for="rhodecode_realm">${_('Realm text')}:</label>
96 </div>
96 </div>
97 <div class="input">
97 <div class="input">
98 ${h.text('rhodecode_realm',size=30)}
98 ${h.text('rhodecode_realm',size=30)}
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="rhodecode_ga_code">${_('GA code')}:</label>
104 <label for="rhodecode_ga_code">${_('GA code')}:</label>
105 </div>
105 </div>
106 <div class="input">
106 <div class="input">
107 ${h.text('rhodecode_ga_code',size=30)}
107 ${h.text('rhodecode_ga_code',size=30)}
108 </div>
108 </div>
109 </div>
109 </div>
110
110
111 <div class="buttons">
111 <div class="buttons">
112 ${h.submit('save',_('Save settings'),class_="ui-btn large")}
112 ${h.submit('save',_('Save settings'),class_="ui-btn large")}
113 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
113 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
114 </div>
114 </div>
115 </div>
115 </div>
116 </div>
116 </div>
117 ${h.end_form()}
117 ${h.end_form()}
118
118
119 <h3>${_('Visualisation settings')}</h3>
119 <h3>${_('Visualisation settings')}</h3>
120 ${h.form(url('admin_setting', setting_id='visual'),method='put')}
120 ${h.form(url('admin_setting', setting_id='visual'),method='put')}
121 <div class="form">
121 <div class="form">
122 <!-- fields -->
122 <!-- fields -->
123
123
124 <div class="fields">
124 <div class="fields">
125
125
126 <div class="field">
126 <div class="field">
127 <div class="label label-checkbox">
127 <div class="label label-checkbox">
128 <label>${_('Icons')}:</label>
128 <label>${_('Icons')}:</label>
129 </div>
129 </div>
130 <div class="checkboxes">
130 <div class="checkboxes">
131 <div class="checkbox">
131 <div class="checkbox">
132 ${h.checkbox('rhodecode_show_public_icon','True')}
132 ${h.checkbox('rhodecode_show_public_icon','True')}
133 <label for="rhodecode_show_public_icon">${_('Show public repo icon on repositories')}</label>
133 <label for="rhodecode_show_public_icon">${_('Show public repo icon on repositories')}</label>
134 </div>
134 </div>
135 <div class="checkbox">
135 <div class="checkbox">
136 ${h.checkbox('rhodecode_show_private_icon','True')}
136 ${h.checkbox('rhodecode_show_private_icon','True')}
137 <label for="rhodecode_show_private_icon">${_('Show private repo icon on repositories')}</label>
137 <label for="rhodecode_show_private_icon">${_('Show private repo icon on repositories')}</label>
138 </div>
138 </div>
139 </div>
139 </div>
140 </div>
140 </div>
141
141
142 <div class="field">
142 <div class="field">
143 <div class="label label-checkbox">
143 <div class="label label-checkbox">
144 <label>${_('Meta-Tagging')}:</label>
144 <label>${_('Meta-Tagging')}:</label>
145 </div>
145 </div>
146 <div class="checkboxes">
146 <div class="checkboxes">
147 <div class="checkbox">
147 <div class="checkbox">
148 ${h.checkbox('rhodecode_stylify_metatags','True')}
148 ${h.checkbox('rhodecode_stylify_metatags','True')}
149 <label for="rhodecode_stylify_metatags">${_('Stylify recognised metatags:')}</label>
149 <label for="rhodecode_stylify_metatags">${_('Stylify recognised metatags:')}</label>
150 </div>
150 </div>
151 <div style="padding-left: 20px;">
151 <div style="padding-left: 20px;">
152 <ul> <!-- Fix style here -->
152 <ul> <!-- Fix style here -->
153 <li>[featured] <span class="metatag" tag="featured">featured</span></li>
153 <li>[featured] <span class="metatag" tag="featured">featured</span></li>
154 <li>[stale] <span class="metatag" tag="stale">stale</span></li>
154 <li>[stale] <span class="metatag" tag="stale">stale</span></li>
155 <li>[dead] <span class="metatag" tag="dead">dead</span></li>
155 <li>[dead] <span class="metatag" tag="dead">dead</span></li>
156 <li>[lang =&gt; lang] <span class="metatag" tag="lang" >lang</span></li>
156 <li>[lang =&gt; lang] <span class="metatag" tag="lang" >lang</span></li>
157 <li>[license =&gt; License] <span class="metatag" tag="license"><a href="http://www.opensource.org/licenses/License" >License</a></span></li>
157 <li>[license =&gt; License] <span class="metatag" tag="license"><a href="http://www.opensource.org/licenses/License" >License</a></span></li>
158 <li>[requires =&gt; Repo] <span class="metatag" tag="requires" >requires =&gt; <a href="#" >Repo</a></span></li>
158 <li>[requires =&gt; Repo] <span class="metatag" tag="requires" >requires =&gt; <a href="#" >Repo</a></span></li>
159 <li>[recommends =&gt; Repo] <span class="metatag" tag="recommends" >recommends =&gt; <a href="#" >Repo</a></span></li>
159 <li>[recommends =&gt; Repo] <span class="metatag" tag="recommends" >recommends =&gt; <a href="#" >Repo</a></span></li>
160 <li>[see =&gt; URI] <span class="metatag" tag="see">see =&gt; <a href="#">URI</a> </span></li>
160 <li>[see =&gt; URI] <span class="metatag" tag="see">see =&gt; <a href="#">URI</a> </span></li>
161 </ul>
161 </ul>
162 </div>
162 </div>
163 </div>
163 </div>
164 </div>
164 </div>
165
165
166 <div class="buttons">
166 <div class="buttons">
167 ${h.submit('save',_('Save settings'),class_="ui-btn large")}
167 ${h.submit('save',_('Save settings'),class_="ui-btn large")}
168 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
168 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
169 </div>
169 </div>
170
170
171 </div>
171 </div>
172 </div>
172 </div>
173 ${h.end_form()}
173 ${h.end_form()}
174
174
175
175
176 <h3>${_('VCS settings')}</h3>
176 <h3>${_('VCS settings')}</h3>
177 ${h.form(url('admin_setting', setting_id='vcs'),method='put')}
177 ${h.form(url('admin_setting', setting_id='vcs'),method='put')}
178 <div class="form">
178 <div class="form">
179 <!-- fields -->
179 <!-- fields -->
180
180
181 <div class="fields">
181 <div class="fields">
182
182
183 <div class="field">
183 <div class="field">
184 <div class="label label-checkbox">
184 <div class="label label-checkbox">
185 <label>${_('Web')}:</label>
185 <label>${_('Web')}:</label>
186 </div>
186 </div>
187 <div class="checkboxes">
187 <div class="checkboxes">
188 <div class="checkbox">
188 <div class="checkbox">
189 ${h.checkbox('web_push_ssl','true')}
189 ${h.checkbox('web_push_ssl','true')}
190 <label for="web_push_ssl">${_('require ssl for vcs operations')}</label>
190 <label for="web_push_ssl">${_('require ssl for vcs operations')}</label>
191 </div>
191 </div>
192 <span class="help-block">${_('RhodeCode will require SSL for pushing or pulling. If SSL is missing it will return HTTP Error 406: Not Acceptable')}</span>
192 <span class="help-block">${_('RhodeCode will require SSL for pushing or pulling. If SSL is missing it will return HTTP Error 406: Not Acceptable')}</span>
193 </div>
193 </div>
194 </div>
194 </div>
195
195
196 <div class="field">
196 <div class="field">
197 <div class="label label-checkbox">
197 <div class="label label-checkbox">
198 <label>${_('Hooks')}:</label>
198 <label>${_('Hooks')}:</label>
199 </div>
199 </div>
200 <div class="checkboxes">
200 <div class="checkboxes">
201 <div class="checkbox">
201 <div class="checkbox">
202 ${h.checkbox('hooks_changegroup_update','True')}
202 ${h.checkbox('hooks_changegroup_update','True')}
203 <label for="hooks_changegroup_update">${_('Update repository after push (hg update)')}</label>
203 <label for="hooks_changegroup_update">${_('Update repository after push (hg update)')}</label>
204 </div>
204 </div>
205 <div class="checkbox">
205 <div class="checkbox">
206 ${h.checkbox('hooks_changegroup_repo_size','True')}
206 ${h.checkbox('hooks_changegroup_repo_size','True')}
207 <label for="hooks_changegroup_repo_size">${_('Show repository size after push')}</label>
207 <label for="hooks_changegroup_repo_size">${_('Show repository size after push')}</label>
208 </div>
208 </div>
209 <div class="checkbox">
209 <div class="checkbox">
210 ${h.checkbox('hooks_changegroup_push_logger','True')}
210 ${h.checkbox('hooks_changegroup_push_logger','True')}
211 <label for="hooks_changegroup_push_logger">${_('Log user push commands')}</label>
211 <label for="hooks_changegroup_push_logger">${_('Log user push commands')}</label>
212 </div>
212 </div>
213 <div class="checkbox">
213 <div class="checkbox">
214 ${h.checkbox('hooks_preoutgoing_pull_logger','True')}
214 ${h.checkbox('hooks_preoutgoing_pull_logger','True')}
215 <label for="hooks_preoutgoing_pull_logger">${_('Log user pull commands')}</label>
215 <label for="hooks_preoutgoing_pull_logger">${_('Log user pull commands')}</label>
216 </div>
216 </div>
217 </div>
217 </div>
218 <div class="input" style="margin-top:10px">
218 <div class="input" style="margin-top:10px">
219 ${h.link_to(_('advanced setup'),url('admin_edit_setting',setting_id='hooks'),class_="ui-btn")}
219 ${h.link_to(_('advanced setup'),url('admin_edit_setting',setting_id='hooks'),class_="ui-btn")}
220 </div>
220 </div>
221 </div>
221 </div>
222 <div class="field">
222 <div class="field">
223 <div class="label label-checkbox">
224 <label>${_('Mercurial Extensions')}:</label>
225 </div>
226 <div class="checkboxes">
227 <div class="checkbox">
228 ${h.checkbox('extensions_largefiles','True')}
229 <label for="extensions_hgsubversion">${_('largefiles extensions')}</label>
230 </div>
231 <div class="checkbox">
232 ${h.checkbox('extensions_hgsubversion','True')}
233 <label for="extensions_hgsubversion">${_('hgsubversion extensions')}</label>
234 </div>
235 <span class="help-block">${_('Requires hgsubversion library installed. Allows clonning from svn remote locations')}</span>
236 ##<div class="checkbox">
237 ## ${h.checkbox('extensions_hggit','True')}
238 ## <label for="extensions_hggit">${_('hg-git extensions')}</label>
239 ##</div>
240 ##<span class="help-block">${_('Requires hg-git library installed. Allows clonning from git remote locations')}</span>
241 </div>
242 </div>
243 <div class="field">
223 <div class="label">
244 <div class="label">
224 <label for="paths_root_path">${_('Repositories location')}:</label>
245 <label for="paths_root_path">${_('Repositories location')}:</label>
225 </div>
246 </div>
226 <div class="input">
247 <div class="input">
227 ${h.text('paths_root_path',size=30,readonly="readonly")}
248 ${h.text('paths_root_path',size=30,readonly="readonly")}
228 <span id="path_unlock" class="tooltip"
249 <span id="path_unlock" class="tooltip"
229 title="${h.tooltip(_('This a crucial application setting. If you are really sure you need to change this, you must restart application in order to make this setting take effect. Click this label to unlock.'))}">
250 title="${h.tooltip(_('This a crucial application setting. If you are really sure you need to change this, you must restart application in order to make this setting take effect. Click this label to unlock.'))}">
230 ${_('unlock')}</span>
251 ${_('unlock')}</span>
231 <span class="help-block">${_('Location where repositories are stored. After changing this value a restart, and rescan is required')}</span>
252 <span class="help-block">${_('Location where repositories are stored. After changing this value a restart, and rescan is required')}</span>
232 </div>
253 </div>
233 </div>
254 </div>
234
255
235 <div class="buttons">
256 <div class="buttons">
236 ${h.submit('save',_('Save settings'),class_="ui-btn large")}
257 ${h.submit('save',_('Save settings'),class_="ui-btn large")}
237 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
258 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
238 </div>
259 </div>
239 </div>
260 </div>
240 </div>
261 </div>
241 ${h.end_form()}
262 ${h.end_form()}
242
263
243 <script type="text/javascript">
264 <script type="text/javascript">
244 YAHOO.util.Event.onDOMReady(function(){
265 YAHOO.util.Event.onDOMReady(function(){
245 YAHOO.util.Event.addListener('path_unlock','click',function(){
266 YAHOO.util.Event.addListener('path_unlock','click',function(){
246 YAHOO.util.Dom.get('paths_root_path').removeAttribute('readonly');
267 YAHOO.util.Dom.get('paths_root_path').removeAttribute('readonly');
247 });
268 });
248 });
269 });
249 </script>
270 </script>
250
271
251 <h3>${_('Test Email')}</h3>
272 <h3>${_('Test Email')}</h3>
252 ${h.form(url('admin_setting', setting_id='email'),method='put')}
273 ${h.form(url('admin_setting', setting_id='email'),method='put')}
253 <div class="form">
274 <div class="form">
254 <!-- fields -->
275 <!-- fields -->
255
276
256 <div class="fields">
277 <div class="fields">
257 <div class="field">
278 <div class="field">
258 <div class="label">
279 <div class="label">
259 <label for="test_email">${_('Email to')}:</label>
280 <label for="test_email">${_('Email to')}:</label>
260 </div>
281 </div>
261 <div class="input">
282 <div class="input">
262 ${h.text('test_email',size=30)}
283 ${h.text('test_email',size=30)}
263 </div>
284 </div>
264 </div>
285 </div>
265
286
266 <div class="buttons">
287 <div class="buttons">
267 ${h.submit('send',_('Send'),class_="ui-btn large")}
288 ${h.submit('send',_('Send'),class_="ui-btn large")}
268 </div>
289 </div>
269 </div>
290 </div>
270 </div>
291 </div>
271 ${h.end_form()}
292 ${h.end_form()}
272
293
273 <h3>${_('System Info and Packages')}</h3>
294 <h3>${_('System Info and Packages')}</h3>
274 <div class="form">
295 <div class="form">
275 <div>
296 <div>
276 <h5 id="expand_modules" style="cursor: pointer">&darr; ${_('show')} &darr;</h5>
297 <h5 id="expand_modules" style="cursor: pointer">&darr; ${_('show')} &darr;</h5>
277 </div>
298 </div>
278 <div id="expand_modules_table" style="display:none">
299 <div id="expand_modules_table" style="display:none">
279 <h5>Python - ${c.py_version}</h5>
300 <h5>Python - ${c.py_version}</h5>
280 <h5>System - ${c.platform}</h5>
301 <h5>System - ${c.platform}</h5>
281
302
282 <table class="table" style="margin:0px 0px 0px 20px">
303 <table class="table" style="margin:0px 0px 0px 20px">
283 <colgroup>
304 <colgroup>
284 <col style="width:220px">
305 <col style="width:220px">
285 </colgroup>
306 </colgroup>
286 <tbody>
307 <tbody>
287 %for key, value in c.modules:
308 %for key, value in c.modules:
288 <tr>
309 <tr>
289 <th style="text-align: right;padding-right:5px;">${key}</th>
310 <th style="text-align: right;padding-right:5px;">${key}</th>
290 <td>${value}</td>
311 <td>${value}</td>
291 </tr>
312 </tr>
292 %endfor
313 %endfor
293 </tbody>
314 </tbody>
294 </table>
315 </table>
295 </div>
316 </div>
296 </div>
317 </div>
297
318
298 <script type="text/javascript">
319 <script type="text/javascript">
299 YUE.on('expand_modules','click',function(e){
320 YUE.on('expand_modules','click',function(e){
300 YUD.setStyle('expand_modules_table','display','');
321 YUD.setStyle('expand_modules_table','display','');
301 YUD.setStyle('expand_modules','display','none');
322 YUD.setStyle('expand_modules','display','none');
302 })
323 })
303 </script>
324 </script>
304
325
305 </div>
326 </div>
306 </%def>
327 </%def>
General Comments 0
You need to be logged in to leave comments. Login now