##// END OF EJS Templates
Removed hardcoded hooks names from settings....
marcink -
r2401:e2af60e4 beta
parent child Browse files
Show More
@@ -1,425 +1,426 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.admin.settings
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 settings controller for rhodecode admin
7 7
8 8 :created_on: Jul 14, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import logging
27 27 import traceback
28 28 import formencode
29 29 import pkg_resources
30 30 import platform
31 31
32 32 from sqlalchemy import func
33 33 from formencode import htmlfill
34 34 from pylons import request, session, tmpl_context as c, url, config
35 35 from pylons.controllers.util import abort, redirect
36 36 from pylons.i18n.translation import _
37 37
38 38 from rhodecode.lib import helpers as h
39 39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
40 40 HasPermissionAnyDecorator, NotAnonymous
41 41 from rhodecode.lib.base import BaseController, render
42 42 from rhodecode.lib.celerylib import tasks, run_task
43 43 from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
44 44 set_rhodecode_config, repo_name_slug
45 45 from rhodecode.model.db import RhodeCodeUi, Repository, RepoGroup, \
46 46 RhodeCodeSetting
47 47 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
48 48 ApplicationUiSettingsForm
49 49 from rhodecode.model.scm import ScmModel
50 50 from rhodecode.model.user import UserModel
51 51 from rhodecode.model.db import User
52 52 from rhodecode.model.notification import EmailNotificationModel
53 53 from rhodecode.model.meta import Session
54 54
55 55 log = logging.getLogger(__name__)
56 56
57 57
58 58 class SettingsController(BaseController):
59 59 """REST Controller styled on the Atom Publishing Protocol"""
60 60 # To properly map this controller, ensure your config/routing.py
61 61 # file has a resource setup:
62 62 # map.resource('setting', 'settings', controller='admin/settings',
63 63 # path_prefix='/admin', name_prefix='admin_')
64 64
65 65 @LoginRequired()
66 66 def __before__(self):
67 67 c.admin_user = session.get('admin_user')
68 68 c.admin_username = session.get('admin_username')
69 69 c.modules = sorted([(p.project_name, p.version)
70 70 for p in pkg_resources.working_set],
71 71 key=lambda k: k[0].lower())
72 72 c.py_version = platform.python_version()
73 73 c.platform = platform.platform()
74 74 super(SettingsController, self).__before__()
75 75
76 76 @HasPermissionAllDecorator('hg.admin')
77 77 def index(self, format='html'):
78 78 """GET /admin/settings: All items in the collection"""
79 79 # url('admin_settings')
80 80
81 81 defaults = RhodeCodeSetting.get_app_settings()
82 82 defaults.update(self.get_hg_ui_settings())
83 83
84 84 return htmlfill.render(
85 85 render('admin/settings/settings.html'),
86 86 defaults=defaults,
87 87 encoding="UTF-8",
88 88 force_defaults=False
89 89 )
90 90
91 91 @HasPermissionAllDecorator('hg.admin')
92 92 def create(self):
93 93 """POST /admin/settings: Create a new item"""
94 94 # url('admin_settings')
95 95
96 96 @HasPermissionAllDecorator('hg.admin')
97 97 def new(self, format='html'):
98 98 """GET /admin/settings/new: Form to create a new item"""
99 99 # url('admin_new_setting')
100 100
101 101 @HasPermissionAllDecorator('hg.admin')
102 102 def update(self, setting_id):
103 103 """PUT /admin/settings/setting_id: Update an existing item"""
104 104 # Forms posted to this method should contain a hidden field:
105 105 # <input type="hidden" name="_method" value="PUT" />
106 106 # Or using helpers:
107 107 # h.form(url('admin_setting', setting_id=ID),
108 108 # method='put')
109 109 # url('admin_setting', setting_id=ID)
110 110 if setting_id == 'mapping':
111 111 rm_obsolete = request.POST.get('destroy', False)
112 112 log.debug('Rescanning directories with destroy=%s' % rm_obsolete)
113 113 initial = ScmModel().repo_scan()
114 114 log.debug('invalidating all repositories')
115 115 for repo_name in initial.keys():
116 116 invalidate_cache('get_repo_cached_%s' % repo_name)
117 117
118 118 added, removed = repo2db_mapper(initial, rm_obsolete)
119 119
120 120 h.flash(_('Repositories successfully'
121 121 ' rescanned added: %s,removed: %s') % (added, removed),
122 122 category='success')
123 123
124 124 if setting_id == 'whoosh':
125 125 repo_location = self.get_hg_ui_settings()['paths_root_path']
126 126 full_index = request.POST.get('full_index', False)
127 127 run_task(tasks.whoosh_index, repo_location, full_index)
128 128
129 129 h.flash(_('Whoosh reindex task scheduled'), category='success')
130 130 if setting_id == 'global':
131 131
132 132 application_form = ApplicationSettingsForm()()
133 133 try:
134 134 form_result = application_form.to_python(dict(request.POST))
135 135
136 136 try:
137 137 hgsettings1 = RhodeCodeSetting.get_by_name('title')
138 138 hgsettings1.app_settings_value = \
139 139 form_result['rhodecode_title']
140 140
141 141 hgsettings2 = RhodeCodeSetting.get_by_name('realm')
142 142 hgsettings2.app_settings_value = \
143 143 form_result['rhodecode_realm']
144 144
145 145 hgsettings3 = RhodeCodeSetting.get_by_name('ga_code')
146 146 hgsettings3.app_settings_value = \
147 147 form_result['rhodecode_ga_code']
148 148
149 149 self.sa.add(hgsettings1)
150 150 self.sa.add(hgsettings2)
151 151 self.sa.add(hgsettings3)
152 152 self.sa.commit()
153 153 set_rhodecode_config(config)
154 154 h.flash(_('Updated application settings'),
155 155 category='success')
156 156
157 157 except Exception:
158 158 log.error(traceback.format_exc())
159 159 h.flash(_('error occurred during updating '
160 160 'application settings'),
161 161 category='error')
162 162
163 163 self.sa.rollback()
164 164
165 165 except formencode.Invalid, errors:
166 166 return htmlfill.render(
167 167 render('admin/settings/settings.html'),
168 168 defaults=errors.value,
169 169 errors=errors.error_dict or {},
170 170 prefix_error=False,
171 171 encoding="UTF-8")
172 172
173 173 if setting_id == 'mercurial':
174 174 application_form = ApplicationUiSettingsForm()()
175 175 try:
176 176 form_result = application_form.to_python(dict(request.POST))
177
177 # fix namespaces for hooks
178 _f = lambda s: s.replace('.', '_')
178 179 try:
179 180
180 181 hgsettings1 = self.sa.query(RhodeCodeUi)\
181 182 .filter(RhodeCodeUi.ui_key == 'push_ssl').one()
182 183 hgsettings1.ui_value = form_result['web_push_ssl']
183 184
184 185 hgsettings2 = self.sa.query(RhodeCodeUi)\
185 186 .filter(RhodeCodeUi.ui_key == '/').one()
186 187 hgsettings2.ui_value = form_result['paths_root_path']
187 188
188 189 #HOOKS
189 190 hgsettings3 = self.sa.query(RhodeCodeUi)\
190 .filter(RhodeCodeUi.ui_key == 'changegroup.update').one()
191 hgsettings3.ui_active = \
192 bool(form_result['hooks_changegroup_update'])
191 .filter(RhodeCodeUi.ui_key == RhodeCodeUi.HOOK_UPDATE)\
192 .one()
193 hgsettings3.ui_active = bool(form_result[_f('hooks_%s' %
194 RhodeCodeUi.HOOK_UPDATE)])
193 195
194 196 hgsettings4 = self.sa.query(RhodeCodeUi)\
195 .filter(RhodeCodeUi.ui_key ==
196 'changegroup.repo_size').one()
197 hgsettings4.ui_active = \
198 bool(form_result['hooks_changegroup_repo_size'])
197 .filter(RhodeCodeUi.ui_key == RhodeCodeUi.HOOK_REPO_SIZE)\
198 .one()
199 hgsettings4.ui_active = bool(form_result[_f('hooks_%s' %
200 RhodeCodeUi.HOOK_REPO_SIZE)])
199 201
200 202 hgsettings5 = self.sa.query(RhodeCodeUi)\
201 .filter(RhodeCodeUi.ui_key ==
202 'pretxnchangegroup.push_logger').one()
203 hgsettings5.ui_active = \
204 bool(form_result['hooks_pretxnchangegroup'
205 '_push_logger'])
203 .filter(RhodeCodeUi.ui_key == RhodeCodeUi.HOOK_PUSH)\
204 .one()
205 hgsettings5.ui_active = bool(form_result[_f('hooks_%s' %
206 RhodeCodeUi.HOOK_PUSH)])
206 207
207 208 hgsettings6 = self.sa.query(RhodeCodeUi)\
208 .filter(RhodeCodeUi.ui_key ==
209 'preoutgoing.pull_logger').one()
210 hgsettings6.ui_active = \
211 bool(form_result['hooks_preoutgoing_pull_logger'])
209 .filter(RhodeCodeUi.ui_key == RhodeCodeUi.HOOK_PULL)\
210 .one()
211 hgsettings6.ui_active = bool(form_result[_f('hooks_%s' %
212 RhodeCodeUi.HOOK_PULL)])
212 213
213 214 self.sa.add(hgsettings1)
214 215 self.sa.add(hgsettings2)
215 216 self.sa.add(hgsettings3)
216 217 self.sa.add(hgsettings4)
217 218 self.sa.add(hgsettings5)
218 219 self.sa.add(hgsettings6)
219 220 self.sa.commit()
220 221
221 222 h.flash(_('Updated mercurial settings'),
222 223 category='success')
223 224
224 225 except:
225 226 log.error(traceback.format_exc())
226 227 h.flash(_('error occurred during updating '
227 228 'application settings'), category='error')
228 229
229 230 self.sa.rollback()
230 231
231 232 except formencode.Invalid, errors:
232 233 return htmlfill.render(
233 234 render('admin/settings/settings.html'),
234 235 defaults=errors.value,
235 236 errors=errors.error_dict or {},
236 237 prefix_error=False,
237 238 encoding="UTF-8")
238 239
239 240 if setting_id == 'hooks':
240 241 ui_key = request.POST.get('new_hook_ui_key')
241 242 ui_value = request.POST.get('new_hook_ui_value')
242 243 try:
243 244
244 245 if ui_value and ui_key:
245 246 RhodeCodeUi.create_or_update_hook(ui_key, ui_value)
246 247 h.flash(_('Added new hook'),
247 248 category='success')
248 249
249 250 # check for edits
250 251 update = False
251 252 _d = request.POST.dict_of_lists()
252 253 for k, v in zip(_d.get('hook_ui_key', []),
253 254 _d.get('hook_ui_value_new', [])):
254 255 RhodeCodeUi.create_or_update_hook(k, v)
255 256 update = True
256 257
257 258 if update:
258 259 h.flash(_('Updated hooks'), category='success')
259 260 self.sa.commit()
260 261 except:
261 262 log.error(traceback.format_exc())
262 263 h.flash(_('error occurred during hook creation'),
263 264 category='error')
264 265
265 266 return redirect(url('admin_edit_setting', setting_id='hooks'))
266 267
267 268 if setting_id == 'email':
268 269 test_email = request.POST.get('test_email')
269 270 test_email_subj = 'RhodeCode TestEmail'
270 271 test_email_body = 'RhodeCode Email test'
271 272
272 273 test_email_html_body = EmailNotificationModel()\
273 274 .get_email_tmpl(EmailNotificationModel.TYPE_DEFAULT,
274 275 body=test_email_body)
275 276
276 277 recipients = [test_email] if [test_email] else None
277 278
278 279 run_task(tasks.send_email, recipients, test_email_subj,
279 280 test_email_body, test_email_html_body)
280 281
281 282 h.flash(_('Email task created'), category='success')
282 283 return redirect(url('admin_settings'))
283 284
284 285 @HasPermissionAllDecorator('hg.admin')
285 286 def delete(self, setting_id):
286 287 """DELETE /admin/settings/setting_id: Delete an existing item"""
287 288 # Forms posted to this method should contain a hidden field:
288 289 # <input type="hidden" name="_method" value="DELETE" />
289 290 # Or using helpers:
290 291 # h.form(url('admin_setting', setting_id=ID),
291 292 # method='delete')
292 293 # url('admin_setting', setting_id=ID)
293 294 if setting_id == 'hooks':
294 295 hook_id = request.POST.get('hook_id')
295 296 RhodeCodeUi.delete(hook_id)
296 297 self.sa.commit()
297 298
298 299 @HasPermissionAllDecorator('hg.admin')
299 300 def show(self, setting_id, format='html'):
300 301 """
301 302 GET /admin/settings/setting_id: Show a specific item"""
302 303 # url('admin_setting', setting_id=ID)
303 304
304 305 @HasPermissionAllDecorator('hg.admin')
305 306 def edit(self, setting_id, format='html'):
306 307 """
307 308 GET /admin/settings/setting_id/edit: Form to
308 309 edit an existing item"""
309 310 # url('admin_edit_setting', setting_id=ID)
310 311
311 312 c.hooks = RhodeCodeUi.get_builtin_hooks()
312 313 c.custom_hooks = RhodeCodeUi.get_custom_hooks()
313 314
314 315 return htmlfill.render(
315 316 render('admin/settings/hooks.html'),
316 317 defaults={},
317 318 encoding="UTF-8",
318 319 force_defaults=False
319 320 )
320 321
321 322 @NotAnonymous()
322 323 def my_account(self):
323 324 """
324 325 GET /_admin/my_account Displays info about my account
325 326 """
326 327 # url('admin_settings_my_account')
327 328
328 329 c.user = User.get(self.rhodecode_user.user_id)
329 330 all_repos = self.sa.query(Repository)\
330 331 .filter(Repository.user_id == c.user.user_id)\
331 332 .order_by(func.lower(Repository.repo_name)).all()
332 333
333 334 c.user_repos = ScmModel().get_repos(all_repos)
334 335
335 336 if c.user.username == 'default':
336 337 h.flash(_("You can't edit this user since it's"
337 338 " crucial for entire application"), category='warning')
338 339 return redirect(url('users'))
339 340
340 341 defaults = c.user.get_dict()
341 342
342 343 c.form = htmlfill.render(
343 344 render('admin/users/user_edit_my_account_form.html'),
344 345 defaults=defaults,
345 346 encoding="UTF-8",
346 347 force_defaults=False
347 348 )
348 349 return render('admin/users/user_edit_my_account.html')
349 350
350 351 def my_account_update(self):
351 352 """PUT /_admin/my_account_update: Update an existing item"""
352 353 # Forms posted to this method should contain a hidden field:
353 354 # <input type="hidden" name="_method" value="PUT" />
354 355 # Or using helpers:
355 356 # h.form(url('admin_settings_my_account_update'),
356 357 # method='put')
357 358 # url('admin_settings_my_account_update', id=ID)
358 359 user_model = UserModel()
359 360 uid = self.rhodecode_user.user_id
360 361 _form = UserForm(edit=True,
361 362 old_data={'user_id': uid,
362 363 'email': self.rhodecode_user.email})()
363 364 form_result = {}
364 365 try:
365 366 form_result = _form.to_python(dict(request.POST))
366 367 user_model.update_my_account(uid, form_result)
367 368 h.flash(_('Your account was updated successfully'),
368 369 category='success')
369 370 Session.commit()
370 371 except formencode.Invalid, errors:
371 372 c.user = User.get(self.rhodecode_user.user_id)
372 373 all_repos = self.sa.query(Repository)\
373 374 .filter(Repository.user_id == c.user.user_id)\
374 375 .order_by(func.lower(Repository.repo_name))\
375 376 .all()
376 377 c.user_repos = ScmModel().get_repos(all_repos)
377 378
378 379 c.form = htmlfill.render(
379 380 render('admin/users/user_edit_my_account_form.html'),
380 381 defaults=errors.value,
381 382 errors=errors.error_dict or {},
382 383 prefix_error=False,
383 384 encoding="UTF-8")
384 385 return render('admin/users/user_edit_my_account.html')
385 386 except Exception:
386 387 log.error(traceback.format_exc())
387 388 h.flash(_('error occurred during update of user %s') \
388 389 % form_result.get('username'), category='error')
389 390
390 391 return redirect(url('my_account'))
391 392
392 393 @NotAnonymous()
393 394 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
394 395 def create_repository(self):
395 396 """GET /_admin/create_repository: Form to create a new item"""
396 397
397 398 c.repo_groups = RepoGroup.groups_choices()
398 399 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
399 400
400 401 new_repo = request.GET.get('repo', '')
401 402 c.new_repo = repo_name_slug(new_repo)
402 403
403 404 return render('admin/repos/repo_add_create_repository.html')
404 405
405 406 def get_hg_ui_settings(self):
406 407 ret = self.sa.query(RhodeCodeUi).all()
407 408
408 409 if not ret:
409 410 raise Exception('Could not get application ui settings !')
410 411 settings = {}
411 412 for each in ret:
412 413 k = each.ui_key
413 414 v = each.ui_value
414 415 if k == '/':
415 416 k = 'root_path'
416 417
417 418 if k.find('.') != -1:
418 419 k = k.replace('.', '_')
419 420
420 421 if each.ui_section == 'hooks':
421 422 v = each.ui_active
422 423
423 424 settings[each.ui_section + '_' + k] = v
424 425
425 426 return settings
@@ -1,1344 +1,1348 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.db
4 4 ~~~~~~~~~~~~~~~~~~
5 5
6 6 Database Models for RhodeCode
7 7
8 8 :created_on: Apr 08, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import os
27 27 import logging
28 28 import datetime
29 29 import traceback
30 30 import hashlib
31 31 from collections import defaultdict
32 32
33 33 from sqlalchemy import *
34 34 from sqlalchemy.ext.hybrid import hybrid_property
35 35 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
36 36 from beaker.cache import cache_region, region_invalidate
37 37
38 38 from rhodecode.lib.vcs import get_backend
39 39 from rhodecode.lib.vcs.utils.helpers import get_scm
40 40 from rhodecode.lib.vcs.exceptions import VCSError
41 41 from rhodecode.lib.vcs.utils.lazy import LazyProperty
42 42
43 43 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
44 44 safe_unicode
45 45 from rhodecode.lib.compat import json
46 46 from rhodecode.lib.caching_query import FromCache
47 47 from rhodecode.model.meta import Base, Session
48 48
49 49
50 50 URL_SEP = '/'
51 51 log = logging.getLogger(__name__)
52 52
53 53 #==============================================================================
54 54 # BASE CLASSES
55 55 #==============================================================================
56 56
57 57 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
58 58
59 59
60 60 class ModelSerializer(json.JSONEncoder):
61 61 """
62 62 Simple Serializer for JSON,
63 63
64 64 usage::
65 65
66 66 to make object customized for serialization implement a __json__
67 67 method that will return a dict for serialization into json
68 68
69 69 example::
70 70
71 71 class Task(object):
72 72
73 73 def __init__(self, name, value):
74 74 self.name = name
75 75 self.value = value
76 76
77 77 def __json__(self):
78 78 return dict(name=self.name,
79 79 value=self.value)
80 80
81 81 """
82 82
83 83 def default(self, obj):
84 84
85 85 if hasattr(obj, '__json__'):
86 86 return obj.__json__()
87 87 else:
88 88 return json.JSONEncoder.default(self, obj)
89 89
90 90
91 91 class BaseModel(object):
92 92 """
93 93 Base Model for all classess
94 94 """
95 95
96 96 @classmethod
97 97 def _get_keys(cls):
98 98 """return column names for this model """
99 99 return class_mapper(cls).c.keys()
100 100
101 101 def get_dict(self):
102 102 """
103 103 return dict with keys and values corresponding
104 104 to this model data """
105 105
106 106 d = {}
107 107 for k in self._get_keys():
108 108 d[k] = getattr(self, k)
109 109
110 110 # also use __json__() if present to get additional fields
111 111 for k, val in getattr(self, '__json__', lambda: {})().iteritems():
112 112 d[k] = val
113 113 return d
114 114
115 115 def get_appstruct(self):
116 116 """return list with keys and values tupples corresponding
117 117 to this model data """
118 118
119 119 l = []
120 120 for k in self._get_keys():
121 121 l.append((k, getattr(self, k),))
122 122 return l
123 123
124 124 def populate_obj(self, populate_dict):
125 125 """populate model with data from given populate_dict"""
126 126
127 127 for k in self._get_keys():
128 128 if k in populate_dict:
129 129 setattr(self, k, populate_dict[k])
130 130
131 131 @classmethod
132 132 def query(cls):
133 133 return Session.query(cls)
134 134
135 135 @classmethod
136 136 def get(cls, id_):
137 137 if id_:
138 138 return cls.query().get(id_)
139 139
140 140 @classmethod
141 141 def getAll(cls):
142 142 return cls.query().all()
143 143
144 144 @classmethod
145 145 def delete(cls, id_):
146 146 obj = cls.query().get(id_)
147 147 Session.delete(obj)
148 148
149 149 def __repr__(self):
150 150 if hasattr(self, '__unicode__'):
151 151 # python repr needs to return str
152 152 return safe_str(self.__unicode__())
153 153 return '<DB:%s>' % (self.__class__.__name__)
154 154
155 155
156 156 class RhodeCodeSetting(Base, BaseModel):
157 157 __tablename__ = 'rhodecode_settings'
158 158 __table_args__ = (
159 159 UniqueConstraint('app_settings_name'),
160 160 {'extend_existing': True, 'mysql_engine': 'InnoDB',
161 161 'mysql_charset': 'utf8'}
162 162 )
163 163 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
164 164 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
165 165 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
166 166
167 167 def __init__(self, k='', v=''):
168 168 self.app_settings_name = k
169 169 self.app_settings_value = v
170 170
171 171 @validates('_app_settings_value')
172 172 def validate_settings_value(self, key, val):
173 173 assert type(val) == unicode
174 174 return val
175 175
176 176 @hybrid_property
177 177 def app_settings_value(self):
178 178 v = self._app_settings_value
179 179 if self.app_settings_name == 'ldap_active':
180 180 v = str2bool(v)
181 181 return v
182 182
183 183 @app_settings_value.setter
184 184 def app_settings_value(self, val):
185 185 """
186 186 Setter that will always make sure we use unicode in app_settings_value
187 187
188 188 :param val:
189 189 """
190 190 self._app_settings_value = safe_unicode(val)
191 191
192 192 def __unicode__(self):
193 193 return u"<%s('%s:%s')>" % (
194 194 self.__class__.__name__,
195 195 self.app_settings_name, self.app_settings_value
196 196 )
197 197
198 198 @classmethod
199 199 def get_by_name(cls, ldap_key):
200 200 return cls.query()\
201 201 .filter(cls.app_settings_name == ldap_key).scalar()
202 202
203 203 @classmethod
204 204 def get_app_settings(cls, cache=False):
205 205
206 206 ret = cls.query()
207 207
208 208 if cache:
209 209 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
210 210
211 211 if not ret:
212 212 raise Exception('Could not get application settings !')
213 213 settings = {}
214 214 for each in ret:
215 215 settings['rhodecode_' + each.app_settings_name] = \
216 216 each.app_settings_value
217 217
218 218 return settings
219 219
220 220 @classmethod
221 221 def get_ldap_settings(cls, cache=False):
222 222 ret = cls.query()\
223 223 .filter(cls.app_settings_name.startswith('ldap_')).all()
224 224 fd = {}
225 225 for row in ret:
226 226 fd.update({row.app_settings_name: row.app_settings_value})
227 227
228 228 return fd
229 229
230 230
231 231 class RhodeCodeUi(Base, BaseModel):
232 232 __tablename__ = 'rhodecode_ui'
233 233 __table_args__ = (
234 234 UniqueConstraint('ui_key'),
235 235 {'extend_existing': True, 'mysql_engine': 'InnoDB',
236 236 'mysql_charset': 'utf8'}
237 237 )
238 238
239 239 HOOK_UPDATE = 'changegroup.update'
240 240 HOOK_REPO_SIZE = 'changegroup.repo_size'
241 241 HOOK_PUSH = 'pretxnchangegroup.push_logger'
242 242 HOOK_PULL = 'preoutgoing.pull_logger'
243 243
244 244 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
245 245 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
246 246 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
247 247 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
248 248 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
249 249
250 250 @classmethod
251 251 def get_by_key(cls, key):
252 252 return cls.query().filter(cls.ui_key == key)
253 253
254 254 @classmethod
255 255 def get_builtin_hooks(cls):
256 256 q = cls.query()
257 257 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
258 258 cls.HOOK_REPO_SIZE,
259 259 cls.HOOK_PUSH, cls.HOOK_PULL]))
260 260 return q.all()
261 261
262 262 @classmethod
263 263 def get_custom_hooks(cls):
264 264 q = cls.query()
265 265 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
266 266 cls.HOOK_REPO_SIZE,
267 267 cls.HOOK_PUSH, cls.HOOK_PULL]))
268 268 q = q.filter(cls.ui_section == 'hooks')
269 269 return q.all()
270 270
271 271 @classmethod
272 def get_repos_location(cls):
273 return cls.get_by_key('/').one().ui_value
274
275 @classmethod
272 276 def create_or_update_hook(cls, key, val):
273 277 new_ui = cls.get_by_key(key).scalar() or cls()
274 278 new_ui.ui_section = 'hooks'
275 279 new_ui.ui_active = True
276 280 new_ui.ui_key = key
277 281 new_ui.ui_value = val
278 282
279 283 Session.add(new_ui)
280 284
281 285
282 286 class User(Base, BaseModel):
283 287 __tablename__ = 'users'
284 288 __table_args__ = (
285 289 UniqueConstraint('username'), UniqueConstraint('email'),
286 290 {'extend_existing': True, 'mysql_engine': 'InnoDB',
287 291 'mysql_charset': 'utf8'}
288 292 )
289 293 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
290 294 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
291 295 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
292 296 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
293 297 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
294 298 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
295 299 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
296 300 _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
297 301 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
298 302 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
299 303 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
300 304
301 305 user_log = relationship('UserLog', cascade='all')
302 306 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
303 307
304 308 repositories = relationship('Repository')
305 309 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
306 310 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
307 311 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
308 312
309 313 group_member = relationship('UsersGroupMember', cascade='all')
310 314
311 315 notifications = relationship('UserNotification', cascade='all')
312 316 # notifications assigned to this user
313 317 user_created_notifications = relationship('Notification', cascade='all')
314 318 # comments created by this user
315 319 user_comments = relationship('ChangesetComment', cascade='all')
316 320
317 321 @hybrid_property
318 322 def email(self):
319 323 return self._email
320 324
321 325 @email.setter
322 326 def email(self, val):
323 327 self._email = val.lower() if val else None
324 328
325 329 @property
326 330 def full_name(self):
327 331 return '%s %s' % (self.name, self.lastname)
328 332
329 333 @property
330 334 def full_name_or_username(self):
331 335 return ('%s %s' % (self.name, self.lastname)
332 336 if (self.name and self.lastname) else self.username)
333 337
334 338 @property
335 339 def full_contact(self):
336 340 return '%s %s <%s>' % (self.name, self.lastname, self.email)
337 341
338 342 @property
339 343 def short_contact(self):
340 344 return '%s %s' % (self.name, self.lastname)
341 345
342 346 @property
343 347 def is_admin(self):
344 348 return self.admin
345 349
346 350 def __unicode__(self):
347 351 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
348 352 self.user_id, self.username)
349 353
350 354 @classmethod
351 355 def get_by_username(cls, username, case_insensitive=False, cache=False):
352 356 if case_insensitive:
353 357 q = cls.query().filter(cls.username.ilike(username))
354 358 else:
355 359 q = cls.query().filter(cls.username == username)
356 360
357 361 if cache:
358 362 q = q.options(FromCache(
359 363 "sql_cache_short",
360 364 "get_user_%s" % _hash_key(username)
361 365 )
362 366 )
363 367 return q.scalar()
364 368
365 369 @classmethod
366 370 def get_by_api_key(cls, api_key, cache=False):
367 371 q = cls.query().filter(cls.api_key == api_key)
368 372
369 373 if cache:
370 374 q = q.options(FromCache("sql_cache_short",
371 375 "get_api_key_%s" % api_key))
372 376 return q.scalar()
373 377
374 378 @classmethod
375 379 def get_by_email(cls, email, case_insensitive=False, cache=False):
376 380 if case_insensitive:
377 381 q = cls.query().filter(cls.email.ilike(email))
378 382 else:
379 383 q = cls.query().filter(cls.email == email)
380 384
381 385 if cache:
382 386 q = q.options(FromCache("sql_cache_short",
383 387 "get_api_key_%s" % email))
384 388 return q.scalar()
385 389
386 390 def update_lastlogin(self):
387 391 """Update user lastlogin"""
388 392 self.last_login = datetime.datetime.now()
389 393 Session.add(self)
390 394 log.debug('updated user %s lastlogin' % self.username)
391 395
392 396 def __json__(self):
393 397 return dict(
394 398 user_id=self.user_id,
395 399 first_name=self.name,
396 400 last_name=self.lastname,
397 401 email=self.email,
398 402 full_name=self.full_name,
399 403 full_name_or_username=self.full_name_or_username,
400 404 short_contact=self.short_contact,
401 405 full_contact=self.full_contact
402 406 )
403 407
404 408
405 409 class UserLog(Base, BaseModel):
406 410 __tablename__ = 'user_logs'
407 411 __table_args__ = (
408 412 {'extend_existing': True, 'mysql_engine': 'InnoDB',
409 413 'mysql_charset': 'utf8'},
410 414 )
411 415 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
412 416 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
413 417 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
414 418 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
415 419 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
416 420 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
417 421 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
418 422
419 423 @property
420 424 def action_as_day(self):
421 425 return datetime.date(*self.action_date.timetuple()[:3])
422 426
423 427 user = relationship('User')
424 428 repository = relationship('Repository', cascade='')
425 429
426 430
427 431 class UsersGroup(Base, BaseModel):
428 432 __tablename__ = 'users_groups'
429 433 __table_args__ = (
430 434 {'extend_existing': True, 'mysql_engine': 'InnoDB',
431 435 'mysql_charset': 'utf8'},
432 436 )
433 437
434 438 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
435 439 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
436 440 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
437 441
438 442 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
439 443 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
440 444 users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
441 445
442 446 def __unicode__(self):
443 447 return u'<userGroup(%s)>' % (self.users_group_name)
444 448
445 449 @classmethod
446 450 def get_by_group_name(cls, group_name, cache=False,
447 451 case_insensitive=False):
448 452 if case_insensitive:
449 453 q = cls.query().filter(cls.users_group_name.ilike(group_name))
450 454 else:
451 455 q = cls.query().filter(cls.users_group_name == group_name)
452 456 if cache:
453 457 q = q.options(FromCache(
454 458 "sql_cache_short",
455 459 "get_user_%s" % _hash_key(group_name)
456 460 )
457 461 )
458 462 return q.scalar()
459 463
460 464 @classmethod
461 465 def get(cls, users_group_id, cache=False):
462 466 users_group = cls.query()
463 467 if cache:
464 468 users_group = users_group.options(FromCache("sql_cache_short",
465 469 "get_users_group_%s" % users_group_id))
466 470 return users_group.get(users_group_id)
467 471
468 472
469 473 class UsersGroupMember(Base, BaseModel):
470 474 __tablename__ = 'users_groups_members'
471 475 __table_args__ = (
472 476 {'extend_existing': True, 'mysql_engine': 'InnoDB',
473 477 'mysql_charset': 'utf8'},
474 478 )
475 479
476 480 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
477 481 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
478 482 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
479 483
480 484 user = relationship('User', lazy='joined')
481 485 users_group = relationship('UsersGroup')
482 486
483 487 def __init__(self, gr_id='', u_id=''):
484 488 self.users_group_id = gr_id
485 489 self.user_id = u_id
486 490
487 491
488 492 class Repository(Base, BaseModel):
489 493 __tablename__ = 'repositories'
490 494 __table_args__ = (
491 495 UniqueConstraint('repo_name'),
492 496 {'extend_existing': True, 'mysql_engine': 'InnoDB',
493 497 'mysql_charset': 'utf8'},
494 498 )
495 499
496 500 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
497 501 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
498 502 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
499 503 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
500 504 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
501 505 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
502 506 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
503 507 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
504 508 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
505 509 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
506 510
507 511 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
508 512 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
509 513
510 514 user = relationship('User')
511 515 fork = relationship('Repository', remote_side=repo_id)
512 516 group = relationship('RepoGroup')
513 517 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
514 518 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
515 519 stats = relationship('Statistics', cascade='all', uselist=False)
516 520
517 521 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
518 522
519 523 logs = relationship('UserLog')
520 524
521 525 def __unicode__(self):
522 526 return u"<%s('%s:%s')>" % (self.__class__.__name__,self.repo_id,
523 527 self.repo_name)
524 528
525 529 @classmethod
526 530 def url_sep(cls):
527 531 return URL_SEP
528 532
529 533 @classmethod
530 534 def get_by_repo_name(cls, repo_name):
531 535 q = Session.query(cls).filter(cls.repo_name == repo_name)
532 536 q = q.options(joinedload(Repository.fork))\
533 537 .options(joinedload(Repository.user))\
534 538 .options(joinedload(Repository.group))
535 539 return q.scalar()
536 540
537 541 @classmethod
538 542 def get_by_full_path(cls, repo_full_path):
539 543 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
540 544 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
541 545
542 546 @classmethod
543 547 def get_repo_forks(cls, repo_id):
544 548 return cls.query().filter(Repository.fork_id == repo_id)
545 549
546 550 @classmethod
547 551 def base_path(cls):
548 552 """
549 553 Returns base path when all repos are stored
550 554
551 555 :param cls:
552 556 """
553 557 q = Session.query(RhodeCodeUi)\
554 558 .filter(RhodeCodeUi.ui_key == cls.url_sep())
555 559 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
556 560 return q.one().ui_value
557 561
558 562 @property
559 563 def just_name(self):
560 564 return self.repo_name.split(Repository.url_sep())[-1]
561 565
562 566 @property
563 567 def groups_with_parents(self):
564 568 groups = []
565 569 if self.group is None:
566 570 return groups
567 571
568 572 cur_gr = self.group
569 573 groups.insert(0, cur_gr)
570 574 while 1:
571 575 gr = getattr(cur_gr, 'parent_group', None)
572 576 cur_gr = cur_gr.parent_group
573 577 if gr is None:
574 578 break
575 579 groups.insert(0, gr)
576 580
577 581 return groups
578 582
579 583 @property
580 584 def groups_and_repo(self):
581 585 return self.groups_with_parents, self.just_name
582 586
583 587 @LazyProperty
584 588 def repo_path(self):
585 589 """
586 590 Returns base full path for that repository means where it actually
587 591 exists on a filesystem
588 592 """
589 593 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
590 594 Repository.url_sep())
591 595 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
592 596 return q.one().ui_value
593 597
594 598 @property
595 599 def repo_full_path(self):
596 600 p = [self.repo_path]
597 601 # we need to split the name by / since this is how we store the
598 602 # names in the database, but that eventually needs to be converted
599 603 # into a valid system path
600 604 p += self.repo_name.split(Repository.url_sep())
601 605 return os.path.join(*p)
602 606
603 607 def get_new_name(self, repo_name):
604 608 """
605 609 returns new full repository name based on assigned group and new new
606 610
607 611 :param group_name:
608 612 """
609 613 path_prefix = self.group.full_path_splitted if self.group else []
610 614 return Repository.url_sep().join(path_prefix + [repo_name])
611 615
612 616 @property
613 617 def _ui(self):
614 618 """
615 619 Creates an db based ui object for this repository
616 620 """
617 621 from mercurial import ui
618 622 from mercurial import config
619 623 baseui = ui.ui()
620 624
621 625 #clean the baseui object
622 626 baseui._ocfg = config.config()
623 627 baseui._ucfg = config.config()
624 628 baseui._tcfg = config.config()
625 629
626 630 ret = RhodeCodeUi.query()\
627 631 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
628 632
629 633 hg_ui = ret
630 634 for ui_ in hg_ui:
631 635 if ui_.ui_active:
632 636 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
633 637 ui_.ui_key, ui_.ui_value)
634 638 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
635 639
636 640 return baseui
637 641
638 642 @classmethod
639 643 def is_valid(cls, repo_name):
640 644 """
641 645 returns True if given repo name is a valid filesystem repository
642 646
643 647 :param cls:
644 648 :param repo_name:
645 649 """
646 650 from rhodecode.lib.utils import is_valid_repo
647 651
648 652 return is_valid_repo(repo_name, cls.base_path())
649 653
650 654 #==========================================================================
651 655 # SCM PROPERTIES
652 656 #==========================================================================
653 657
654 658 def get_changeset(self, rev=None):
655 659 return get_changeset_safe(self.scm_instance, rev)
656 660
657 661 @property
658 662 def tip(self):
659 663 return self.get_changeset('tip')
660 664
661 665 @property
662 666 def author(self):
663 667 return self.tip.author
664 668
665 669 @property
666 670 def last_change(self):
667 671 return self.scm_instance.last_change
668 672
669 673 def comments(self, revisions=None):
670 674 """
671 675 Returns comments for this repository grouped by revisions
672 676
673 677 :param revisions: filter query by revisions only
674 678 """
675 679 cmts = ChangesetComment.query()\
676 680 .filter(ChangesetComment.repo == self)
677 681 if revisions:
678 682 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
679 683 grouped = defaultdict(list)
680 684 for cmt in cmts.all():
681 685 grouped[cmt.revision].append(cmt)
682 686 return grouped
683 687
684 688 #==========================================================================
685 689 # SCM CACHE INSTANCE
686 690 #==========================================================================
687 691
688 692 @property
689 693 def invalidate(self):
690 694 return CacheInvalidation.invalidate(self.repo_name)
691 695
692 696 def set_invalidate(self):
693 697 """
694 698 set a cache for invalidation for this instance
695 699 """
696 700 CacheInvalidation.set_invalidate(self.repo_name)
697 701
698 702 @LazyProperty
699 703 def scm_instance(self):
700 704 return self.__get_instance()
701 705
702 706 def scm_instance_cached(self, cache_map=None):
703 707 @cache_region('long_term')
704 708 def _c(repo_name):
705 709 return self.__get_instance()
706 710 rn = self.repo_name
707 711 log.debug('Getting cached instance of repo')
708 712
709 713 if cache_map:
710 714 # get using prefilled cache_map
711 715 invalidate_repo = cache_map[self.repo_name]
712 716 if invalidate_repo:
713 717 invalidate_repo = (None if invalidate_repo.cache_active
714 718 else invalidate_repo)
715 719 else:
716 720 # get from invalidate
717 721 invalidate_repo = self.invalidate
718 722
719 723 if invalidate_repo is not None:
720 724 region_invalidate(_c, None, rn)
721 725 # update our cache
722 726 CacheInvalidation.set_valid(invalidate_repo.cache_key)
723 727 return _c(rn)
724 728
725 729 def __get_instance(self):
726 730 repo_full_path = self.repo_full_path
727 731 try:
728 732 alias = get_scm(repo_full_path)[0]
729 733 log.debug('Creating instance of %s repository' % alias)
730 734 backend = get_backend(alias)
731 735 except VCSError:
732 736 log.error(traceback.format_exc())
733 737 log.error('Perhaps this repository is in db and not in '
734 738 'filesystem run rescan repositories with '
735 739 '"destroy old data " option from admin panel')
736 740 return
737 741
738 742 if alias == 'hg':
739 743
740 744 repo = backend(safe_str(repo_full_path), create=False,
741 745 baseui=self._ui)
742 746 # skip hidden web repository
743 747 if repo._get_hidden():
744 748 return
745 749 else:
746 750 repo = backend(repo_full_path, create=False)
747 751
748 752 return repo
749 753
750 754
751 755 class RepoGroup(Base, BaseModel):
752 756 __tablename__ = 'groups'
753 757 __table_args__ = (
754 758 UniqueConstraint('group_name', 'group_parent_id'),
755 759 CheckConstraint('group_id != group_parent_id'),
756 760 {'extend_existing': True, 'mysql_engine': 'InnoDB',
757 761 'mysql_charset': 'utf8'},
758 762 )
759 763 __mapper_args__ = {'order_by': 'group_name'}
760 764
761 765 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
762 766 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
763 767 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
764 768 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
765 769
766 770 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
767 771 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
768 772
769 773 parent_group = relationship('RepoGroup', remote_side=group_id)
770 774
771 775 def __init__(self, group_name='', parent_group=None):
772 776 self.group_name = group_name
773 777 self.parent_group = parent_group
774 778
775 779 def __unicode__(self):
776 780 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
777 781 self.group_name)
778 782
779 783 @classmethod
780 784 def groups_choices(cls):
781 785 from webhelpers.html import literal as _literal
782 786 repo_groups = [('', '')]
783 787 sep = ' &raquo; '
784 788 _name = lambda k: _literal(sep.join(k))
785 789
786 790 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
787 791 for x in cls.query().all()])
788 792
789 793 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
790 794 return repo_groups
791 795
792 796 @classmethod
793 797 def url_sep(cls):
794 798 return URL_SEP
795 799
796 800 @classmethod
797 801 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
798 802 if case_insensitive:
799 803 gr = cls.query()\
800 804 .filter(cls.group_name.ilike(group_name))
801 805 else:
802 806 gr = cls.query()\
803 807 .filter(cls.group_name == group_name)
804 808 if cache:
805 809 gr = gr.options(FromCache(
806 810 "sql_cache_short",
807 811 "get_group_%s" % _hash_key(group_name)
808 812 )
809 813 )
810 814 return gr.scalar()
811 815
812 816 @property
813 817 def parents(self):
814 818 parents_recursion_limit = 5
815 819 groups = []
816 820 if self.parent_group is None:
817 821 return groups
818 822 cur_gr = self.parent_group
819 823 groups.insert(0, cur_gr)
820 824 cnt = 0
821 825 while 1:
822 826 cnt += 1
823 827 gr = getattr(cur_gr, 'parent_group', None)
824 828 cur_gr = cur_gr.parent_group
825 829 if gr is None:
826 830 break
827 831 if cnt == parents_recursion_limit:
828 832 # this will prevent accidental infinit loops
829 833 log.error('group nested more than %s' %
830 834 parents_recursion_limit)
831 835 break
832 836
833 837 groups.insert(0, gr)
834 838 return groups
835 839
836 840 @property
837 841 def children(self):
838 842 return RepoGroup.query().filter(RepoGroup.parent_group == self)
839 843
840 844 @property
841 845 def name(self):
842 846 return self.group_name.split(RepoGroup.url_sep())[-1]
843 847
844 848 @property
845 849 def full_path(self):
846 850 return self.group_name
847 851
848 852 @property
849 853 def full_path_splitted(self):
850 854 return self.group_name.split(RepoGroup.url_sep())
851 855
852 856 @property
853 857 def repositories(self):
854 858 return Repository.query()\
855 859 .filter(Repository.group == self)\
856 860 .order_by(Repository.repo_name)
857 861
858 862 @property
859 863 def repositories_recursive_count(self):
860 864 cnt = self.repositories.count()
861 865
862 866 def children_count(group):
863 867 cnt = 0
864 868 for child in group.children:
865 869 cnt += child.repositories.count()
866 870 cnt += children_count(child)
867 871 return cnt
868 872
869 873 return cnt + children_count(self)
870 874
871 875 def get_new_name(self, group_name):
872 876 """
873 877 returns new full group name based on parent and new name
874 878
875 879 :param group_name:
876 880 """
877 881 path_prefix = (self.parent_group.full_path_splitted if
878 882 self.parent_group else [])
879 883 return RepoGroup.url_sep().join(path_prefix + [group_name])
880 884
881 885
882 886 class Permission(Base, BaseModel):
883 887 __tablename__ = 'permissions'
884 888 __table_args__ = (
885 889 {'extend_existing': True, 'mysql_engine': 'InnoDB',
886 890 'mysql_charset': 'utf8'},
887 891 )
888 892 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
889 893 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
890 894 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
891 895
892 896 def __unicode__(self):
893 897 return u"<%s('%s:%s')>" % (
894 898 self.__class__.__name__, self.permission_id, self.permission_name
895 899 )
896 900
897 901 @classmethod
898 902 def get_by_key(cls, key):
899 903 return cls.query().filter(cls.permission_name == key).scalar()
900 904
901 905 @classmethod
902 906 def get_default_perms(cls, default_user_id):
903 907 q = Session.query(UserRepoToPerm, Repository, cls)\
904 908 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
905 909 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
906 910 .filter(UserRepoToPerm.user_id == default_user_id)
907 911
908 912 return q.all()
909 913
910 914 @classmethod
911 915 def get_default_group_perms(cls, default_user_id):
912 916 q = Session.query(UserRepoGroupToPerm, RepoGroup, cls)\
913 917 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
914 918 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
915 919 .filter(UserRepoGroupToPerm.user_id == default_user_id)
916 920
917 921 return q.all()
918 922
919 923
920 924 class UserRepoToPerm(Base, BaseModel):
921 925 __tablename__ = 'repo_to_perm'
922 926 __table_args__ = (
923 927 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
924 928 {'extend_existing': True, 'mysql_engine': 'InnoDB',
925 929 'mysql_charset': 'utf8'}
926 930 )
927 931 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
928 932 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
929 933 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
930 934 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
931 935
932 936 user = relationship('User')
933 937 repository = relationship('Repository')
934 938 permission = relationship('Permission')
935 939
936 940 @classmethod
937 941 def create(cls, user, repository, permission):
938 942 n = cls()
939 943 n.user = user
940 944 n.repository = repository
941 945 n.permission = permission
942 946 Session.add(n)
943 947 return n
944 948
945 949 def __unicode__(self):
946 950 return u'<user:%s => %s >' % (self.user, self.repository)
947 951
948 952
949 953 class UserToPerm(Base, BaseModel):
950 954 __tablename__ = 'user_to_perm'
951 955 __table_args__ = (
952 956 UniqueConstraint('user_id', 'permission_id'),
953 957 {'extend_existing': True, 'mysql_engine': 'InnoDB',
954 958 'mysql_charset': 'utf8'}
955 959 )
956 960 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
957 961 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
958 962 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
959 963
960 964 user = relationship('User')
961 965 permission = relationship('Permission', lazy='joined')
962 966
963 967
964 968 class UsersGroupRepoToPerm(Base, BaseModel):
965 969 __tablename__ = 'users_group_repo_to_perm'
966 970 __table_args__ = (
967 971 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
968 972 {'extend_existing': True, 'mysql_engine': 'InnoDB',
969 973 'mysql_charset': 'utf8'}
970 974 )
971 975 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
972 976 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
973 977 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
974 978 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
975 979
976 980 users_group = relationship('UsersGroup')
977 981 permission = relationship('Permission')
978 982 repository = relationship('Repository')
979 983
980 984 @classmethod
981 985 def create(cls, users_group, repository, permission):
982 986 n = cls()
983 987 n.users_group = users_group
984 988 n.repository = repository
985 989 n.permission = permission
986 990 Session.add(n)
987 991 return n
988 992
989 993 def __unicode__(self):
990 994 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
991 995
992 996
993 997 class UsersGroupToPerm(Base, BaseModel):
994 998 __tablename__ = 'users_group_to_perm'
995 999 __table_args__ = (
996 1000 UniqueConstraint('users_group_id', 'permission_id',),
997 1001 {'extend_existing': True, 'mysql_engine': 'InnoDB',
998 1002 'mysql_charset': 'utf8'}
999 1003 )
1000 1004 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1001 1005 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1002 1006 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1003 1007
1004 1008 users_group = relationship('UsersGroup')
1005 1009 permission = relationship('Permission')
1006 1010
1007 1011
1008 1012 class UserRepoGroupToPerm(Base, BaseModel):
1009 1013 __tablename__ = 'user_repo_group_to_perm'
1010 1014 __table_args__ = (
1011 1015 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1012 1016 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1013 1017 'mysql_charset': 'utf8'}
1014 1018 )
1015 1019
1016 1020 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1017 1021 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1018 1022 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1019 1023 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1020 1024
1021 1025 user = relationship('User')
1022 1026 group = relationship('RepoGroup')
1023 1027 permission = relationship('Permission')
1024 1028
1025 1029
1026 1030 class UsersGroupRepoGroupToPerm(Base, BaseModel):
1027 1031 __tablename__ = 'users_group_repo_group_to_perm'
1028 1032 __table_args__ = (
1029 1033 UniqueConstraint('users_group_id', 'group_id'),
1030 1034 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1031 1035 'mysql_charset': 'utf8'}
1032 1036 )
1033 1037
1034 1038 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)
1035 1039 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1036 1040 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1037 1041 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1038 1042
1039 1043 users_group = relationship('UsersGroup')
1040 1044 permission = relationship('Permission')
1041 1045 group = relationship('RepoGroup')
1042 1046
1043 1047
1044 1048 class Statistics(Base, BaseModel):
1045 1049 __tablename__ = 'statistics'
1046 1050 __table_args__ = (
1047 1051 UniqueConstraint('repository_id'),
1048 1052 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1049 1053 'mysql_charset': 'utf8'}
1050 1054 )
1051 1055 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1052 1056 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1053 1057 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1054 1058 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1055 1059 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1056 1060 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1057 1061
1058 1062 repository = relationship('Repository', single_parent=True)
1059 1063
1060 1064
1061 1065 class UserFollowing(Base, BaseModel):
1062 1066 __tablename__ = 'user_followings'
1063 1067 __table_args__ = (
1064 1068 UniqueConstraint('user_id', 'follows_repository_id'),
1065 1069 UniqueConstraint('user_id', 'follows_user_id'),
1066 1070 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1067 1071 'mysql_charset': 'utf8'}
1068 1072 )
1069 1073
1070 1074 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1071 1075 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1072 1076 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1073 1077 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1074 1078 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1075 1079
1076 1080 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1077 1081
1078 1082 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1079 1083 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1080 1084
1081 1085 @classmethod
1082 1086 def get_repo_followers(cls, repo_id):
1083 1087 return cls.query().filter(cls.follows_repo_id == repo_id)
1084 1088
1085 1089
1086 1090 class CacheInvalidation(Base, BaseModel):
1087 1091 __tablename__ = 'cache_invalidation'
1088 1092 __table_args__ = (
1089 1093 UniqueConstraint('cache_key'),
1090 1094 Index('key_idx', 'cache_key'),
1091 1095 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1092 1096 'mysql_charset': 'utf8'},
1093 1097 )
1094 1098 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1095 1099 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1096 1100 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1097 1101 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1098 1102
1099 1103 def __init__(self, cache_key, cache_args=''):
1100 1104 self.cache_key = cache_key
1101 1105 self.cache_args = cache_args
1102 1106 self.cache_active = False
1103 1107
1104 1108 def __unicode__(self):
1105 1109 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1106 1110 self.cache_id, self.cache_key)
1107 1111
1108 1112 @classmethod
1109 1113 def clear_cache(cls):
1110 1114 cls.query().delete()
1111 1115
1112 1116 @classmethod
1113 1117 def _get_key(cls, key):
1114 1118 """
1115 1119 Wrapper for generating a key, together with a prefix
1116 1120
1117 1121 :param key:
1118 1122 """
1119 1123 import rhodecode
1120 1124 prefix = ''
1121 1125 iid = rhodecode.CONFIG.get('instance_id')
1122 1126 if iid:
1123 1127 prefix = iid
1124 1128 return "%s%s" % (prefix, key), prefix, key.rstrip('_README')
1125 1129
1126 1130 @classmethod
1127 1131 def get_by_key(cls, key):
1128 1132 return cls.query().filter(cls.cache_key == key).scalar()
1129 1133
1130 1134 @classmethod
1131 1135 def _get_or_create_key(cls, key, prefix, org_key):
1132 1136 inv_obj = Session.query(cls).filter(cls.cache_key == key).scalar()
1133 1137 if not inv_obj:
1134 1138 try:
1135 1139 inv_obj = CacheInvalidation(key, org_key)
1136 1140 Session.add(inv_obj)
1137 1141 Session.commit()
1138 1142 except Exception:
1139 1143 log.error(traceback.format_exc())
1140 1144 Session.rollback()
1141 1145 return inv_obj
1142 1146
1143 1147 @classmethod
1144 1148 def invalidate(cls, key):
1145 1149 """
1146 1150 Returns Invalidation object if this given key should be invalidated
1147 1151 None otherwise. `cache_active = False` means that this cache
1148 1152 state is not valid and needs to be invalidated
1149 1153
1150 1154 :param key:
1151 1155 """
1152 1156
1153 1157 key, _prefix, _org_key = cls._get_key(key)
1154 1158 inv = cls._get_or_create_key(key, _prefix, _org_key)
1155 1159
1156 1160 if inv and inv.cache_active is False:
1157 1161 return inv
1158 1162
1159 1163 @classmethod
1160 1164 def set_invalidate(cls, key):
1161 1165 """
1162 1166 Mark this Cache key for invalidation
1163 1167
1164 1168 :param key:
1165 1169 """
1166 1170
1167 1171 key, _prefix, _org_key = cls._get_key(key)
1168 1172 inv_objs = Session.query(cls).filter(cls.cache_args == _org_key).all()
1169 1173 log.debug('marking %s key[s] %s for invalidation' % (len(inv_objs),
1170 1174 _org_key))
1171 1175 try:
1172 1176 for inv_obj in inv_objs:
1173 1177 if inv_obj:
1174 1178 inv_obj.cache_active = False
1175 1179
1176 1180 Session.add(inv_obj)
1177 1181 Session.commit()
1178 1182 except Exception:
1179 1183 log.error(traceback.format_exc())
1180 1184 Session.rollback()
1181 1185
1182 1186 @classmethod
1183 1187 def set_valid(cls, key):
1184 1188 """
1185 1189 Mark this cache key as active and currently cached
1186 1190
1187 1191 :param key:
1188 1192 """
1189 1193 inv_obj = cls.get_by_key(key)
1190 1194 inv_obj.cache_active = True
1191 1195 Session.add(inv_obj)
1192 1196 Session.commit()
1193 1197
1194 1198 @classmethod
1195 1199 def get_cache_map(cls):
1196 1200
1197 1201 class cachemapdict(dict):
1198 1202
1199 1203 def __init__(self, *args, **kwargs):
1200 1204 fixkey = kwargs.get('fixkey')
1201 1205 if fixkey:
1202 1206 del kwargs['fixkey']
1203 1207 self.fixkey = fixkey
1204 1208 super(cachemapdict, self).__init__(*args, **kwargs)
1205 1209
1206 1210 def __getattr__(self, name):
1207 1211 key = name
1208 1212 if self.fixkey:
1209 1213 key, _prefix, _org_key = cls._get_key(key)
1210 1214 if key in self.__dict__:
1211 1215 return self.__dict__[key]
1212 1216 else:
1213 1217 return self[key]
1214 1218
1215 1219 def __getitem__(self, key):
1216 1220 if self.fixkey:
1217 1221 key, _prefix, _org_key = cls._get_key(key)
1218 1222 try:
1219 1223 return super(cachemapdict, self).__getitem__(key)
1220 1224 except KeyError:
1221 1225 return
1222 1226
1223 1227 cache_map = cachemapdict(fixkey=True)
1224 1228 for obj in cls.query().all():
1225 1229 cache_map[obj.cache_key] = cachemapdict(obj.get_dict())
1226 1230 return cache_map
1227 1231
1228 1232
1229 1233 class ChangesetComment(Base, BaseModel):
1230 1234 __tablename__ = 'changeset_comments'
1231 1235 __table_args__ = (
1232 1236 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1233 1237 'mysql_charset': 'utf8'},
1234 1238 )
1235 1239 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1236 1240 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1237 1241 revision = Column('revision', String(40), nullable=False)
1238 1242 line_no = Column('line_no', Unicode(10), nullable=True)
1239 1243 f_path = Column('f_path', Unicode(1000), nullable=True)
1240 1244 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1241 1245 text = Column('text', Unicode(25000), nullable=False)
1242 1246 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1243 1247
1244 1248 author = relationship('User', lazy='joined')
1245 1249 repo = relationship('Repository')
1246 1250
1247 1251 @classmethod
1248 1252 def get_users(cls, revision):
1249 1253 """
1250 1254 Returns user associated with this changesetComment. ie those
1251 1255 who actually commented
1252 1256
1253 1257 :param cls:
1254 1258 :param revision:
1255 1259 """
1256 1260 return Session.query(User)\
1257 1261 .filter(cls.revision == revision)\
1258 1262 .join(ChangesetComment.author).all()
1259 1263
1260 1264
1261 1265 class Notification(Base, BaseModel):
1262 1266 __tablename__ = 'notifications'
1263 1267 __table_args__ = (
1264 1268 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1265 1269 'mysql_charset': 'utf8'},
1266 1270 )
1267 1271
1268 1272 TYPE_CHANGESET_COMMENT = u'cs_comment'
1269 1273 TYPE_MESSAGE = u'message'
1270 1274 TYPE_MENTION = u'mention'
1271 1275 TYPE_REGISTRATION = u'registration'
1272 1276
1273 1277 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1274 1278 subject = Column('subject', Unicode(512), nullable=True)
1275 1279 body = Column('body', Unicode(50000), nullable=True)
1276 1280 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1277 1281 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1278 1282 type_ = Column('type', Unicode(256))
1279 1283
1280 1284 created_by_user = relationship('User')
1281 1285 notifications_to_users = relationship('UserNotification', lazy='joined',
1282 1286 cascade="all, delete, delete-orphan")
1283 1287
1284 1288 @property
1285 1289 def recipients(self):
1286 1290 return [x.user for x in UserNotification.query()\
1287 1291 .filter(UserNotification.notification == self)\
1288 1292 .order_by(UserNotification.user).all()]
1289 1293
1290 1294 @classmethod
1291 1295 def create(cls, created_by, subject, body, recipients, type_=None):
1292 1296 if type_ is None:
1293 1297 type_ = Notification.TYPE_MESSAGE
1294 1298
1295 1299 notification = cls()
1296 1300 notification.created_by_user = created_by
1297 1301 notification.subject = subject
1298 1302 notification.body = body
1299 1303 notification.type_ = type_
1300 1304 notification.created_on = datetime.datetime.now()
1301 1305
1302 1306 for u in recipients:
1303 1307 assoc = UserNotification()
1304 1308 assoc.notification = notification
1305 1309 u.notifications.append(assoc)
1306 1310 Session.add(notification)
1307 1311 return notification
1308 1312
1309 1313 @property
1310 1314 def description(self):
1311 1315 from rhodecode.model.notification import NotificationModel
1312 1316 return NotificationModel().make_description(self)
1313 1317
1314 1318
1315 1319 class UserNotification(Base, BaseModel):
1316 1320 __tablename__ = 'user_to_notification'
1317 1321 __table_args__ = (
1318 1322 UniqueConstraint('user_id', 'notification_id'),
1319 1323 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1320 1324 'mysql_charset': 'utf8'}
1321 1325 )
1322 1326 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1323 1327 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1324 1328 read = Column('read', Boolean, default=False)
1325 1329 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1326 1330
1327 1331 user = relationship('User', lazy="joined")
1328 1332 notification = relationship('Notification', lazy="joined",
1329 1333 order_by=lambda: Notification.created_on.desc(),)
1330 1334
1331 1335 def mark_as_read(self):
1332 1336 self.read = True
1333 1337 Session.add(self)
1334 1338
1335 1339
1336 1340 class DbMigrateVersion(Base, BaseModel):
1337 1341 __tablename__ = 'db_migrate_version'
1338 1342 __table_args__ = (
1339 1343 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1340 1344 'mysql_charset': 'utf8'},
1341 1345 )
1342 1346 repository_id = Column('repository_id', String(250), primary_key=True)
1343 1347 repository_path = Column('repository_path', Text)
1344 1348 version = Column('version', Integer)
General Comments 0
You need to be logged in to leave comments. Login now