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