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