##// END OF EJS Templates
merge with beta
marcink -
r2357:ea079c9b merge codereview
parent child Browse files
Show More
@@ -0,0 +1,85 b''
1 <div>
2 ${h.form(url('admin_settings_my_account_update'),method='put')}
3 <div class="form">
4
5 <div class="field">
6 <div class="gravatar_box">
7 <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(c.user.email)}"/></div>
8 <p>
9 %if c.use_gravatar:
10 <strong>${_('Change your avatar at')} <a href="http://gravatar.com">gravatar.com</a></strong>
11 <br/>${_('Using')} ${c.user.email}
12 %else:
13 <br/>${c.user.email}
14 %endif
15 </p>
16 </div>
17 </div>
18 <div class="field">
19 <div class="label">
20 <label>${_('API key')}</label> ${c.user.api_key}
21 </div>
22 </div>
23 <div class="fields">
24 <div class="field">
25 <div class="label">
26 <label for="username">${_('Username')}:</label>
27 </div>
28 <div class="input">
29 ${h.text('username',class_="medium")}
30 </div>
31 </div>
32
33 <div class="field">
34 <div class="label">
35 <label for="new_password">${_('New password')}:</label>
36 </div>
37 <div class="input">
38 ${h.password('new_password',class_="medium",autocomplete="off")}
39 </div>
40 </div>
41
42 <div class="field">
43 <div class="label">
44 <label for="password_confirmation">${_('New password confirmation')}:</label>
45 </div>
46 <div class="input">
47 ${h.password('password_confirmation',class_="medium",autocomplete="off")}
48 </div>
49 </div>
50
51 <div class="field">
52 <div class="label">
53 <label for="name">${_('First Name')}:</label>
54 </div>
55 <div class="input">
56 ${h.text('name',class_="medium")}
57 </div>
58 </div>
59
60 <div class="field">
61 <div class="label">
62 <label for="lastname">${_('Last Name')}:</label>
63 </div>
64 <div class="input">
65 ${h.text('lastname',class_="medium")}
66 </div>
67 </div>
68
69 <div class="field">
70 <div class="label">
71 <label for="email">${_('Email')}:</label>
72 </div>
73 <div class="input">
74 ${h.text('email',class_="medium")}
75 </div>
76 </div>
77
78 <div class="buttons">
79 ${h.submit('save',_('Save'),class_="ui-button")}
80 ${h.reset('reset',_('Reset'),class_="ui-button")}
81 </div>
82 </div>
83 </div>
84 ${h.end_form()}
85 </div> No newline at end of file
@@ -1,124 +1,124 b''
1 .. _installation:
1 .. _installation:
2
2
3 ============
3 ============
4 Installation
4 Installation
5 ============
5 ============
6
6
7 ``RhodeCode`` is written entirely in Python. Before posting any issues make
7 ``RhodeCode`` is written entirely in Python. Before posting any issues make
8 sure, your not missing any system libraries and using right version of
8 sure, your not missing any system libraries and using right version of
9 libraries required by RhodeCode. There's also restriction in terms of mercurial
9 libraries required by RhodeCode. There's also restriction in terms of mercurial
10 clients. Minimal version of hg client known working fine with RhodeCode is
10 clients. Minimal version of hg client known working fine with RhodeCode is
11 **1.6**. If you're using older client, please upgrade.
11 **1.6**. If you're using older client, please upgrade.
12
12
13
13
14 Installing RhodeCode from Cheese Shop
14 Installing RhodeCode from Cheese Shop
15 -------------------------------------
15 -------------------------------------
16
16
17 Rhodecode requires python version 2.5 or higher.
17 Rhodecode requires python version 2.5 or higher.
18
18
19 The easiest way to install ``rhodecode`` is to run::
19 The easiest way to install ``rhodecode`` is to run::
20
20
21 easy_install rhodecode
21 easy_install rhodecode
22
22
23 Or::
23 Or::
24
24
25 pip install rhodecode
25 pip install rhodecode
26
26
27 If you prefer to install RhodeCode manually simply grab latest release from
27 If you prefer to install RhodeCode manually simply grab latest release from
28 http://pypi.python.org/pypi/rhodecode, decompress the archive and run::
28 http://pypi.python.org/pypi/RhodeCode, decompress the archive and run::
29
29
30 python setup.py install
30 python setup.py install
31
31
32
32
33 Step by step installation example
33 Step by step installation example
34 ---------------------------------
34 ---------------------------------
35
35
36
36
37 For installing RhodeCode i highly recommend using separate virtualenv_. This
37 For installing RhodeCode i highly recommend using separate virtualenv_. This
38 way many required by RhodeCode libraries will remain sandboxed from your main
38 way many required by RhodeCode libraries will remain sandboxed from your main
39 python and making things less problematic when doing system python updates.
39 python and making things less problematic when doing system python updates.
40
40
41 - Assuming you have installed virtualenv_ create a new virtual environment
41 - Assuming you have installed virtualenv_ create a new virtual environment
42 using virtualenv command::
42 using virtualenv command::
43
43
44 virtualenv --no-site-packages /var/www/rhodecode-venv
44 virtualenv --no-site-packages /var/www/rhodecode-venv
45
45
46
46
47 .. note:: Using ``--no-site-packages`` when generating your
47 .. note:: Using ``--no-site-packages`` when generating your
48 virtualenv is **very important**. This flag provides the necessary
48 virtualenv is **very important**. This flag provides the necessary
49 isolation for running the set of packages required by
49 isolation for running the set of packages required by
50 RhodeCode. If you do not specify ``--no-site-packages``,
50 RhodeCode. If you do not specify ``--no-site-packages``,
51 it's possible that RhodeCode will not install properly into
51 it's possible that RhodeCode will not install properly into
52 the virtualenv, or, even if it does, may not run properly,
52 the virtualenv, or, even if it does, may not run properly,
53 depending on the packages you've already got installed into your
53 depending on the packages you've already got installed into your
54 Python's "main" site-packages dir.
54 Python's "main" site-packages dir.
55
55
56
56
57 - this will install new virtualenv_ into `/var/www/rhodecode-venv`.
57 - this will install new virtualenv_ into `/var/www/rhodecode-venv`.
58 - Activate the virtualenv_ by running::
58 - Activate the virtualenv_ by running::
59
59
60 source /var/www/rhodecode-venv/bin/activate
60 source /var/www/rhodecode-venv/bin/activate
61
61
62 .. note:: If you're using UNIX, *do not* use ``sudo`` to run the
62 .. note:: If you're using UNIX, *do not* use ``sudo`` to run the
63 ``virtualenv`` script. It's perfectly acceptable (and desirable)
63 ``virtualenv`` script. It's perfectly acceptable (and desirable)
64 to create a virtualenv as a normal user.
64 to create a virtualenv as a normal user.
65
65
66 - Make a folder for rhodecode data files, and configuration somewhere on the
66 - Make a folder for rhodecode data files, and configuration somewhere on the
67 filesystem. For example::
67 filesystem. For example::
68
68
69 mkdir /var/www/rhodecode
69 mkdir /var/www/rhodecode
70
70
71
71
72 - Go into the created directory run this command to install rhodecode::
72 - Go into the created directory run this command to install rhodecode::
73
73
74 easy_install rhodecode
74 easy_install rhodecode
75
75
76 or::
76 or::
77
77
78 pip install rhodecode
78 pip install rhodecode
79
79
80 - This will install rhodecode together with pylons and all other required
80 - This will install rhodecode together with pylons and all other required
81 python libraries into activated virtualenv
81 python libraries into activated virtualenv
82
82
83 Requirements for Celery (optional)
83 Requirements for Celery (optional)
84 ----------------------------------
84 ----------------------------------
85
85
86 In order to gain maximum performance
86 In order to gain maximum performance
87 there are some third-party you must install. When RhodeCode is used
87 there are some third-party you must install. When RhodeCode is used
88 together with celery you have to install some kind of message broker,
88 together with celery you have to install some kind of message broker,
89 recommended one is rabbitmq_ to make the async tasks work.
89 recommended one is rabbitmq_ to make the async tasks work.
90
90
91 Of course RhodeCode works in sync mode also and then you do not have to install
91 Of course RhodeCode works in sync mode also and then you do not have to install
92 any third party applications. However, using Celery_ will give you a large
92 any third party applications. However, using Celery_ will give you a large
93 speed improvement when using many big repositories. If you plan to use
93 speed improvement when using many big repositories. If you plan to use
94 RhodeCode for say 7 to 10 repositories, RhodeCode will perform perfectly well
94 RhodeCode for say 7 to 10 repositories, RhodeCode will perform perfectly well
95 without celery running.
95 without celery running.
96
96
97 If you make the decision to run RhodeCode with celery make sure you run
97 If you make the decision to run RhodeCode with celery make sure you run
98 celeryd using paster and message broker together with the application.
98 celeryd using paster and message broker together with the application.
99
99
100 .. note::
100 .. note::
101 Installing message broker and using celery is optional, RhodeCode will
101 Installing message broker and using celery is optional, RhodeCode will
102 work perfectly fine without them.
102 work perfectly fine without them.
103
103
104
104
105 **Message Broker**
105 **Message Broker**
106
106
107 - preferred is `RabbitMq <http://www.rabbitmq.com/>`_
107 - preferred is `RabbitMq <http://www.rabbitmq.com/>`_
108 - A possible alternative is `Redis <http://code.google.com/p/redis/>`_
108 - A possible alternative is `Redis <http://code.google.com/p/redis/>`_
109
109
110 For installation instructions you can visit:
110 For installation instructions you can visit:
111 http://ask.github.com/celery/getting-started/index.html.
111 http://ask.github.com/celery/getting-started/index.html.
112 This is a very nice tutorial on how to start using celery_ with rabbitmq_
112 This is a very nice tutorial on how to start using celery_ with rabbitmq_
113
113
114
114
115 You can now proceed to :ref:`setup`
115 You can now proceed to :ref:`setup`
116 -----------------------------------
116 -----------------------------------
117
117
118
118
119
119
120 .. _virtualenv: http://pypi.python.org/pypi/virtualenv
120 .. _virtualenv: http://pypi.python.org/pypi/virtualenv
121 .. _python: http://www.python.org/
121 .. _python: http://www.python.org/
122 .. _mercurial: http://mercurial.selenic.com/
122 .. _mercurial: http://mercurial.selenic.com/
123 .. _celery: http://celeryproject.org/
123 .. _celery: http://celeryproject.org/
124 .. _rabbitmq: http://www.rabbitmq.com/ No newline at end of file
124 .. _rabbitmq: http://www.rabbitmq.com/
@@ -1,422 +1,425 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.settings
3 rhodecode.controllers.admin.settings
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 settings controller for rhodecode admin
6 settings controller for rhodecode admin
7
7
8 :created_on: Jul 14, 2010
8 :created_on: Jul 14, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import logging
26 import logging
27 import traceback
27 import traceback
28 import formencode
28 import formencode
29 import pkg_resources
29 import pkg_resources
30 import platform
30 import platform
31
31
32 from sqlalchemy import func
32 from sqlalchemy import func
33 from formencode import htmlfill
33 from formencode import htmlfill
34 from pylons import request, session, tmpl_context as c, url, config
34 from pylons import request, session, tmpl_context as c, url, config
35 from pylons.controllers.util import abort, redirect
35 from pylons.controllers.util import abort, redirect
36 from pylons.i18n.translation import _
36 from pylons.i18n.translation import _
37
37
38 from rhodecode.lib import helpers as h
38 from rhodecode.lib import helpers as h
39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
40 HasPermissionAnyDecorator, NotAnonymous
40 HasPermissionAnyDecorator, NotAnonymous
41 from rhodecode.lib.base import BaseController, render
41 from rhodecode.lib.base import BaseController, render
42 from rhodecode.lib.celerylib import tasks, run_task
42 from rhodecode.lib.celerylib import tasks, run_task
43 from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
43 from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
44 set_rhodecode_config, repo_name_slug
44 set_rhodecode_config, repo_name_slug
45 from rhodecode.model.db import RhodeCodeUi, Repository, RepoGroup, \
45 from rhodecode.model.db import RhodeCodeUi, Repository, RepoGroup, \
46 RhodeCodeSetting
46 RhodeCodeSetting
47 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
47 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
48 ApplicationUiSettingsForm
48 ApplicationUiSettingsForm
49 from rhodecode.model.scm import ScmModel
49 from rhodecode.model.scm import ScmModel
50 from rhodecode.model.user import UserModel
50 from rhodecode.model.user import UserModel
51 from rhodecode.model.db import User
51 from rhodecode.model.db import User
52 from rhodecode.model.notification import EmailNotificationModel
52 from rhodecode.model.notification import EmailNotificationModel
53 from rhodecode.model.meta import Session
53 from rhodecode.model.meta import Session
54
54
55 log = logging.getLogger(__name__)
55 log = logging.getLogger(__name__)
56
56
57
57
58 class SettingsController(BaseController):
58 class SettingsController(BaseController):
59 """REST Controller styled on the Atom Publishing Protocol"""
59 """REST Controller styled on the Atom Publishing Protocol"""
60 # To properly map this controller, ensure your config/routing.py
60 # To properly map this controller, ensure your config/routing.py
61 # file has a resource setup:
61 # file has a resource setup:
62 # map.resource('setting', 'settings', controller='admin/settings',
62 # map.resource('setting', 'settings', controller='admin/settings',
63 # path_prefix='/admin', name_prefix='admin_')
63 # path_prefix='/admin', name_prefix='admin_')
64
64
65 @LoginRequired()
65 @LoginRequired()
66 def __before__(self):
66 def __before__(self):
67 c.admin_user = session.get('admin_user')
67 c.admin_user = session.get('admin_user')
68 c.admin_username = session.get('admin_username')
68 c.admin_username = session.get('admin_username')
69 c.modules = sorted([(p.project_name, p.version)
69 c.modules = sorted([(p.project_name, p.version)
70 for p in pkg_resources.working_set],
70 for p in pkg_resources.working_set],
71 key=lambda k: k[0].lower())
71 key=lambda k: k[0].lower())
72 c.py_version = platform.python_version()
72 c.py_version = platform.python_version()
73 c.platform = platform.platform()
73 c.platform = platform.platform()
74 super(SettingsController, self).__before__()
74 super(SettingsController, self).__before__()
75
75
76 @HasPermissionAllDecorator('hg.admin')
76 @HasPermissionAllDecorator('hg.admin')
77 def index(self, format='html'):
77 def index(self, format='html'):
78 """GET /admin/settings: All items in the collection"""
78 """GET /admin/settings: All items in the collection"""
79 # url('admin_settings')
79 # url('admin_settings')
80
80
81 defaults = RhodeCodeSetting.get_app_settings()
81 defaults = RhodeCodeSetting.get_app_settings()
82 defaults.update(self.get_hg_ui_settings())
82 defaults.update(self.get_hg_ui_settings())
83
83
84 return htmlfill.render(
84 return htmlfill.render(
85 render('admin/settings/settings.html'),
85 render('admin/settings/settings.html'),
86 defaults=defaults,
86 defaults=defaults,
87 encoding="UTF-8",
87 encoding="UTF-8",
88 force_defaults=False
88 force_defaults=False
89 )
89 )
90
90
91 @HasPermissionAllDecorator('hg.admin')
91 @HasPermissionAllDecorator('hg.admin')
92 def create(self):
92 def create(self):
93 """POST /admin/settings: Create a new item"""
93 """POST /admin/settings: Create a new item"""
94 # url('admin_settings')
94 # url('admin_settings')
95
95
96 @HasPermissionAllDecorator('hg.admin')
96 @HasPermissionAllDecorator('hg.admin')
97 def new(self, format='html'):
97 def new(self, format='html'):
98 """GET /admin/settings/new: Form to create a new item"""
98 """GET /admin/settings/new: Form to create a new item"""
99 # url('admin_new_setting')
99 # url('admin_new_setting')
100
100
101 @HasPermissionAllDecorator('hg.admin')
101 @HasPermissionAllDecorator('hg.admin')
102 def update(self, setting_id):
102 def update(self, setting_id):
103 """PUT /admin/settings/setting_id: Update an existing item"""
103 """PUT /admin/settings/setting_id: Update an existing item"""
104 # Forms posted to this method should contain a hidden field:
104 # Forms posted to this method should contain a hidden field:
105 # <input type="hidden" name="_method" value="PUT" />
105 # <input type="hidden" name="_method" value="PUT" />
106 # Or using helpers:
106 # Or using helpers:
107 # h.form(url('admin_setting', setting_id=ID),
107 # h.form(url('admin_setting', setting_id=ID),
108 # method='put')
108 # method='put')
109 # url('admin_setting', setting_id=ID)
109 # url('admin_setting', setting_id=ID)
110 if setting_id == 'mapping':
110 if setting_id == 'mapping':
111 rm_obsolete = request.POST.get('destroy', False)
111 rm_obsolete = request.POST.get('destroy', False)
112 log.debug('Rescanning directories with destroy=%s' % rm_obsolete)
112 log.debug('Rescanning directories with destroy=%s' % rm_obsolete)
113 initial = ScmModel().repo_scan()
113 initial = ScmModel().repo_scan()
114 log.debug('invalidating all repositories')
114 log.debug('invalidating all repositories')
115 for repo_name in initial.keys():
115 for repo_name in initial.keys():
116 invalidate_cache('get_repo_cached_%s' % repo_name)
116 invalidate_cache('get_repo_cached_%s' % repo_name)
117
117
118 added, removed = repo2db_mapper(initial, rm_obsolete)
118 added, removed = repo2db_mapper(initial, rm_obsolete)
119
119
120 h.flash(_('Repositories successfully'
120 h.flash(_('Repositories successfully'
121 ' rescanned added: %s,removed: %s') % (added, removed),
121 ' rescanned added: %s,removed: %s') % (added, removed),
122 category='success')
122 category='success')
123
123
124 if setting_id == 'whoosh':
124 if setting_id == 'whoosh':
125 repo_location = self.get_hg_ui_settings()['paths_root_path']
125 repo_location = self.get_hg_ui_settings()['paths_root_path']
126 full_index = request.POST.get('full_index', False)
126 full_index = request.POST.get('full_index', False)
127 run_task(tasks.whoosh_index, repo_location, full_index)
127 run_task(tasks.whoosh_index, repo_location, full_index)
128
128
129 h.flash(_('Whoosh reindex task scheduled'), category='success')
129 h.flash(_('Whoosh reindex task scheduled'), category='success')
130 if setting_id == 'global':
130 if setting_id == 'global':
131
131
132 application_form = ApplicationSettingsForm()()
132 application_form = ApplicationSettingsForm()()
133 try:
133 try:
134 form_result = application_form.to_python(dict(request.POST))
134 form_result = application_form.to_python(dict(request.POST))
135
135
136 try:
136 try:
137 hgsettings1 = RhodeCodeSetting.get_by_name('title')
137 hgsettings1 = RhodeCodeSetting.get_by_name('title')
138 hgsettings1.app_settings_value = \
138 hgsettings1.app_settings_value = \
139 form_result['rhodecode_title']
139 form_result['rhodecode_title']
140
140
141 hgsettings2 = RhodeCodeSetting.get_by_name('realm')
141 hgsettings2 = RhodeCodeSetting.get_by_name('realm')
142 hgsettings2.app_settings_value = \
142 hgsettings2.app_settings_value = \
143 form_result['rhodecode_realm']
143 form_result['rhodecode_realm']
144
144
145 hgsettings3 = RhodeCodeSetting.get_by_name('ga_code')
145 hgsettings3 = RhodeCodeSetting.get_by_name('ga_code')
146 hgsettings3.app_settings_value = \
146 hgsettings3.app_settings_value = \
147 form_result['rhodecode_ga_code']
147 form_result['rhodecode_ga_code']
148
148
149 self.sa.add(hgsettings1)
149 self.sa.add(hgsettings1)
150 self.sa.add(hgsettings2)
150 self.sa.add(hgsettings2)
151 self.sa.add(hgsettings3)
151 self.sa.add(hgsettings3)
152 self.sa.commit()
152 self.sa.commit()
153 set_rhodecode_config(config)
153 set_rhodecode_config(config)
154 h.flash(_('Updated application settings'),
154 h.flash(_('Updated application settings'),
155 category='success')
155 category='success')
156
156
157 except Exception:
157 except Exception:
158 log.error(traceback.format_exc())
158 log.error(traceback.format_exc())
159 h.flash(_('error occurred during updating '
159 h.flash(_('error occurred during updating '
160 'application settings'),
160 'application settings'),
161 category='error')
161 category='error')
162
162
163 self.sa.rollback()
163 self.sa.rollback()
164
164
165 except formencode.Invalid, errors:
165 except formencode.Invalid, errors:
166 return htmlfill.render(
166 return htmlfill.render(
167 render('admin/settings/settings.html'),
167 render('admin/settings/settings.html'),
168 defaults=errors.value,
168 defaults=errors.value,
169 errors=errors.error_dict or {},
169 errors=errors.error_dict or {},
170 prefix_error=False,
170 prefix_error=False,
171 encoding="UTF-8")
171 encoding="UTF-8")
172
172
173 if setting_id == 'mercurial':
173 if setting_id == 'mercurial':
174 application_form = ApplicationUiSettingsForm()()
174 application_form = ApplicationUiSettingsForm()()
175 try:
175 try:
176 form_result = application_form.to_python(dict(request.POST))
176 form_result = application_form.to_python(dict(request.POST))
177
177
178 try:
178 try:
179
179
180 hgsettings1 = self.sa.query(RhodeCodeUi)\
180 hgsettings1 = self.sa.query(RhodeCodeUi)\
181 .filter(RhodeCodeUi.ui_key == 'push_ssl').one()
181 .filter(RhodeCodeUi.ui_key == 'push_ssl').one()
182 hgsettings1.ui_value = form_result['web_push_ssl']
182 hgsettings1.ui_value = form_result['web_push_ssl']
183
183
184 hgsettings2 = self.sa.query(RhodeCodeUi)\
184 hgsettings2 = self.sa.query(RhodeCodeUi)\
185 .filter(RhodeCodeUi.ui_key == '/').one()
185 .filter(RhodeCodeUi.ui_key == '/').one()
186 hgsettings2.ui_value = form_result['paths_root_path']
186 hgsettings2.ui_value = form_result['paths_root_path']
187
187
188 #HOOKS
188 #HOOKS
189 hgsettings3 = self.sa.query(RhodeCodeUi)\
189 hgsettings3 = self.sa.query(RhodeCodeUi)\
190 .filter(RhodeCodeUi.ui_key == 'changegroup.update').one()
190 .filter(RhodeCodeUi.ui_key == 'changegroup.update').one()
191 hgsettings3.ui_active = \
191 hgsettings3.ui_active = \
192 bool(form_result['hooks_changegroup_update'])
192 bool(form_result['hooks_changegroup_update'])
193
193
194 hgsettings4 = self.sa.query(RhodeCodeUi)\
194 hgsettings4 = self.sa.query(RhodeCodeUi)\
195 .filter(RhodeCodeUi.ui_key ==
195 .filter(RhodeCodeUi.ui_key ==
196 'changegroup.repo_size').one()
196 'changegroup.repo_size').one()
197 hgsettings4.ui_active = \
197 hgsettings4.ui_active = \
198 bool(form_result['hooks_changegroup_repo_size'])
198 bool(form_result['hooks_changegroup_repo_size'])
199
199
200 hgsettings5 = self.sa.query(RhodeCodeUi)\
200 hgsettings5 = self.sa.query(RhodeCodeUi)\
201 .filter(RhodeCodeUi.ui_key ==
201 .filter(RhodeCodeUi.ui_key ==
202 'pretxnchangegroup.push_logger').one()
202 'pretxnchangegroup.push_logger').one()
203 hgsettings5.ui_active = \
203 hgsettings5.ui_active = \
204 bool(form_result['hooks_pretxnchangegroup'
204 bool(form_result['hooks_pretxnchangegroup'
205 '_push_logger'])
205 '_push_logger'])
206
206
207 hgsettings6 = self.sa.query(RhodeCodeUi)\
207 hgsettings6 = self.sa.query(RhodeCodeUi)\
208 .filter(RhodeCodeUi.ui_key ==
208 .filter(RhodeCodeUi.ui_key ==
209 'preoutgoing.pull_logger').one()
209 'preoutgoing.pull_logger').one()
210 hgsettings6.ui_active = \
210 hgsettings6.ui_active = \
211 bool(form_result['hooks_preoutgoing_pull_logger'])
211 bool(form_result['hooks_preoutgoing_pull_logger'])
212
212
213 self.sa.add(hgsettings1)
213 self.sa.add(hgsettings1)
214 self.sa.add(hgsettings2)
214 self.sa.add(hgsettings2)
215 self.sa.add(hgsettings3)
215 self.sa.add(hgsettings3)
216 self.sa.add(hgsettings4)
216 self.sa.add(hgsettings4)
217 self.sa.add(hgsettings5)
217 self.sa.add(hgsettings5)
218 self.sa.add(hgsettings6)
218 self.sa.add(hgsettings6)
219 self.sa.commit()
219 self.sa.commit()
220
220
221 h.flash(_('Updated mercurial settings'),
221 h.flash(_('Updated mercurial settings'),
222 category='success')
222 category='success')
223
223
224 except:
224 except:
225 log.error(traceback.format_exc())
225 log.error(traceback.format_exc())
226 h.flash(_('error occurred during updating '
226 h.flash(_('error occurred during updating '
227 'application settings'), category='error')
227 'application settings'), category='error')
228
228
229 self.sa.rollback()
229 self.sa.rollback()
230
230
231 except formencode.Invalid, errors:
231 except formencode.Invalid, errors:
232 return htmlfill.render(
232 return htmlfill.render(
233 render('admin/settings/settings.html'),
233 render('admin/settings/settings.html'),
234 defaults=errors.value,
234 defaults=errors.value,
235 errors=errors.error_dict or {},
235 errors=errors.error_dict or {},
236 prefix_error=False,
236 prefix_error=False,
237 encoding="UTF-8")
237 encoding="UTF-8")
238
238
239 if setting_id == 'hooks':
239 if setting_id == 'hooks':
240 ui_key = request.POST.get('new_hook_ui_key')
240 ui_key = request.POST.get('new_hook_ui_key')
241 ui_value = request.POST.get('new_hook_ui_value')
241 ui_value = request.POST.get('new_hook_ui_value')
242 try:
242 try:
243
243
244 if ui_value and ui_key:
244 if ui_value and ui_key:
245 RhodeCodeUi.create_or_update_hook(ui_key, ui_value)
245 RhodeCodeUi.create_or_update_hook(ui_key, ui_value)
246 h.flash(_('Added new hook'),
246 h.flash(_('Added new hook'),
247 category='success')
247 category='success')
248
248
249 # check for edits
249 # check for edits
250 update = False
250 update = False
251 _d = request.POST.dict_of_lists()
251 _d = request.POST.dict_of_lists()
252 for k, v in zip(_d.get('hook_ui_key', []),
252 for k, v in zip(_d.get('hook_ui_key', []),
253 _d.get('hook_ui_value_new', [])):
253 _d.get('hook_ui_value_new', [])):
254 RhodeCodeUi.create_or_update_hook(k, v)
254 RhodeCodeUi.create_or_update_hook(k, v)
255 update = True
255 update = True
256
256
257 if update:
257 if update:
258 h.flash(_('Updated hooks'), category='success')
258 h.flash(_('Updated hooks'), category='success')
259 self.sa.commit()
259 self.sa.commit()
260 except:
260 except:
261 log.error(traceback.format_exc())
261 log.error(traceback.format_exc())
262 h.flash(_('error occurred during hook creation'),
262 h.flash(_('error occurred during hook creation'),
263 category='error')
263 category='error')
264
264
265 return redirect(url('admin_edit_setting', setting_id='hooks'))
265 return redirect(url('admin_edit_setting', setting_id='hooks'))
266
266
267 if setting_id == 'email':
267 if setting_id == 'email':
268 test_email = request.POST.get('test_email')
268 test_email = request.POST.get('test_email')
269 test_email_subj = 'RhodeCode TestEmail'
269 test_email_subj = 'RhodeCode TestEmail'
270 test_email_body = 'RhodeCode Email test'
270 test_email_body = 'RhodeCode Email test'
271
271
272 test_email_html_body = EmailNotificationModel()\
272 test_email_html_body = EmailNotificationModel()\
273 .get_email_tmpl(EmailNotificationModel.TYPE_DEFAULT,
273 .get_email_tmpl(EmailNotificationModel.TYPE_DEFAULT,
274 body=test_email_body)
274 body=test_email_body)
275
275
276 recipients = [test_email] if [test_email] else None
276 recipients = [test_email] if [test_email] else None
277
277
278 run_task(tasks.send_email, recipients, test_email_subj,
278 run_task(tasks.send_email, recipients, test_email_subj,
279 test_email_body, test_email_html_body)
279 test_email_body, test_email_html_body)
280
280
281 h.flash(_('Email task created'), category='success')
281 h.flash(_('Email task created'), category='success')
282 return redirect(url('admin_settings'))
282 return redirect(url('admin_settings'))
283
283
284 @HasPermissionAllDecorator('hg.admin')
284 @HasPermissionAllDecorator('hg.admin')
285 def delete(self, setting_id):
285 def delete(self, setting_id):
286 """DELETE /admin/settings/setting_id: Delete an existing item"""
286 """DELETE /admin/settings/setting_id: Delete an existing item"""
287 # Forms posted to this method should contain a hidden field:
287 # Forms posted to this method should contain a hidden field:
288 # <input type="hidden" name="_method" value="DELETE" />
288 # <input type="hidden" name="_method" value="DELETE" />
289 # Or using helpers:
289 # Or using helpers:
290 # h.form(url('admin_setting', setting_id=ID),
290 # h.form(url('admin_setting', setting_id=ID),
291 # method='delete')
291 # method='delete')
292 # url('admin_setting', setting_id=ID)
292 # url('admin_setting', setting_id=ID)
293 if setting_id == 'hooks':
293 if setting_id == 'hooks':
294 hook_id = request.POST.get('hook_id')
294 hook_id = request.POST.get('hook_id')
295 RhodeCodeUi.delete(hook_id)
295 RhodeCodeUi.delete(hook_id)
296 self.sa.commit()
296 self.sa.commit()
297
297
298 @HasPermissionAllDecorator('hg.admin')
298 @HasPermissionAllDecorator('hg.admin')
299 def show(self, setting_id, format='html'):
299 def show(self, setting_id, format='html'):
300 """
300 """
301 GET /admin/settings/setting_id: Show a specific item"""
301 GET /admin/settings/setting_id: Show a specific item"""
302 # url('admin_setting', setting_id=ID)
302 # url('admin_setting', setting_id=ID)
303
303
304 @HasPermissionAllDecorator('hg.admin')
304 @HasPermissionAllDecorator('hg.admin')
305 def edit(self, setting_id, format='html'):
305 def edit(self, setting_id, format='html'):
306 """
306 """
307 GET /admin/settings/setting_id/edit: Form to
307 GET /admin/settings/setting_id/edit: Form to
308 edit an existing item"""
308 edit an existing item"""
309 # url('admin_edit_setting', setting_id=ID)
309 # url('admin_edit_setting', setting_id=ID)
310
310
311 c.hooks = RhodeCodeUi.get_builtin_hooks()
311 c.hooks = RhodeCodeUi.get_builtin_hooks()
312 c.custom_hooks = RhodeCodeUi.get_custom_hooks()
312 c.custom_hooks = RhodeCodeUi.get_custom_hooks()
313
313
314 return htmlfill.render(
314 return htmlfill.render(
315 render('admin/settings/hooks.html'),
315 render('admin/settings/hooks.html'),
316 defaults={},
316 defaults={},
317 encoding="UTF-8",
317 encoding="UTF-8",
318 force_defaults=False
318 force_defaults=False
319 )
319 )
320
320
321 @NotAnonymous()
321 @NotAnonymous()
322 def my_account(self):
322 def my_account(self):
323 """
323 """
324 GET /_admin/my_account Displays info about my account
324 GET /_admin/my_account Displays info about my account
325 """
325 """
326 # url('admin_settings_my_account')
326 # url('admin_settings_my_account')
327
327
328 c.user = User.get(self.rhodecode_user.user_id)
328 c.user = User.get(self.rhodecode_user.user_id)
329 all_repos = self.sa.query(Repository)\
329 all_repos = self.sa.query(Repository)\
330 .filter(Repository.user_id == c.user.user_id)\
330 .filter(Repository.user_id == c.user.user_id)\
331 .order_by(func.lower(Repository.repo_name)).all()
331 .order_by(func.lower(Repository.repo_name)).all()
332
332
333 c.user_repos = ScmModel().get_repos(all_repos)
333 c.user_repos = ScmModel().get_repos(all_repos)
334
334
335 if c.user.username == 'default':
335 if c.user.username == 'default':
336 h.flash(_("You can't edit this user since it's"
336 h.flash(_("You can't edit this user since it's"
337 " crucial for entire application"), category='warning')
337 " crucial for entire application"), category='warning')
338 return redirect(url('users'))
338 return redirect(url('users'))
339
339
340 defaults = c.user.get_dict()
340 defaults = c.user.get_dict()
341 return htmlfill.render(
341
342 render('admin/users/user_edit_my_account.html'),
342 c.form = htmlfill.render(
343 render('admin/users/user_edit_my_account_form.html'),
343 defaults=defaults,
344 defaults=defaults,
344 encoding="UTF-8",
345 encoding="UTF-8",
345 force_defaults=False
346 force_defaults=False
346 )
347 )
348 return render('admin/users/user_edit_my_account.html')
347
349
348 def my_account_update(self):
350 def my_account_update(self):
349 """PUT /_admin/my_account_update: Update an existing item"""
351 """PUT /_admin/my_account_update: Update an existing item"""
350 # Forms posted to this method should contain a hidden field:
352 # Forms posted to this method should contain a hidden field:
351 # <input type="hidden" name="_method" value="PUT" />
353 # <input type="hidden" name="_method" value="PUT" />
352 # Or using helpers:
354 # Or using helpers:
353 # h.form(url('admin_settings_my_account_update'),
355 # h.form(url('admin_settings_my_account_update'),
354 # method='put')
356 # method='put')
355 # url('admin_settings_my_account_update', id=ID)
357 # url('admin_settings_my_account_update', id=ID)
356 user_model = UserModel()
358 user_model = UserModel()
357 uid = self.rhodecode_user.user_id
359 uid = self.rhodecode_user.user_id
358 _form = UserForm(edit=True,
360 _form = UserForm(edit=True,
359 old_data={'user_id': uid,
361 old_data={'user_id': uid,
360 'email': self.rhodecode_user.email})()
362 'email': self.rhodecode_user.email})()
361 form_result = {}
363 form_result = {}
362 try:
364 try:
363 form_result = _form.to_python(dict(request.POST))
365 form_result = _form.to_python(dict(request.POST))
364 user_model.update_my_account(uid, form_result)
366 user_model.update_my_account(uid, form_result)
365 h.flash(_('Your account was updated successfully'),
367 h.flash(_('Your account was updated successfully'),
366 category='success')
368 category='success')
367 Session.commit()
369 Session.commit()
368 except formencode.Invalid, errors:
370 except formencode.Invalid, errors:
369 c.user = User.get(self.rhodecode_user.user_id)
371 c.user = User.get(self.rhodecode_user.user_id)
370 all_repos = self.sa.query(Repository)\
372 all_repos = self.sa.query(Repository)\
371 .filter(Repository.user_id == c.user.user_id)\
373 .filter(Repository.user_id == c.user.user_id)\
372 .order_by(func.lower(Repository.repo_name))\
374 .order_by(func.lower(Repository.repo_name))\
373 .all()
375 .all()
374 c.user_repos = ScmModel().get_repos(all_repos)
376 c.user_repos = ScmModel().get_repos(all_repos)
375
377
376 return htmlfill.render(
378 c.form = htmlfill.render(
377 render('admin/users/user_edit_my_account.html'),
379 render('admin/users/user_edit_my_account_form.html'),
378 defaults=errors.value,
380 defaults=errors.value,
379 errors=errors.error_dict or {},
381 errors=errors.error_dict or {},
380 prefix_error=False,
382 prefix_error=False,
381 encoding="UTF-8")
383 encoding="UTF-8")
384 return render('admin/users/user_edit_my_account.html')
382 except Exception:
385 except Exception:
383 log.error(traceback.format_exc())
386 log.error(traceback.format_exc())
384 h.flash(_('error occurred during update of user %s') \
387 h.flash(_('error occurred during update of user %s') \
385 % form_result.get('username'), category='error')
388 % form_result.get('username'), category='error')
386
389
387 return redirect(url('my_account'))
390 return redirect(url('my_account'))
388
391
389 @NotAnonymous()
392 @NotAnonymous()
390 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
393 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
391 def create_repository(self):
394 def create_repository(self):
392 """GET /_admin/create_repository: Form to create a new item"""
395 """GET /_admin/create_repository: Form to create a new item"""
393
396
394 c.repo_groups = RepoGroup.groups_choices()
397 c.repo_groups = RepoGroup.groups_choices()
395 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
398 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
396
399
397 new_repo = request.GET.get('repo', '')
400 new_repo = request.GET.get('repo', '')
398 c.new_repo = repo_name_slug(new_repo)
401 c.new_repo = repo_name_slug(new_repo)
399
402
400 return render('admin/repos/repo_add_create_repository.html')
403 return render('admin/repos/repo_add_create_repository.html')
401
404
402 def get_hg_ui_settings(self):
405 def get_hg_ui_settings(self):
403 ret = self.sa.query(RhodeCodeUi).all()
406 ret = self.sa.query(RhodeCodeUi).all()
404
407
405 if not ret:
408 if not ret:
406 raise Exception('Could not get application ui settings !')
409 raise Exception('Could not get application ui settings !')
407 settings = {}
410 settings = {}
408 for each in ret:
411 for each in ret:
409 k = each.ui_key
412 k = each.ui_key
410 v = each.ui_value
413 v = each.ui_value
411 if k == '/':
414 if k == '/':
412 k = 'root_path'
415 k = 'root_path'
413
416
414 if k.find('.') != -1:
417 if k.find('.') != -1:
415 k = k.replace('.', '_')
418 k = k.replace('.', '_')
416
419
417 if each.ui_section == 'hooks':
420 if each.ui_section == 'hooks':
418 v = each.ui_active
421 v = each.ui_active
419
422
420 settings[each.ui_section + '_' + k] = v
423 settings[each.ui_section + '_' + k] = v
421
424
422 return settings
425 return settings
@@ -1,1400 +1,1447 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.db
3 rhodecode.model.db
4 ~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~
5
5
6 Database Models for RhodeCode
6 Database Models for RhodeCode
7
7
8 :created_on: Apr 08, 2010
8 :created_on: Apr 08, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import os
26 import os
27 import logging
27 import logging
28 import datetime
28 import datetime
29 import traceback
29 import traceback
30 import hashlib
30 from collections import defaultdict
31 from collections import defaultdict
31
32
32 from sqlalchemy import *
33 from sqlalchemy import *
33 from sqlalchemy.ext.hybrid import hybrid_property
34 from sqlalchemy.ext.hybrid import hybrid_property
34 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
35 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
36 from sqlalchemy.exc import DatabaseError
35 from beaker.cache import cache_region, region_invalidate
37 from beaker.cache import cache_region, region_invalidate
36
38
37 from pylons.i18n.translation import lazy_ugettext as _
39 from pylons.i18n.translation import lazy_ugettext as _
38
40
39 from rhodecode.lib.vcs import get_backend
41 from rhodecode.lib.vcs import get_backend
40 from rhodecode.lib.vcs.utils.helpers import get_scm
42 from rhodecode.lib.vcs.utils.helpers import get_scm
41 from rhodecode.lib.vcs.exceptions import VCSError
43 from rhodecode.lib.vcs.exceptions import VCSError
42 from rhodecode.lib.vcs.utils.lazy import LazyProperty
44 from rhodecode.lib.vcs.utils.lazy import LazyProperty
43
45
44 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
46 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
45 safe_unicode
47 safe_unicode
46 from rhodecode.lib.compat import json
48 from rhodecode.lib.compat import json
47 from rhodecode.lib.caching_query import FromCache
49 from rhodecode.lib.caching_query import FromCache
48
50
49 from rhodecode.model.meta import Base, Session
51 from rhodecode.model.meta import Base, Session
50 import hashlib
52
51 from sqlalchemy.exc import DatabaseError
52
53
53 URL_SEP = '/'
54 URL_SEP = '/'
54 log = logging.getLogger(__name__)
55 log = logging.getLogger(__name__)
55
56
56 #==============================================================================
57 #==============================================================================
57 # BASE CLASSES
58 # BASE CLASSES
58 #==============================================================================
59 #==============================================================================
59
60
60 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
61 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
61
62
62
63
63 class ModelSerializer(json.JSONEncoder):
64 class ModelSerializer(json.JSONEncoder):
64 """
65 """
65 Simple Serializer for JSON,
66 Simple Serializer for JSON,
66
67
67 usage::
68 usage::
68
69
69 to make object customized for serialization implement a __json__
70 to make object customized for serialization implement a __json__
70 method that will return a dict for serialization into json
71 method that will return a dict for serialization into json
71
72
72 example::
73 example::
73
74
74 class Task(object):
75 class Task(object):
75
76
76 def __init__(self, name, value):
77 def __init__(self, name, value):
77 self.name = name
78 self.name = name
78 self.value = value
79 self.value = value
79
80
80 def __json__(self):
81 def __json__(self):
81 return dict(name=self.name,
82 return dict(name=self.name,
82 value=self.value)
83 value=self.value)
83
84
84 """
85 """
85
86
86 def default(self, obj):
87 def default(self, obj):
87
88
88 if hasattr(obj, '__json__'):
89 if hasattr(obj, '__json__'):
89 return obj.__json__()
90 return obj.__json__()
90 else:
91 else:
91 return json.JSONEncoder.default(self, obj)
92 return json.JSONEncoder.default(self, obj)
92
93
93
94
94 class BaseModel(object):
95 class BaseModel(object):
95 """
96 """
96 Base Model for all classess
97 Base Model for all classess
97 """
98 """
98
99
99 @classmethod
100 @classmethod
100 def _get_keys(cls):
101 def _get_keys(cls):
101 """return column names for this model """
102 """return column names for this model """
102 return class_mapper(cls).c.keys()
103 return class_mapper(cls).c.keys()
103
104
104 def get_dict(self):
105 def get_dict(self):
105 """
106 """
106 return dict with keys and values corresponding
107 return dict with keys and values corresponding
107 to this model data """
108 to this model data """
108
109
109 d = {}
110 d = {}
110 for k in self._get_keys():
111 for k in self._get_keys():
111 d[k] = getattr(self, k)
112 d[k] = getattr(self, k)
112
113
113 # also use __json__() if present to get additional fields
114 # also use __json__() if present to get additional fields
114 for k, val in getattr(self, '__json__', lambda: {})().iteritems():
115 for k, val in getattr(self, '__json__', lambda: {})().iteritems():
115 d[k] = val
116 d[k] = val
116 return d
117 return d
117
118
118 def get_appstruct(self):
119 def get_appstruct(self):
119 """return list with keys and values tupples corresponding
120 """return list with keys and values tupples corresponding
120 to this model data """
121 to this model data """
121
122
122 l = []
123 l = []
123 for k in self._get_keys():
124 for k in self._get_keys():
124 l.append((k, getattr(self, k),))
125 l.append((k, getattr(self, k),))
125 return l
126 return l
126
127
127 def populate_obj(self, populate_dict):
128 def populate_obj(self, populate_dict):
128 """populate model with data from given populate_dict"""
129 """populate model with data from given populate_dict"""
129
130
130 for k in self._get_keys():
131 for k in self._get_keys():
131 if k in populate_dict:
132 if k in populate_dict:
132 setattr(self, k, populate_dict[k])
133 setattr(self, k, populate_dict[k])
133
134
134 @classmethod
135 @classmethod
135 def query(cls):
136 def query(cls):
136 return Session.query(cls)
137 return Session.query(cls)
137
138
138 @classmethod
139 @classmethod
139 def get(cls, id_):
140 def get(cls, id_):
140 if id_:
141 if id_:
141 return cls.query().get(id_)
142 return cls.query().get(id_)
142
143
143 @classmethod
144 @classmethod
144 def getAll(cls):
145 def getAll(cls):
145 return cls.query().all()
146 return cls.query().all()
146
147
147 @classmethod
148 @classmethod
148 def delete(cls, id_):
149 def delete(cls, id_):
149 obj = cls.query().get(id_)
150 obj = cls.query().get(id_)
150 Session.delete(obj)
151 Session.delete(obj)
151
152
152 def __repr__(self):
153 def __repr__(self):
153 if hasattr(self, '__unicode__'):
154 if hasattr(self, '__unicode__'):
154 # python repr needs to return str
155 # python repr needs to return str
155 return safe_str(self.__unicode__())
156 return safe_str(self.__unicode__())
156 return '<DB:%s>' % (self.__class__.__name__)
157 return '<DB:%s>' % (self.__class__.__name__)
157
158
159
158 class RhodeCodeSetting(Base, BaseModel):
160 class RhodeCodeSetting(Base, BaseModel):
159 __tablename__ = 'rhodecode_settings'
161 __tablename__ = 'rhodecode_settings'
160 __table_args__ = (
162 __table_args__ = (
161 UniqueConstraint('app_settings_name'),
163 UniqueConstraint('app_settings_name'),
162 {'extend_existing': True, 'mysql_engine': 'InnoDB',
164 {'extend_existing': True, 'mysql_engine': 'InnoDB',
163 'mysql_charset': 'utf8'}
165 'mysql_charset': 'utf8'}
164 )
166 )
165 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
167 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
166 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
168 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
167 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
169 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
168
170
169 def __init__(self, k='', v=''):
171 def __init__(self, k='', v=''):
170 self.app_settings_name = k
172 self.app_settings_name = k
171 self.app_settings_value = v
173 self.app_settings_value = v
172
174
173 @validates('_app_settings_value')
175 @validates('_app_settings_value')
174 def validate_settings_value(self, key, val):
176 def validate_settings_value(self, key, val):
175 assert type(val) == unicode
177 assert type(val) == unicode
176 return val
178 return val
177
179
178 @hybrid_property
180 @hybrid_property
179 def app_settings_value(self):
181 def app_settings_value(self):
180 v = self._app_settings_value
182 v = self._app_settings_value
181 if self.app_settings_name == 'ldap_active':
183 if self.app_settings_name == 'ldap_active':
182 v = str2bool(v)
184 v = str2bool(v)
183 return v
185 return v
184
186
185 @app_settings_value.setter
187 @app_settings_value.setter
186 def app_settings_value(self, val):
188 def app_settings_value(self, val):
187 """
189 """
188 Setter that will always make sure we use unicode in app_settings_value
190 Setter that will always make sure we use unicode in app_settings_value
189
191
190 :param val:
192 :param val:
191 """
193 """
192 self._app_settings_value = safe_unicode(val)
194 self._app_settings_value = safe_unicode(val)
193
195
194 def __unicode__(self):
196 def __unicode__(self):
195 return u"<%s('%s:%s')>" % (
197 return u"<%s('%s:%s')>" % (
196 self.__class__.__name__,
198 self.__class__.__name__,
197 self.app_settings_name, self.app_settings_value
199 self.app_settings_name, self.app_settings_value
198 )
200 )
199
201
200 @classmethod
202 @classmethod
201 def get_by_name(cls, ldap_key):
203 def get_by_name(cls, ldap_key):
202 return cls.query()\
204 return cls.query()\
203 .filter(cls.app_settings_name == ldap_key).scalar()
205 .filter(cls.app_settings_name == ldap_key).scalar()
204
206
205 @classmethod
207 @classmethod
206 def get_app_settings(cls, cache=False):
208 def get_app_settings(cls, cache=False):
207
209
208 ret = cls.query()
210 ret = cls.query()
209
211
210 if cache:
212 if cache:
211 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
213 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
212
214
213 if not ret:
215 if not ret:
214 raise Exception('Could not get application settings !')
216 raise Exception('Could not get application settings !')
215 settings = {}
217 settings = {}
216 for each in ret:
218 for each in ret:
217 settings['rhodecode_' + each.app_settings_name] = \
219 settings['rhodecode_' + each.app_settings_name] = \
218 each.app_settings_value
220 each.app_settings_value
219
221
220 return settings
222 return settings
221
223
222 @classmethod
224 @classmethod
223 def get_ldap_settings(cls, cache=False):
225 def get_ldap_settings(cls, cache=False):
224 ret = cls.query()\
226 ret = cls.query()\
225 .filter(cls.app_settings_name.startswith('ldap_')).all()
227 .filter(cls.app_settings_name.startswith('ldap_')).all()
226 fd = {}
228 fd = {}
227 for row in ret:
229 for row in ret:
228 fd.update({row.app_settings_name:row.app_settings_value})
230 fd.update({row.app_settings_name: row.app_settings_value})
229
231
230 return fd
232 return fd
231
233
232
234
233 class RhodeCodeUi(Base, BaseModel):
235 class RhodeCodeUi(Base, BaseModel):
234 __tablename__ = 'rhodecode_ui'
236 __tablename__ = 'rhodecode_ui'
235 __table_args__ = (
237 __table_args__ = (
236 UniqueConstraint('ui_key'),
238 UniqueConstraint('ui_key'),
237 {'extend_existing': True, 'mysql_engine': 'InnoDB',
239 {'extend_existing': True, 'mysql_engine': 'InnoDB',
238 'mysql_charset': 'utf8'}
240 'mysql_charset': 'utf8'}
239 )
241 )
240
242
241 HOOK_UPDATE = 'changegroup.update'
243 HOOK_UPDATE = 'changegroup.update'
242 HOOK_REPO_SIZE = 'changegroup.repo_size'
244 HOOK_REPO_SIZE = 'changegroup.repo_size'
243 HOOK_PUSH = 'pretxnchangegroup.push_logger'
245 HOOK_PUSH = 'pretxnchangegroup.push_logger'
244 HOOK_PULL = 'preoutgoing.pull_logger'
246 HOOK_PULL = 'preoutgoing.pull_logger'
245
247
246 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
248 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
247 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
249 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
248 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
250 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
249 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
251 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
250 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
252 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
251
253
252 @classmethod
254 @classmethod
253 def get_by_key(cls, key):
255 def get_by_key(cls, key):
254 return cls.query().filter(cls.ui_key == key)
256 return cls.query().filter(cls.ui_key == key)
255
257
256 @classmethod
258 @classmethod
257 def get_builtin_hooks(cls):
259 def get_builtin_hooks(cls):
258 q = cls.query()
260 q = cls.query()
259 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
261 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
260 cls.HOOK_REPO_SIZE,
262 cls.HOOK_REPO_SIZE,
261 cls.HOOK_PUSH, cls.HOOK_PULL]))
263 cls.HOOK_PUSH, cls.HOOK_PULL]))
262 return q.all()
264 return q.all()
263
265
264 @classmethod
266 @classmethod
265 def get_custom_hooks(cls):
267 def get_custom_hooks(cls):
266 q = cls.query()
268 q = cls.query()
267 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
269 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
268 cls.HOOK_REPO_SIZE,
270 cls.HOOK_REPO_SIZE,
269 cls.HOOK_PUSH, cls.HOOK_PULL]))
271 cls.HOOK_PUSH, cls.HOOK_PULL]))
270 q = q.filter(cls.ui_section == 'hooks')
272 q = q.filter(cls.ui_section == 'hooks')
271 return q.all()
273 return q.all()
272
274
273 @classmethod
275 @classmethod
274 def create_or_update_hook(cls, key, val):
276 def create_or_update_hook(cls, key, val):
275 new_ui = cls.get_by_key(key).scalar() or cls()
277 new_ui = cls.get_by_key(key).scalar() or cls()
276 new_ui.ui_section = 'hooks'
278 new_ui.ui_section = 'hooks'
277 new_ui.ui_active = True
279 new_ui.ui_active = True
278 new_ui.ui_key = key
280 new_ui.ui_key = key
279 new_ui.ui_value = val
281 new_ui.ui_value = val
280
282
281 Session.add(new_ui)
283 Session.add(new_ui)
282
284
283
285
284 class User(Base, BaseModel):
286 class User(Base, BaseModel):
285 __tablename__ = 'users'
287 __tablename__ = 'users'
286 __table_args__ = (
288 __table_args__ = (
287 UniqueConstraint('username'), UniqueConstraint('email'),
289 UniqueConstraint('username'), UniqueConstraint('email'),
288 {'extend_existing': True, 'mysql_engine': 'InnoDB',
290 {'extend_existing': True, 'mysql_engine': 'InnoDB',
289 'mysql_charset': 'utf8'}
291 'mysql_charset': 'utf8'}
290 )
292 )
291 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
293 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
292 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
294 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
293 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
295 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
294 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
296 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
295 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
297 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
296 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
298 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
297 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
299 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
298 _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
300 _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
299 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
301 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
300 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
302 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
301 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
303 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
302
304
303 user_log = relationship('UserLog', cascade='all')
305 user_log = relationship('UserLog', cascade='all')
304 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
306 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
305
307
306 repositories = relationship('Repository')
308 repositories = relationship('Repository')
307 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
309 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
308 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
310 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
309 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
311 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
310
312
311 group_member = relationship('UsersGroupMember', cascade='all')
313 group_member = relationship('UsersGroupMember', cascade='all')
312
314
313 notifications = relationship('UserNotification', cascade='all')
315 notifications = relationship('UserNotification', cascade='all')
314 # notifications assigned to this user
316 # notifications assigned to this user
315 user_created_notifications = relationship('Notification', cascade='all')
317 user_created_notifications = relationship('Notification', cascade='all')
316 # comments created by this user
318 # comments created by this user
317 user_comments = relationship('ChangesetComment', cascade='all')
319 user_comments = relationship('ChangesetComment', cascade='all')
318
320
319 @hybrid_property
321 @hybrid_property
320 def email(self):
322 def email(self):
321 return self._email
323 return self._email
322
324
323 @email.setter
325 @email.setter
324 def email(self, val):
326 def email(self, val):
325 self._email = val.lower() if val else None
327 self._email = val.lower() if val else None
326
328
327 @property
329 @property
328 def full_name(self):
330 def full_name(self):
329 return '%s %s' % (self.name, self.lastname)
331 return '%s %s' % (self.name, self.lastname)
330
332
331 @property
333 @property
332 def full_name_or_username(self):
334 def full_name_or_username(self):
333 return ('%s %s' % (self.name, self.lastname)
335 return ('%s %s' % (self.name, self.lastname)
334 if (self.name and self.lastname) else self.username)
336 if (self.name and self.lastname) else self.username)
335
337
336 @property
338 @property
337 def full_contact(self):
339 def full_contact(self):
338 return '%s %s <%s>' % (self.name, self.lastname, self.email)
340 return '%s %s <%s>' % (self.name, self.lastname, self.email)
339
341
340 @property
342 @property
341 def short_contact(self):
343 def short_contact(self):
342 return '%s %s' % (self.name, self.lastname)
344 return '%s %s' % (self.name, self.lastname)
343
345
344 @property
346 @property
345 def is_admin(self):
347 def is_admin(self):
346 return self.admin
348 return self.admin
347
349
348 def __unicode__(self):
350 def __unicode__(self):
349 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
351 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
350 self.user_id, self.username)
352 self.user_id, self.username)
351
353
352 @classmethod
354 @classmethod
353 def get_by_username(cls, username, case_insensitive=False, cache=False):
355 def get_by_username(cls, username, case_insensitive=False, cache=False):
354 if case_insensitive:
356 if case_insensitive:
355 q = cls.query().filter(cls.username.ilike(username))
357 q = cls.query().filter(cls.username.ilike(username))
356 else:
358 else:
357 q = cls.query().filter(cls.username == username)
359 q = cls.query().filter(cls.username == username)
358
360
359 if cache:
361 if cache:
360 q = q.options(FromCache(
362 q = q.options(FromCache(
361 "sql_cache_short",
363 "sql_cache_short",
362 "get_user_%s" % _hash_key(username)
364 "get_user_%s" % _hash_key(username)
363 )
365 )
364 )
366 )
365 return q.scalar()
367 return q.scalar()
366
368
367 @classmethod
369 @classmethod
368 def get_by_api_key(cls, api_key, cache=False):
370 def get_by_api_key(cls, api_key, cache=False):
369 q = cls.query().filter(cls.api_key == api_key)
371 q = cls.query().filter(cls.api_key == api_key)
370
372
371 if cache:
373 if cache:
372 q = q.options(FromCache("sql_cache_short",
374 q = q.options(FromCache("sql_cache_short",
373 "get_api_key_%s" % api_key))
375 "get_api_key_%s" % api_key))
374 return q.scalar()
376 return q.scalar()
375
377
376 @classmethod
378 @classmethod
377 def get_by_email(cls, email, case_insensitive=False, cache=False):
379 def get_by_email(cls, email, case_insensitive=False, cache=False):
378 if case_insensitive:
380 if case_insensitive:
379 q = cls.query().filter(cls.email.ilike(email))
381 q = cls.query().filter(cls.email.ilike(email))
380 else:
382 else:
381 q = cls.query().filter(cls.email == email)
383 q = cls.query().filter(cls.email == email)
382
384
383 if cache:
385 if cache:
384 q = q.options(FromCache("sql_cache_short",
386 q = q.options(FromCache("sql_cache_short",
385 "get_email_key_%s" % email))
387 "get_email_key_%s" % email))
386
388
387 ret = q.scalar()
389 ret = q.scalar()
388 if ret is None:
390 if ret is None:
389 q = UserEmailMap.query()
391 q = UserEmailMap.query()
390 # try fetching in alternate email map
392 # try fetching in alternate email map
391 if case_insensitive:
393 if case_insensitive:
392 q = q.filter(UserEmailMap.email.ilike(email))
394 q = q.filter(UserEmailMap.email.ilike(email))
393 else:
395 else:
394 q = q.filter(UserEmailMap.email == email)
396 q = q.filter(UserEmailMap.email == email)
395 q = q.options(joinedload(UserEmailMap.user))
397 q = q.options(joinedload(UserEmailMap.user))
396 if cache:
398 if cache:
397 q = q.options(FromCache("sql_cache_short",
399 q = q.options(FromCache("sql_cache_short",
398 "get_email_map_key_%s" % email))
400 "get_email_map_key_%s" % email))
399 ret = getattr(q.scalar(), 'user', None)
401 ret = getattr(q.scalar(), 'user', None)
400
402
401 return ret
403 return ret
402
404
403 def update_lastlogin(self):
405 def update_lastlogin(self):
404 """Update user lastlogin"""
406 """Update user lastlogin"""
405 self.last_login = datetime.datetime.now()
407 self.last_login = datetime.datetime.now()
406 Session.add(self)
408 Session.add(self)
407 log.debug('updated user %s lastlogin' % self.username)
409 log.debug('updated user %s lastlogin' % self.username)
408
410
409 def __json__(self):
411 def __json__(self):
410 return dict(
412 return dict(
411 user_id=self.user_id,
413 user_id=self.user_id,
412 first_name=self.name,
414 first_name=self.name,
413 last_name=self.lastname,
415 last_name=self.lastname,
414 email=self.email,
416 email=self.email,
415 full_name=self.full_name,
417 full_name=self.full_name,
416 full_name_or_username=self.full_name_or_username,
418 full_name_or_username=self.full_name_or_username,
417 short_contact=self.short_contact,
419 short_contact=self.short_contact,
418 full_contact=self.full_contact
420 full_contact=self.full_contact
419 )
421 )
420
422
421
423
422 class UserEmailMap(Base, BaseModel):
424 class UserEmailMap(Base, BaseModel):
423 __tablename__ = 'user_email_map'
425 __tablename__ = 'user_email_map'
424 __table_args__ = (
426 __table_args__ = (
425 UniqueConstraint('email'),
427 UniqueConstraint('email'),
426 {'extend_existing': True, 'mysql_engine':'InnoDB',
428 {'extend_existing': True, 'mysql_engine':'InnoDB',
427 'mysql_charset': 'utf8'}
429 'mysql_charset': 'utf8'}
428 )
430 )
429 __mapper_args__ = {}
431 __mapper_args__ = {}
430
432
431 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
433 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
432 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
434 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
433 _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
435 _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
434
436
435 user = relationship('User')
437 user = relationship('User')
436
438
437 @validates('_email')
439 @validates('_email')
438 def validate_email(self, key, email):
440 def validate_email(self, key, email):
439 # check if this email is not main one
441 # check if this email is not main one
440 main_email = Session.query(User).filter(User.email == email).scalar()
442 main_email = Session.query(User).filter(User.email == email).scalar()
441 if main_email is not None:
443 if main_email is not None:
442 raise AttributeError('email %s is present is user table' % email)
444 raise AttributeError('email %s is present is user table' % email)
443 return email
445 return email
444
446
445 @hybrid_property
447 @hybrid_property
446 def email(self):
448 def email(self):
447 return self._email
449 return self._email
448
450
449 @email.setter
451 @email.setter
450 def email(self, val):
452 def email(self, val):
451 self._email = val.lower() if val else None
453 self._email = val.lower() if val else None
452
454
453
455
454 class UserLog(Base, BaseModel):
456 class UserLog(Base, BaseModel):
455 __tablename__ = 'user_logs'
457 __tablename__ = 'user_logs'
456 __table_args__ = (
458 __table_args__ = (
457 {'extend_existing': True, 'mysql_engine': 'InnoDB',
459 {'extend_existing': True, 'mysql_engine': 'InnoDB',
458 'mysql_charset': 'utf8'},
460 'mysql_charset': 'utf8'},
459 )
461 )
460 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
462 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
461 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
463 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
462 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
464 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
463 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
465 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
464 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
466 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
465 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
467 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
466 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
468 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
467
469
468 @property
470 @property
469 def action_as_day(self):
471 def action_as_day(self):
470 return datetime.date(*self.action_date.timetuple()[:3])
472 return datetime.date(*self.action_date.timetuple()[:3])
471
473
472 user = relationship('User')
474 user = relationship('User')
473 repository = relationship('Repository', cascade='')
475 repository = relationship('Repository', cascade='')
474
476
475
477
476 class UsersGroup(Base, BaseModel):
478 class UsersGroup(Base, BaseModel):
477 __tablename__ = 'users_groups'
479 __tablename__ = 'users_groups'
478 __table_args__ = (
480 __table_args__ = (
479 {'extend_existing': True, 'mysql_engine': 'InnoDB',
481 {'extend_existing': True, 'mysql_engine': 'InnoDB',
480 'mysql_charset': 'utf8'},
482 'mysql_charset': 'utf8'},
481 )
483 )
482
484
483 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
485 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
484 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
486 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
485 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
487 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
486
488
487 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
489 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
488 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
490 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
489 users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
491 users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
490
492
491 def __unicode__(self):
493 def __unicode__(self):
492 return u'<userGroup(%s)>' % (self.users_group_name)
494 return u'<userGroup(%s)>' % (self.users_group_name)
493
495
494 @classmethod
496 @classmethod
495 def get_by_group_name(cls, group_name, cache=False,
497 def get_by_group_name(cls, group_name, cache=False,
496 case_insensitive=False):
498 case_insensitive=False):
497 if case_insensitive:
499 if case_insensitive:
498 q = cls.query().filter(cls.users_group_name.ilike(group_name))
500 q = cls.query().filter(cls.users_group_name.ilike(group_name))
499 else:
501 else:
500 q = cls.query().filter(cls.users_group_name == group_name)
502 q = cls.query().filter(cls.users_group_name == group_name)
501 if cache:
503 if cache:
502 q = q.options(FromCache(
504 q = q.options(FromCache(
503 "sql_cache_short",
505 "sql_cache_short",
504 "get_user_%s" % _hash_key(group_name)
506 "get_user_%s" % _hash_key(group_name)
505 )
507 )
506 )
508 )
507 return q.scalar()
509 return q.scalar()
508
510
509 @classmethod
511 @classmethod
510 def get(cls, users_group_id, cache=False):
512 def get(cls, users_group_id, cache=False):
511 users_group = cls.query()
513 users_group = cls.query()
512 if cache:
514 if cache:
513 users_group = users_group.options(FromCache("sql_cache_short",
515 users_group = users_group.options(FromCache("sql_cache_short",
514 "get_users_group_%s" % users_group_id))
516 "get_users_group_%s" % users_group_id))
515 return users_group.get(users_group_id)
517 return users_group.get(users_group_id)
516
518
517
519
518 class UsersGroupMember(Base, BaseModel):
520 class UsersGroupMember(Base, BaseModel):
519 __tablename__ = 'users_groups_members'
521 __tablename__ = 'users_groups_members'
520 __table_args__ = (
522 __table_args__ = (
521 {'extend_existing': True, 'mysql_engine': 'InnoDB',
523 {'extend_existing': True, 'mysql_engine': 'InnoDB',
522 'mysql_charset': 'utf8'},
524 'mysql_charset': 'utf8'},
523 )
525 )
524
526
525 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
527 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
526 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
528 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
527 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
529 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
528
530
529 user = relationship('User', lazy='joined')
531 user = relationship('User', lazy='joined')
530 users_group = relationship('UsersGroup')
532 users_group = relationship('UsersGroup')
531
533
532 def __init__(self, gr_id='', u_id=''):
534 def __init__(self, gr_id='', u_id=''):
533 self.users_group_id = gr_id
535 self.users_group_id = gr_id
534 self.user_id = u_id
536 self.user_id = u_id
535
537
536
538
537 class Repository(Base, BaseModel):
539 class Repository(Base, BaseModel):
538 __tablename__ = 'repositories'
540 __tablename__ = 'repositories'
539 __table_args__ = (
541 __table_args__ = (
540 UniqueConstraint('repo_name'),
542 UniqueConstraint('repo_name'),
541 {'extend_existing': True, 'mysql_engine': 'InnoDB',
543 {'extend_existing': True, 'mysql_engine': 'InnoDB',
542 'mysql_charset': 'utf8'},
544 'mysql_charset': 'utf8'},
543 )
545 )
544
546
545 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
547 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
546 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
548 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
547 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
549 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
548 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
550 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
549 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
551 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
550 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
552 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
551 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
553 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
552 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
554 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
553 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
555 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
554 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
556 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
555
557
556 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
558 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
557 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
559 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
558
560
559 user = relationship('User')
561 user = relationship('User')
560 fork = relationship('Repository', remote_side=repo_id)
562 fork = relationship('Repository', remote_side=repo_id)
561 group = relationship('RepoGroup')
563 group = relationship('RepoGroup')
562 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
564 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
563 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
565 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
564 stats = relationship('Statistics', cascade='all', uselist=False)
566 stats = relationship('Statistics', cascade='all', uselist=False)
565
567
566 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
568 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
567
569
568 logs = relationship('UserLog')
570 logs = relationship('UserLog')
569
571
570 def __unicode__(self):
572 def __unicode__(self):
571 return u"<%s('%s:%s')>" % (self.__class__.__name__,self.repo_id,
573 return u"<%s('%s:%s')>" % (self.__class__.__name__,self.repo_id,
572 self.repo_name)
574 self.repo_name)
573
575
574 @classmethod
576 @classmethod
575 def url_sep(cls):
577 def url_sep(cls):
576 return URL_SEP
578 return URL_SEP
577
579
578 @classmethod
580 @classmethod
579 def get_by_repo_name(cls, repo_name):
581 def get_by_repo_name(cls, repo_name):
580 q = Session.query(cls).filter(cls.repo_name == repo_name)
582 q = Session.query(cls).filter(cls.repo_name == repo_name)
581 q = q.options(joinedload(Repository.fork))\
583 q = q.options(joinedload(Repository.fork))\
582 .options(joinedload(Repository.user))\
584 .options(joinedload(Repository.user))\
583 .options(joinedload(Repository.group))
585 .options(joinedload(Repository.group))
584 return q.scalar()
586 return q.scalar()
585
587
586 @classmethod
588 @classmethod
587 def get_repo_forks(cls, repo_id):
589 def get_repo_forks(cls, repo_id):
588 return cls.query().filter(Repository.fork_id == repo_id)
590 return cls.query().filter(Repository.fork_id == repo_id)
589
591
590 @classmethod
592 @classmethod
591 def base_path(cls):
593 def base_path(cls):
592 """
594 """
593 Returns base path when all repos are stored
595 Returns base path when all repos are stored
594
596
595 :param cls:
597 :param cls:
596 """
598 """
597 q = Session.query(RhodeCodeUi)\
599 q = Session.query(RhodeCodeUi)\
598 .filter(RhodeCodeUi.ui_key == cls.url_sep())
600 .filter(RhodeCodeUi.ui_key == cls.url_sep())
599 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
601 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
600 return q.one().ui_value
602 return q.one().ui_value
601
603
602 @property
604 @property
603 def just_name(self):
605 def just_name(self):
604 return self.repo_name.split(Repository.url_sep())[-1]
606 return self.repo_name.split(Repository.url_sep())[-1]
605
607
606 @property
608 @property
607 def groups_with_parents(self):
609 def groups_with_parents(self):
608 groups = []
610 groups = []
609 if self.group is None:
611 if self.group is None:
610 return groups
612 return groups
611
613
612 cur_gr = self.group
614 cur_gr = self.group
613 groups.insert(0, cur_gr)
615 groups.insert(0, cur_gr)
614 while 1:
616 while 1:
615 gr = getattr(cur_gr, 'parent_group', None)
617 gr = getattr(cur_gr, 'parent_group', None)
616 cur_gr = cur_gr.parent_group
618 cur_gr = cur_gr.parent_group
617 if gr is None:
619 if gr is None:
618 break
620 break
619 groups.insert(0, gr)
621 groups.insert(0, gr)
620
622
621 return groups
623 return groups
622
624
623 @property
625 @property
624 def groups_and_repo(self):
626 def groups_and_repo(self):
625 return self.groups_with_parents, self.just_name
627 return self.groups_with_parents, self.just_name
626
628
627 @LazyProperty
629 @LazyProperty
628 def repo_path(self):
630 def repo_path(self):
629 """
631 """
630 Returns base full path for that repository means where it actually
632 Returns base full path for that repository means where it actually
631 exists on a filesystem
633 exists on a filesystem
632 """
634 """
633 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
635 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
634 Repository.url_sep())
636 Repository.url_sep())
635 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
637 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
636 return q.one().ui_value
638 return q.one().ui_value
637
639
638 @property
640 @property
639 def repo_full_path(self):
641 def repo_full_path(self):
640 p = [self.repo_path]
642 p = [self.repo_path]
641 # we need to split the name by / since this is how we store the
643 # we need to split the name by / since this is how we store the
642 # names in the database, but that eventually needs to be converted
644 # names in the database, but that eventually needs to be converted
643 # into a valid system path
645 # into a valid system path
644 p += self.repo_name.split(Repository.url_sep())
646 p += self.repo_name.split(Repository.url_sep())
645 return os.path.join(*p)
647 return os.path.join(*p)
646
648
647 def get_new_name(self, repo_name):
649 def get_new_name(self, repo_name):
648 """
650 """
649 returns new full repository name based on assigned group and new new
651 returns new full repository name based on assigned group and new new
650
652
651 :param group_name:
653 :param group_name:
652 """
654 """
653 path_prefix = self.group.full_path_splitted if self.group else []
655 path_prefix = self.group.full_path_splitted if self.group else []
654 return Repository.url_sep().join(path_prefix + [repo_name])
656 return Repository.url_sep().join(path_prefix + [repo_name])
655
657
656 @property
658 @property
657 def _ui(self):
659 def _ui(self):
658 """
660 """
659 Creates an db based ui object for this repository
661 Creates an db based ui object for this repository
660 """
662 """
661 from mercurial import ui
663 from mercurial import ui
662 from mercurial import config
664 from mercurial import config
663 baseui = ui.ui()
665 baseui = ui.ui()
664
666
665 #clean the baseui object
667 #clean the baseui object
666 baseui._ocfg = config.config()
668 baseui._ocfg = config.config()
667 baseui._ucfg = config.config()
669 baseui._ucfg = config.config()
668 baseui._tcfg = config.config()
670 baseui._tcfg = config.config()
669
671
670 ret = RhodeCodeUi.query()\
672 ret = RhodeCodeUi.query()\
671 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
673 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
672
674
673 hg_ui = ret
675 hg_ui = ret
674 for ui_ in hg_ui:
676 for ui_ in hg_ui:
675 if ui_.ui_active:
677 if ui_.ui_active:
676 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
678 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
677 ui_.ui_key, ui_.ui_value)
679 ui_.ui_key, ui_.ui_value)
678 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
680 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
679
681
680 return baseui
682 return baseui
681
683
682 @classmethod
684 @classmethod
683 def is_valid(cls, repo_name):
685 def is_valid(cls, repo_name):
684 """
686 """
685 returns True if given repo name is a valid filesystem repository
687 returns True if given repo name is a valid filesystem repository
686
688
687 :param cls:
689 :param cls:
688 :param repo_name:
690 :param repo_name:
689 """
691 """
690 from rhodecode.lib.utils import is_valid_repo
692 from rhodecode.lib.utils import is_valid_repo
691
693
692 return is_valid_repo(repo_name, cls.base_path())
694 return is_valid_repo(repo_name, cls.base_path())
693
695
694 #==========================================================================
696 #==========================================================================
695 # SCM PROPERTIES
697 # SCM PROPERTIES
696 #==========================================================================
698 #==========================================================================
697
699
698 def get_changeset(self, rev=None):
700 def get_changeset(self, rev=None):
699 return get_changeset_safe(self.scm_instance, rev)
701 return get_changeset_safe(self.scm_instance, rev)
700
702
701 @property
703 @property
702 def tip(self):
704 def tip(self):
703 return self.get_changeset('tip')
705 return self.get_changeset('tip')
704
706
705 @property
707 @property
706 def author(self):
708 def author(self):
707 return self.tip.author
709 return self.tip.author
708
710
709 @property
711 @property
710 def last_change(self):
712 def last_change(self):
711 return self.scm_instance.last_change
713 return self.scm_instance.last_change
712
714
713 def comments(self, revisions=None):
715 def comments(self, revisions=None):
714 """
716 """
715 Returns comments for this repository grouped by revisions
717 Returns comments for this repository grouped by revisions
716
718
717 :param revisions: filter query by revisions only
719 :param revisions: filter query by revisions only
718 """
720 """
719 cmts = ChangesetComment.query()\
721 cmts = ChangesetComment.query()\
720 .filter(ChangesetComment.repo == self)
722 .filter(ChangesetComment.repo == self)
721 if revisions:
723 if revisions:
722 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
724 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
723 grouped = defaultdict(list)
725 grouped = defaultdict(list)
724 for cmt in cmts.all():
726 for cmt in cmts.all():
725 grouped[cmt.revision].append(cmt)
727 grouped[cmt.revision].append(cmt)
726 return grouped
728 return grouped
727
729
728 def statuses(self, revisions=None):
730 def statuses(self, revisions=None):
729 """
731 """
730 Returns statuses for this repository
732 Returns statuses for this repository
731
733
732 :param revisions: list of revisions to get statuses for
734 :param revisions: list of revisions to get statuses for
733 :type revisions: list
735 :type revisions: list
734 """
736 """
735
737
736 statuses = ChangesetStatus.query()\
738 statuses = ChangesetStatus.query()\
737 .filter(ChangesetStatus.repo == self)\
739 .filter(ChangesetStatus.repo == self)\
738 .filter(ChangesetStatus.version == 0)
740 .filter(ChangesetStatus.version == 0)
739 if revisions:
741 if revisions:
740 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
742 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
741 grouped = {}
743 grouped = {}
742 for stat in statuses.all():
744 for stat in statuses.all():
743 grouped[stat.revision] = [str(stat.status), stat.status_lbl]
745 grouped[stat.revision] = [str(stat.status), stat.status_lbl]
744 return grouped
746 return grouped
745
747
746 #==========================================================================
748 #==========================================================================
747 # SCM CACHE INSTANCE
749 # SCM CACHE INSTANCE
748 #==========================================================================
750 #==========================================================================
749
751
750 @property
752 @property
751 def invalidate(self):
753 def invalidate(self):
752 return CacheInvalidation.invalidate(self.repo_name)
754 return CacheInvalidation.invalidate(self.repo_name)
753
755
754 def set_invalidate(self):
756 def set_invalidate(self):
755 """
757 """
756 set a cache for invalidation for this instance
758 set a cache for invalidation for this instance
757 """
759 """
758 CacheInvalidation.set_invalidate(self.repo_name)
760 CacheInvalidation.set_invalidate(self.repo_name)
759
761
760 @LazyProperty
762 @LazyProperty
761 def scm_instance(self):
763 def scm_instance(self):
762 return self.__get_instance()
764 return self.__get_instance()
763
765
764 @property
766 def scm_instance_cached(self, cache_map=None):
765 def scm_instance_cached(self):
766 @cache_region('long_term')
767 @cache_region('long_term')
767 def _c(repo_name):
768 def _c(repo_name):
768 return self.__get_instance()
769 return self.__get_instance()
769 rn = self.repo_name
770 rn = self.repo_name
770 log.debug('Getting cached instance of repo')
771 log.debug('Getting cached instance of repo')
771 inv = self.invalidate
772
772 if inv is not None:
773 if cache_map:
774 # get using prefilled cache_map
775 invalidate_repo = cache_map[self.repo_name]
776 if invalidate_repo:
777 invalidate_repo = (None if invalidate_repo.cache_active
778 else invalidate_repo)
779 else:
780 # get from invalidate
781 invalidate_repo = self.invalidate
782
783 if invalidate_repo is not None:
773 region_invalidate(_c, None, rn)
784 region_invalidate(_c, None, rn)
774 # update our cache
785 # update our cache
775 CacheInvalidation.set_valid(inv.cache_key)
786 CacheInvalidation.set_valid(invalidate_repo.cache_key)
776 return _c(rn)
787 return _c(rn)
777
788
778 def __get_instance(self):
789 def __get_instance(self):
779 repo_full_path = self.repo_full_path
790 repo_full_path = self.repo_full_path
780 try:
791 try:
781 alias = get_scm(repo_full_path)[0]
792 alias = get_scm(repo_full_path)[0]
782 log.debug('Creating instance of %s repository' % alias)
793 log.debug('Creating instance of %s repository' % alias)
783 backend = get_backend(alias)
794 backend = get_backend(alias)
784 except VCSError:
795 except VCSError:
785 log.error(traceback.format_exc())
796 log.error(traceback.format_exc())
786 log.error('Perhaps this repository is in db and not in '
797 log.error('Perhaps this repository is in db and not in '
787 'filesystem run rescan repositories with '
798 'filesystem run rescan repositories with '
788 '"destroy old data " option from admin panel')
799 '"destroy old data " option from admin panel')
789 return
800 return
790
801
791 if alias == 'hg':
802 if alias == 'hg':
792
803
793 repo = backend(safe_str(repo_full_path), create=False,
804 repo = backend(safe_str(repo_full_path), create=False,
794 baseui=self._ui)
805 baseui=self._ui)
795 # skip hidden web repository
806 # skip hidden web repository
796 if repo._get_hidden():
807 if repo._get_hidden():
797 return
808 return
798 else:
809 else:
799 repo = backend(repo_full_path, create=False)
810 repo = backend(repo_full_path, create=False)
800
811
801 return repo
812 return repo
802
813
803
814
804 class RepoGroup(Base, BaseModel):
815 class RepoGroup(Base, BaseModel):
805 __tablename__ = 'groups'
816 __tablename__ = 'groups'
806 __table_args__ = (
817 __table_args__ = (
807 UniqueConstraint('group_name', 'group_parent_id'),
818 UniqueConstraint('group_name', 'group_parent_id'),
808 CheckConstraint('group_id != group_parent_id'),
819 CheckConstraint('group_id != group_parent_id'),
809 {'extend_existing': True, 'mysql_engine': 'InnoDB',
820 {'extend_existing': True, 'mysql_engine': 'InnoDB',
810 'mysql_charset': 'utf8'},
821 'mysql_charset': 'utf8'},
811 )
822 )
812 __mapper_args__ = {'order_by': 'group_name'}
823 __mapper_args__ = {'order_by': 'group_name'}
813
824
814 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
825 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
815 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
826 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
816 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
827 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
817 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
828 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
818
829
819 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
830 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
820 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
831 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
821
832
822 parent_group = relationship('RepoGroup', remote_side=group_id)
833 parent_group = relationship('RepoGroup', remote_side=group_id)
823
834
824 def __init__(self, group_name='', parent_group=None):
835 def __init__(self, group_name='', parent_group=None):
825 self.group_name = group_name
836 self.group_name = group_name
826 self.parent_group = parent_group
837 self.parent_group = parent_group
827
838
828 def __unicode__(self):
839 def __unicode__(self):
829 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
840 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
830 self.group_name)
841 self.group_name)
831
842
832 @classmethod
843 @classmethod
833 def groups_choices(cls):
844 def groups_choices(cls):
834 from webhelpers.html import literal as _literal
845 from webhelpers.html import literal as _literal
835 repo_groups = [('', '')]
846 repo_groups = [('', '')]
836 sep = ' &raquo; '
847 sep = ' &raquo; '
837 _name = lambda k: _literal(sep.join(k))
848 _name = lambda k: _literal(sep.join(k))
838
849
839 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
850 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
840 for x in cls.query().all()])
851 for x in cls.query().all()])
841
852
842 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
853 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
843 return repo_groups
854 return repo_groups
844
855
845 @classmethod
856 @classmethod
846 def url_sep(cls):
857 def url_sep(cls):
847 return URL_SEP
858 return URL_SEP
848
859
849 @classmethod
860 @classmethod
850 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
861 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
851 if case_insensitive:
862 if case_insensitive:
852 gr = cls.query()\
863 gr = cls.query()\
853 .filter(cls.group_name.ilike(group_name))
864 .filter(cls.group_name.ilike(group_name))
854 else:
865 else:
855 gr = cls.query()\
866 gr = cls.query()\
856 .filter(cls.group_name == group_name)
867 .filter(cls.group_name == group_name)
857 if cache:
868 if cache:
858 gr = gr.options(FromCache(
869 gr = gr.options(FromCache(
859 "sql_cache_short",
870 "sql_cache_short",
860 "get_group_%s" % _hash_key(group_name)
871 "get_group_%s" % _hash_key(group_name)
861 )
872 )
862 )
873 )
863 return gr.scalar()
874 return gr.scalar()
864
875
865 @property
876 @property
866 def parents(self):
877 def parents(self):
867 parents_recursion_limit = 5
878 parents_recursion_limit = 5
868 groups = []
879 groups = []
869 if self.parent_group is None:
880 if self.parent_group is None:
870 return groups
881 return groups
871 cur_gr = self.parent_group
882 cur_gr = self.parent_group
872 groups.insert(0, cur_gr)
883 groups.insert(0, cur_gr)
873 cnt = 0
884 cnt = 0
874 while 1:
885 while 1:
875 cnt += 1
886 cnt += 1
876 gr = getattr(cur_gr, 'parent_group', None)
887 gr = getattr(cur_gr, 'parent_group', None)
877 cur_gr = cur_gr.parent_group
888 cur_gr = cur_gr.parent_group
878 if gr is None:
889 if gr is None:
879 break
890 break
880 if cnt == parents_recursion_limit:
891 if cnt == parents_recursion_limit:
881 # this will prevent accidental infinit loops
892 # this will prevent accidental infinit loops
882 log.error('group nested more than %s' %
893 log.error('group nested more than %s' %
883 parents_recursion_limit)
894 parents_recursion_limit)
884 break
895 break
885
896
886 groups.insert(0, gr)
897 groups.insert(0, gr)
887 return groups
898 return groups
888
899
889 @property
900 @property
890 def children(self):
901 def children(self):
891 return RepoGroup.query().filter(RepoGroup.parent_group == self)
902 return RepoGroup.query().filter(RepoGroup.parent_group == self)
892
903
893 @property
904 @property
894 def name(self):
905 def name(self):
895 return self.group_name.split(RepoGroup.url_sep())[-1]
906 return self.group_name.split(RepoGroup.url_sep())[-1]
896
907
897 @property
908 @property
898 def full_path(self):
909 def full_path(self):
899 return self.group_name
910 return self.group_name
900
911
901 @property
912 @property
902 def full_path_splitted(self):
913 def full_path_splitted(self):
903 return self.group_name.split(RepoGroup.url_sep())
914 return self.group_name.split(RepoGroup.url_sep())
904
915
905 @property
916 @property
906 def repositories(self):
917 def repositories(self):
907 return Repository.query()\
918 return Repository.query()\
908 .filter(Repository.group == self)\
919 .filter(Repository.group == self)\
909 .order_by(Repository.repo_name)
920 .order_by(Repository.repo_name)
910
921
911 @property
922 @property
912 def repositories_recursive_count(self):
923 def repositories_recursive_count(self):
913 cnt = self.repositories.count()
924 cnt = self.repositories.count()
914
925
915 def children_count(group):
926 def children_count(group):
916 cnt = 0
927 cnt = 0
917 for child in group.children:
928 for child in group.children:
918 cnt += child.repositories.count()
929 cnt += child.repositories.count()
919 cnt += children_count(child)
930 cnt += children_count(child)
920 return cnt
931 return cnt
921
932
922 return cnt + children_count(self)
933 return cnt + children_count(self)
923
934
924 def get_new_name(self, group_name):
935 def get_new_name(self, group_name):
925 """
936 """
926 returns new full group name based on parent and new name
937 returns new full group name based on parent and new name
927
938
928 :param group_name:
939 :param group_name:
929 """
940 """
930 path_prefix = (self.parent_group.full_path_splitted if
941 path_prefix = (self.parent_group.full_path_splitted if
931 self.parent_group else [])
942 self.parent_group else [])
932 return RepoGroup.url_sep().join(path_prefix + [group_name])
943 return RepoGroup.url_sep().join(path_prefix + [group_name])
933
944
934
945
935 class Permission(Base, BaseModel):
946 class Permission(Base, BaseModel):
936 __tablename__ = 'permissions'
947 __tablename__ = 'permissions'
937 __table_args__ = (
948 __table_args__ = (
938 {'extend_existing': True, 'mysql_engine': 'InnoDB',
949 {'extend_existing': True, 'mysql_engine': 'InnoDB',
939 'mysql_charset': 'utf8'},
950 'mysql_charset': 'utf8'},
940 )
951 )
941 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
952 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
942 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
953 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
943 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
954 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
944
955
945 def __unicode__(self):
956 def __unicode__(self):
946 return u"<%s('%s:%s')>" % (
957 return u"<%s('%s:%s')>" % (
947 self.__class__.__name__, self.permission_id, self.permission_name
958 self.__class__.__name__, self.permission_id, self.permission_name
948 )
959 )
949
960
950 @classmethod
961 @classmethod
951 def get_by_key(cls, key):
962 def get_by_key(cls, key):
952 return cls.query().filter(cls.permission_name == key).scalar()
963 return cls.query().filter(cls.permission_name == key).scalar()
953
964
954 @classmethod
965 @classmethod
955 def get_default_perms(cls, default_user_id):
966 def get_default_perms(cls, default_user_id):
956 q = Session.query(UserRepoToPerm, Repository, cls)\
967 q = Session.query(UserRepoToPerm, Repository, cls)\
957 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
968 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
958 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
969 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
959 .filter(UserRepoToPerm.user_id == default_user_id)
970 .filter(UserRepoToPerm.user_id == default_user_id)
960
971
961 return q.all()
972 return q.all()
962
973
963 @classmethod
974 @classmethod
964 def get_default_group_perms(cls, default_user_id):
975 def get_default_group_perms(cls, default_user_id):
965 q = Session.query(UserRepoGroupToPerm, RepoGroup, cls)\
976 q = Session.query(UserRepoGroupToPerm, RepoGroup, cls)\
966 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
977 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
967 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
978 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
968 .filter(UserRepoGroupToPerm.user_id == default_user_id)
979 .filter(UserRepoGroupToPerm.user_id == default_user_id)
969
980
970 return q.all()
981 return q.all()
971
982
972
983
973 class UserRepoToPerm(Base, BaseModel):
984 class UserRepoToPerm(Base, BaseModel):
974 __tablename__ = 'repo_to_perm'
985 __tablename__ = 'repo_to_perm'
975 __table_args__ = (
986 __table_args__ = (
976 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
987 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
977 {'extend_existing': True, 'mysql_engine': 'InnoDB',
988 {'extend_existing': True, 'mysql_engine': 'InnoDB',
978 'mysql_charset': 'utf8'}
989 'mysql_charset': 'utf8'}
979 )
990 )
980 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
991 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
981 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
992 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
982 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
993 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
983 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
994 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
984
995
985 user = relationship('User')
996 user = relationship('User')
986 repository = relationship('Repository')
997 repository = relationship('Repository')
987 permission = relationship('Permission')
998 permission = relationship('Permission')
988
999
989 @classmethod
1000 @classmethod
990 def create(cls, user, repository, permission):
1001 def create(cls, user, repository, permission):
991 n = cls()
1002 n = cls()
992 n.user = user
1003 n.user = user
993 n.repository = repository
1004 n.repository = repository
994 n.permission = permission
1005 n.permission = permission
995 Session.add(n)
1006 Session.add(n)
996 return n
1007 return n
997
1008
998 def __unicode__(self):
1009 def __unicode__(self):
999 return u'<user:%s => %s >' % (self.user, self.repository)
1010 return u'<user:%s => %s >' % (self.user, self.repository)
1000
1011
1001
1012
1002 class UserToPerm(Base, BaseModel):
1013 class UserToPerm(Base, BaseModel):
1003 __tablename__ = 'user_to_perm'
1014 __tablename__ = 'user_to_perm'
1004 __table_args__ = (
1015 __table_args__ = (
1005 UniqueConstraint('user_id', 'permission_id'),
1016 UniqueConstraint('user_id', 'permission_id'),
1006 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1017 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1007 'mysql_charset': 'utf8'}
1018 'mysql_charset': 'utf8'}
1008 )
1019 )
1009 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1020 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1010 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1021 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1011 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1022 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1012
1023
1013 user = relationship('User')
1024 user = relationship('User')
1014 permission = relationship('Permission', lazy='joined')
1025 permission = relationship('Permission', lazy='joined')
1015
1026
1016
1027
1017 class UsersGroupRepoToPerm(Base, BaseModel):
1028 class UsersGroupRepoToPerm(Base, BaseModel):
1018 __tablename__ = 'users_group_repo_to_perm'
1029 __tablename__ = 'users_group_repo_to_perm'
1019 __table_args__ = (
1030 __table_args__ = (
1020 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1031 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1021 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1032 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1022 'mysql_charset': 'utf8'}
1033 'mysql_charset': 'utf8'}
1023 )
1034 )
1024 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1035 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1025 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1036 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1026 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1037 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1027 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1038 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1028
1039
1029 users_group = relationship('UsersGroup')
1040 users_group = relationship('UsersGroup')
1030 permission = relationship('Permission')
1041 permission = relationship('Permission')
1031 repository = relationship('Repository')
1042 repository = relationship('Repository')
1032
1043
1033 @classmethod
1044 @classmethod
1034 def create(cls, users_group, repository, permission):
1045 def create(cls, users_group, repository, permission):
1035 n = cls()
1046 n = cls()
1036 n.users_group = users_group
1047 n.users_group = users_group
1037 n.repository = repository
1048 n.repository = repository
1038 n.permission = permission
1049 n.permission = permission
1039 Session.add(n)
1050 Session.add(n)
1040 return n
1051 return n
1041
1052
1042 def __unicode__(self):
1053 def __unicode__(self):
1043 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
1054 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
1044
1055
1045
1056
1046 class UsersGroupToPerm(Base, BaseModel):
1057 class UsersGroupToPerm(Base, BaseModel):
1047 __tablename__ = 'users_group_to_perm'
1058 __tablename__ = 'users_group_to_perm'
1048 __table_args__ = (
1059 __table_args__ = (
1049 UniqueConstraint('users_group_id', 'permission_id',),
1060 UniqueConstraint('users_group_id', 'permission_id',),
1050 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1061 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1051 'mysql_charset': 'utf8'}
1062 'mysql_charset': 'utf8'}
1052 )
1063 )
1053 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1064 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1054 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1065 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1055 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1066 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1056
1067
1057 users_group = relationship('UsersGroup')
1068 users_group = relationship('UsersGroup')
1058 permission = relationship('Permission')
1069 permission = relationship('Permission')
1059
1070
1060
1071
1061 class UserRepoGroupToPerm(Base, BaseModel):
1072 class UserRepoGroupToPerm(Base, BaseModel):
1062 __tablename__ = 'user_repo_group_to_perm'
1073 __tablename__ = 'user_repo_group_to_perm'
1063 __table_args__ = (
1074 __table_args__ = (
1064 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1075 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1065 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1076 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1066 'mysql_charset': 'utf8'}
1077 'mysql_charset': 'utf8'}
1067 )
1078 )
1068
1079
1069 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1080 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1070 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1081 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1071 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1082 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1072 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1083 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1073
1084
1074 user = relationship('User')
1085 user = relationship('User')
1075 group = relationship('RepoGroup')
1086 group = relationship('RepoGroup')
1076 permission = relationship('Permission')
1087 permission = relationship('Permission')
1077
1088
1078
1089
1079 class UsersGroupRepoGroupToPerm(Base, BaseModel):
1090 class UsersGroupRepoGroupToPerm(Base, BaseModel):
1080 __tablename__ = 'users_group_repo_group_to_perm'
1091 __tablename__ = 'users_group_repo_group_to_perm'
1081 __table_args__ = (
1092 __table_args__ = (
1082 UniqueConstraint('users_group_id', 'group_id'),
1093 UniqueConstraint('users_group_id', 'group_id'),
1083 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1094 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1084 'mysql_charset': 'utf8'}
1095 'mysql_charset': 'utf8'}
1085 )
1096 )
1086
1097
1087 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)
1098 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)
1088 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1099 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1089 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1100 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1090 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1101 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1091
1102
1092 users_group = relationship('UsersGroup')
1103 users_group = relationship('UsersGroup')
1093 permission = relationship('Permission')
1104 permission = relationship('Permission')
1094 group = relationship('RepoGroup')
1105 group = relationship('RepoGroup')
1095
1106
1096
1107
1097 class Statistics(Base, BaseModel):
1108 class Statistics(Base, BaseModel):
1098 __tablename__ = 'statistics'
1109 __tablename__ = 'statistics'
1099 __table_args__ = (
1110 __table_args__ = (
1100 UniqueConstraint('repository_id'),
1111 UniqueConstraint('repository_id'),
1101 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1112 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1102 'mysql_charset': 'utf8'}
1113 'mysql_charset': 'utf8'}
1103 )
1114 )
1104 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1115 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1105 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1116 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1106 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1117 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1107 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1118 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1108 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1119 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1109 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1120 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1110
1121
1111 repository = relationship('Repository', single_parent=True)
1122 repository = relationship('Repository', single_parent=True)
1112
1123
1113
1124
1114 class UserFollowing(Base, BaseModel):
1125 class UserFollowing(Base, BaseModel):
1115 __tablename__ = 'user_followings'
1126 __tablename__ = 'user_followings'
1116 __table_args__ = (
1127 __table_args__ = (
1117 UniqueConstraint('user_id', 'follows_repository_id'),
1128 UniqueConstraint('user_id', 'follows_repository_id'),
1118 UniqueConstraint('user_id', 'follows_user_id'),
1129 UniqueConstraint('user_id', 'follows_user_id'),
1119 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1130 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1120 'mysql_charset': 'utf8'}
1131 'mysql_charset': 'utf8'}
1121 )
1132 )
1122
1133
1123 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1134 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1124 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1135 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1125 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1136 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1126 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1137 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1127 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1138 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1128
1139
1129 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1140 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1130
1141
1131 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1142 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1132 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1143 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1133
1144
1134 @classmethod
1145 @classmethod
1135 def get_repo_followers(cls, repo_id):
1146 def get_repo_followers(cls, repo_id):
1136 return cls.query().filter(cls.follows_repo_id == repo_id)
1147 return cls.query().filter(cls.follows_repo_id == repo_id)
1137
1148
1138
1149
1139 class CacheInvalidation(Base, BaseModel):
1150 class CacheInvalidation(Base, BaseModel):
1140 __tablename__ = 'cache_invalidation'
1151 __tablename__ = 'cache_invalidation'
1141 __table_args__ = (
1152 __table_args__ = (
1142 UniqueConstraint('cache_key'),
1153 UniqueConstraint('cache_key'),
1154 Index('key_idx', 'cache_key'),
1143 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1155 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1144 'mysql_charset': 'utf8'},
1156 'mysql_charset': 'utf8'},
1145 )
1157 )
1146 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1158 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1147 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1159 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1148 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1160 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1149 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1161 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1150
1162
1151 def __init__(self, cache_key, cache_args=''):
1163 def __init__(self, cache_key, cache_args=''):
1152 self.cache_key = cache_key
1164 self.cache_key = cache_key
1153 self.cache_args = cache_args
1165 self.cache_args = cache_args
1154 self.cache_active = False
1166 self.cache_active = False
1155
1167
1156 def __unicode__(self):
1168 def __unicode__(self):
1157 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1169 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1158 self.cache_id, self.cache_key)
1170 self.cache_id, self.cache_key)
1171
1159 @classmethod
1172 @classmethod
1160 def clear_cache(cls):
1173 def clear_cache(cls):
1161 cls.query().delete()
1174 cls.query().delete()
1162
1175
1163 @classmethod
1176 @classmethod
1164 def _get_key(cls, key):
1177 def _get_key(cls, key):
1165 """
1178 """
1166 Wrapper for generating a key, together with a prefix
1179 Wrapper for generating a key, together with a prefix
1167
1180
1168 :param key:
1181 :param key:
1169 """
1182 """
1170 import rhodecode
1183 import rhodecode
1171 prefix = ''
1184 prefix = ''
1172 iid = rhodecode.CONFIG.get('instance_id')
1185 iid = rhodecode.CONFIG.get('instance_id')
1173 if iid:
1186 if iid:
1174 prefix = iid
1187 prefix = iid
1175 return "%s%s" % (prefix, key), prefix, key.rstrip('_README')
1188 return "%s%s" % (prefix, key), prefix, key.rstrip('_README')
1176
1189
1177 @classmethod
1190 @classmethod
1178 def get_by_key(cls, key):
1191 def get_by_key(cls, key):
1179 return cls.query().filter(cls.cache_key == key).scalar()
1192 return cls.query().filter(cls.cache_key == key).scalar()
1180
1193
1181 @classmethod
1194 @classmethod
1182 def _get_or_create_key(cls, key, prefix, org_key):
1195 def _get_or_create_key(cls, key, prefix, org_key):
1183 inv_obj = Session.query(cls).filter(cls.cache_key == key).scalar()
1196 inv_obj = Session.query(cls).filter(cls.cache_key == key).scalar()
1184 if not inv_obj:
1197 if not inv_obj:
1185 try:
1198 try:
1186 inv_obj = CacheInvalidation(key, org_key)
1199 inv_obj = CacheInvalidation(key, org_key)
1187 Session.add(inv_obj)
1200 Session.add(inv_obj)
1188 Session.commit()
1201 Session.commit()
1189 except Exception:
1202 except Exception:
1190 log.error(traceback.format_exc())
1203 log.error(traceback.format_exc())
1191 Session.rollback()
1204 Session.rollback()
1192 return inv_obj
1205 return inv_obj
1193
1206
1194 @classmethod
1207 @classmethod
1195 def invalidate(cls, key):
1208 def invalidate(cls, key):
1196 """
1209 """
1197 Returns Invalidation object if this given key should be invalidated
1210 Returns Invalidation object if this given key should be invalidated
1198 None otherwise. `cache_active = False` means that this cache
1211 None otherwise. `cache_active = False` means that this cache
1199 state is not valid and needs to be invalidated
1212 state is not valid and needs to be invalidated
1200
1213
1201 :param key:
1214 :param key:
1202 """
1215 """
1203
1216
1204 key, _prefix, _org_key = cls._get_key(key)
1217 key, _prefix, _org_key = cls._get_key(key)
1205 inv = cls._get_or_create_key(key, _prefix, _org_key)
1218 inv = cls._get_or_create_key(key, _prefix, _org_key)
1206
1219
1207 if inv and inv.cache_active is False:
1220 if inv and inv.cache_active is False:
1208 return inv
1221 return inv
1209
1222
1210 @classmethod
1223 @classmethod
1211 def set_invalidate(cls, key):
1224 def set_invalidate(cls, key):
1212 """
1225 """
1213 Mark this Cache key for invalidation
1226 Mark this Cache key for invalidation
1214
1227
1215 :param key:
1228 :param key:
1216 """
1229 """
1217
1230
1218 key, _prefix, _org_key = cls._get_key(key)
1231 key, _prefix, _org_key = cls._get_key(key)
1219 inv_objs = Session.query(cls).filter(cls.cache_args == _org_key).all()
1232 inv_objs = Session.query(cls).filter(cls.cache_args == _org_key).all()
1220 log.debug('marking %s key[s] %s for invalidation' % (len(inv_objs),
1233 log.debug('marking %s key[s] %s for invalidation' % (len(inv_objs),
1221 _org_key))
1234 _org_key))
1222 try:
1235 try:
1223 for inv_obj in inv_objs:
1236 for inv_obj in inv_objs:
1224 if inv_obj:
1237 if inv_obj:
1225 inv_obj.cache_active = False
1238 inv_obj.cache_active = False
1226
1239
1227 Session.add(inv_obj)
1240 Session.add(inv_obj)
1228 Session.commit()
1241 Session.commit()
1229 except Exception:
1242 except Exception:
1230 log.error(traceback.format_exc())
1243 log.error(traceback.format_exc())
1231 Session.rollback()
1244 Session.rollback()
1232
1245
1233 @classmethod
1246 @classmethod
1234 def set_valid(cls, key):
1247 def set_valid(cls, key):
1235 """
1248 """
1236 Mark this cache key as active and currently cached
1249 Mark this cache key as active and currently cached
1237
1250
1238 :param key:
1251 :param key:
1239 """
1252 """
1240 inv_obj = cls.get_by_key(key)
1253 inv_obj = cls.get_by_key(key)
1241 inv_obj.cache_active = True
1254 inv_obj.cache_active = True
1242 Session.add(inv_obj)
1255 Session.add(inv_obj)
1243 Session.commit()
1256 Session.commit()
1244
1257
1258 @classmethod
1259 def get_cache_map(cls):
1260
1261 class cachemapdict(dict):
1262
1263 def __init__(self, *args, **kwargs):
1264 fixkey = kwargs.get('fixkey')
1265 if fixkey:
1266 del kwargs['fixkey']
1267 self.fixkey = fixkey
1268 super(cachemapdict, self).__init__(*args, **kwargs)
1269
1270 def __getattr__(self, name):
1271 key = name
1272 if self.fixkey:
1273 key, _prefix, _org_key = cls._get_key(key)
1274 if key in self.__dict__:
1275 return self.__dict__[key]
1276 else:
1277 return self[key]
1278
1279 def __getitem__(self, key):
1280 if self.fixkey:
1281 key, _prefix, _org_key = cls._get_key(key)
1282 try:
1283 return super(cachemapdict, self).__getitem__(key)
1284 except KeyError:
1285 return
1286
1287 cache_map = cachemapdict(fixkey=True)
1288 for obj in cls.query().all():
1289 cache_map[obj.cache_key] = cachemapdict(obj.get_dict())
1290 return cache_map
1291
1245
1292
1246 class ChangesetComment(Base, BaseModel):
1293 class ChangesetComment(Base, BaseModel):
1247 __tablename__ = 'changeset_comments'
1294 __tablename__ = 'changeset_comments'
1248 __table_args__ = (
1295 __table_args__ = (
1249 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1296 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1250 'mysql_charset': 'utf8'},
1297 'mysql_charset': 'utf8'},
1251 )
1298 )
1252 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1299 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1253 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1300 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1254 revision = Column('revision', String(40), nullable=False)
1301 revision = Column('revision', String(40), nullable=False)
1255 line_no = Column('line_no', Unicode(10), nullable=True)
1302 line_no = Column('line_no', Unicode(10), nullable=True)
1256 f_path = Column('f_path', Unicode(1000), nullable=True)
1303 f_path = Column('f_path', Unicode(1000), nullable=True)
1257 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1304 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1258 text = Column('text', Unicode(25000), nullable=False)
1305 text = Column('text', Unicode(25000), nullable=False)
1259 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1306 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1260
1307
1261 author = relationship('User', lazy='joined')
1308 author = relationship('User', lazy='joined')
1262 repo = relationship('Repository')
1309 repo = relationship('Repository')
1263 status_change = relationship('ChangesetStatus', uselist=False)
1310 status_change = relationship('ChangesetStatus', uselist=False)
1264
1311
1265 @classmethod
1312 @classmethod
1266 def get_users(cls, revision):
1313 def get_users(cls, revision):
1267 """
1314 """
1268 Returns user associated with this changesetComment. ie those
1315 Returns user associated with this changesetComment. ie those
1269 who actually commented
1316 who actually commented
1270
1317
1271 :param cls:
1318 :param cls:
1272 :param revision:
1319 :param revision:
1273 """
1320 """
1274 return Session.query(User)\
1321 return Session.query(User)\
1275 .filter(cls.revision == revision)\
1322 .filter(cls.revision == revision)\
1276 .join(ChangesetComment.author).all()
1323 .join(ChangesetComment.author).all()
1277
1324
1278
1325
1279 class ChangesetStatus(Base, BaseModel):
1326 class ChangesetStatus(Base, BaseModel):
1280 __tablename__ = 'changeset_statuses'
1327 __tablename__ = 'changeset_statuses'
1281 __table_args__ = (
1328 __table_args__ = (
1282 UniqueConstraint('repo_id', 'revision', 'version'),
1329 UniqueConstraint('repo_id', 'revision', 'version'),
1283 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1330 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1284 'mysql_charset': 'utf8'}
1331 'mysql_charset': 'utf8'}
1285 )
1332 )
1286
1333
1287 STATUSES = [
1334 STATUSES = [
1288 ('not_reviewed', _("Not Reviewed")), # (no icon) and default
1335 ('not_reviewed', _("Not Reviewed")), # (no icon) and default
1289 ('approved', _("Approved")),
1336 ('approved', _("Approved")),
1290 ('rejected', _("Rejected")),
1337 ('rejected', _("Rejected")),
1291 ('under_review', _("Under Review")),
1338 ('under_review', _("Under Review")),
1292 ]
1339 ]
1293 DEFAULT = STATUSES[0][0]
1340 DEFAULT = STATUSES[0][0]
1294
1341
1295 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1342 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1296 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1343 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1297 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1344 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1298 revision = Column('revision', String(40), nullable=False)
1345 revision = Column('revision', String(40), nullable=False)
1299 status = Column('status', String(128), nullable=False, default=DEFAULT)
1346 status = Column('status', String(128), nullable=False, default=DEFAULT)
1300 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1347 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1301 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1348 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1302 version = Column('version', Integer(), nullable=False, default=0)
1349 version = Column('version', Integer(), nullable=False, default=0)
1303 author = relationship('User', lazy='joined')
1350 author = relationship('User', lazy='joined')
1304 repo = relationship('Repository')
1351 repo = relationship('Repository')
1305 comment = relationship('ChangesetComment', lazy='joined')
1352 comment = relationship('ChangesetComment', lazy='joined')
1306
1353
1307 @classmethod
1354 @classmethod
1308 def get_status_lbl(cls, value):
1355 def get_status_lbl(cls, value):
1309 return dict(cls.STATUSES).get(value)
1356 return dict(cls.STATUSES).get(value)
1310
1357
1311 @property
1358 @property
1312 def status_lbl(self):
1359 def status_lbl(self):
1313 return ChangesetStatus.get_status_lbl(self.status)
1360 return ChangesetStatus.get_status_lbl(self.status)
1314
1361
1315
1362
1316 class Notification(Base, BaseModel):
1363 class Notification(Base, BaseModel):
1317 __tablename__ = 'notifications'
1364 __tablename__ = 'notifications'
1318 __table_args__ = (
1365 __table_args__ = (
1319 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1366 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1320 'mysql_charset': 'utf8'},
1367 'mysql_charset': 'utf8'},
1321 )
1368 )
1322
1369
1323 TYPE_CHANGESET_COMMENT = u'cs_comment'
1370 TYPE_CHANGESET_COMMENT = u'cs_comment'
1324 TYPE_MESSAGE = u'message'
1371 TYPE_MESSAGE = u'message'
1325 TYPE_MENTION = u'mention'
1372 TYPE_MENTION = u'mention'
1326 TYPE_REGISTRATION = u'registration'
1373 TYPE_REGISTRATION = u'registration'
1327 TYPE_PULL_REQUEST = u'pull_request'
1374 TYPE_PULL_REQUEST = u'pull_request'
1328
1375
1329 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1376 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1330 subject = Column('subject', Unicode(512), nullable=True)
1377 subject = Column('subject', Unicode(512), nullable=True)
1331 body = Column('body', Unicode(50000), nullable=True)
1378 body = Column('body', Unicode(50000), nullable=True)
1332 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1379 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1333 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1380 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1334 type_ = Column('type', Unicode(256))
1381 type_ = Column('type', Unicode(256))
1335
1382
1336 created_by_user = relationship('User')
1383 created_by_user = relationship('User')
1337 notifications_to_users = relationship('UserNotification', lazy='joined',
1384 notifications_to_users = relationship('UserNotification', lazy='joined',
1338 cascade="all, delete, delete-orphan")
1385 cascade="all, delete, delete-orphan")
1339
1386
1340 @property
1387 @property
1341 def recipients(self):
1388 def recipients(self):
1342 return [x.user for x in UserNotification.query()\
1389 return [x.user for x in UserNotification.query()\
1343 .filter(UserNotification.notification == self)\
1390 .filter(UserNotification.notification == self)\
1344 .order_by(UserNotification.user).all()]
1391 .order_by(UserNotification.user).all()]
1345
1392
1346 @classmethod
1393 @classmethod
1347 def create(cls, created_by, subject, body, recipients, type_=None):
1394 def create(cls, created_by, subject, body, recipients, type_=None):
1348 if type_ is None:
1395 if type_ is None:
1349 type_ = Notification.TYPE_MESSAGE
1396 type_ = Notification.TYPE_MESSAGE
1350
1397
1351 notification = cls()
1398 notification = cls()
1352 notification.created_by_user = created_by
1399 notification.created_by_user = created_by
1353 notification.subject = subject
1400 notification.subject = subject
1354 notification.body = body
1401 notification.body = body
1355 notification.type_ = type_
1402 notification.type_ = type_
1356 notification.created_on = datetime.datetime.now()
1403 notification.created_on = datetime.datetime.now()
1357
1404
1358 for u in recipients:
1405 for u in recipients:
1359 assoc = UserNotification()
1406 assoc = UserNotification()
1360 assoc.notification = notification
1407 assoc.notification = notification
1361 u.notifications.append(assoc)
1408 u.notifications.append(assoc)
1362 Session.add(notification)
1409 Session.add(notification)
1363 return notification
1410 return notification
1364
1411
1365 @property
1412 @property
1366 def description(self):
1413 def description(self):
1367 from rhodecode.model.notification import NotificationModel
1414 from rhodecode.model.notification import NotificationModel
1368 return NotificationModel().make_description(self)
1415 return NotificationModel().make_description(self)
1369
1416
1370
1417
1371 class UserNotification(Base, BaseModel):
1418 class UserNotification(Base, BaseModel):
1372 __tablename__ = 'user_to_notification'
1419 __tablename__ = 'user_to_notification'
1373 __table_args__ = (
1420 __table_args__ = (
1374 UniqueConstraint('user_id', 'notification_id'),
1421 UniqueConstraint('user_id', 'notification_id'),
1375 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1422 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1376 'mysql_charset': 'utf8'}
1423 'mysql_charset': 'utf8'}
1377 )
1424 )
1378 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1425 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1379 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1426 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1380 read = Column('read', Boolean, default=False)
1427 read = Column('read', Boolean, default=False)
1381 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1428 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1382
1429
1383 user = relationship('User', lazy="joined")
1430 user = relationship('User', lazy="joined")
1384 notification = relationship('Notification', lazy="joined",
1431 notification = relationship('Notification', lazy="joined",
1385 order_by=lambda: Notification.created_on.desc(),)
1432 order_by=lambda: Notification.created_on.desc(),)
1386
1433
1387 def mark_as_read(self):
1434 def mark_as_read(self):
1388 self.read = True
1435 self.read = True
1389 Session.add(self)
1436 Session.add(self)
1390
1437
1391
1438
1392 class DbMigrateVersion(Base, BaseModel):
1439 class DbMigrateVersion(Base, BaseModel):
1393 __tablename__ = 'db_migrate_version'
1440 __tablename__ = 'db_migrate_version'
1394 __table_args__ = (
1441 __table_args__ = (
1395 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1442 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1396 'mysql_charset': 'utf8'},
1443 'mysql_charset': 'utf8'},
1397 )
1444 )
1398 repository_id = Column('repository_id', String(250), primary_key=True)
1445 repository_id = Column('repository_id', String(250), primary_key=True)
1399 repository_path = Column('repository_path', Text)
1446 repository_path = Column('repository_path', Text)
1400 version = Column('version', Integer)
1447 version = Column('version', Integer)
@@ -1,466 +1,472 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.scm
3 rhodecode.model.scm
4 ~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~
5
5
6 Scm model for RhodeCode
6 Scm model for RhodeCode
7
7
8 :created_on: Apr 9, 2010
8 :created_on: Apr 9, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 import os
25 import os
26 import time
26 import time
27 import traceback
27 import traceback
28 import logging
28 import logging
29 import cStringIO
29 import cStringIO
30
30
31 from sqlalchemy import func
32
31 from rhodecode.lib.vcs import get_backend
33 from rhodecode.lib.vcs import get_backend
32 from rhodecode.lib.vcs.exceptions import RepositoryError
34 from rhodecode.lib.vcs.exceptions import RepositoryError
33 from rhodecode.lib.vcs.utils.lazy import LazyProperty
35 from rhodecode.lib.vcs.utils.lazy import LazyProperty
34 from rhodecode.lib.vcs.nodes import FileNode
36 from rhodecode.lib.vcs.nodes import FileNode
35
37
36 from rhodecode import BACKENDS
38 from rhodecode import BACKENDS
37 from rhodecode.lib import helpers as h
39 from rhodecode.lib import helpers as h
38 from rhodecode.lib.utils2 import safe_str, safe_unicode
40 from rhodecode.lib.utils2 import safe_str, safe_unicode
39 from rhodecode.lib.auth import HasRepoPermissionAny, HasReposGroupPermissionAny
41 from rhodecode.lib.auth import HasRepoPermissionAny, HasReposGroupPermissionAny
40 from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, \
42 from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, \
41 action_logger, EmptyChangeset, REMOVED_REPO_PAT
43 action_logger, EmptyChangeset, REMOVED_REPO_PAT
42 from rhodecode.model import BaseModel
44 from rhodecode.model import BaseModel
43 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
45 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
44 UserFollowing, UserLog, User, RepoGroup
46 UserFollowing, UserLog, User, RepoGroup
45
47
46 log = logging.getLogger(__name__)
48 log = logging.getLogger(__name__)
47
49
48
50
49 class UserTemp(object):
51 class UserTemp(object):
50 def __init__(self, user_id):
52 def __init__(self, user_id):
51 self.user_id = user_id
53 self.user_id = user_id
52
54
53 def __repr__(self):
55 def __repr__(self):
54 return "<%s('id:%s')>" % (self.__class__.__name__, self.user_id)
56 return "<%s('id:%s')>" % (self.__class__.__name__, self.user_id)
55
57
56
58
57 class RepoTemp(object):
59 class RepoTemp(object):
58 def __init__(self, repo_id):
60 def __init__(self, repo_id):
59 self.repo_id = repo_id
61 self.repo_id = repo_id
60
62
61 def __repr__(self):
63 def __repr__(self):
62 return "<%s('id:%s')>" % (self.__class__.__name__, self.repo_id)
64 return "<%s('id:%s')>" % (self.__class__.__name__, self.repo_id)
63
65
64
66
65 class CachedRepoList(object):
67 class CachedRepoList(object):
66
68
67 def __init__(self, db_repo_list, repos_path, order_by=None):
69 def __init__(self, db_repo_list, repos_path, order_by=None):
68 self.db_repo_list = db_repo_list
70 self.db_repo_list = db_repo_list
69 self.repos_path = repos_path
71 self.repos_path = repos_path
70 self.order_by = order_by
72 self.order_by = order_by
71 self.reversed = (order_by or '').startswith('-')
73 self.reversed = (order_by or '').startswith('-')
72
74
73 def __len__(self):
75 def __len__(self):
74 return len(self.db_repo_list)
76 return len(self.db_repo_list)
75
77
76 def __repr__(self):
78 def __repr__(self):
77 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
79 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
78
80
79 def __iter__(self):
81 def __iter__(self):
82 # pre-propagated cache_map to save executing select statements
83 # for each repo
84 cache_map = CacheInvalidation.get_cache_map()
85
80 for dbr in self.db_repo_list:
86 for dbr in self.db_repo_list:
81 scmr = dbr.scm_instance_cached
87 scmr = dbr.scm_instance_cached(cache_map)
82 # check permission at this level
88 # check permission at this level
83 if not HasRepoPermissionAny(
89 if not HasRepoPermissionAny(
84 'repository.read', 'repository.write', 'repository.admin'
90 'repository.read', 'repository.write', 'repository.admin'
85 )(dbr.repo_name, 'get repo check'):
91 )(dbr.repo_name, 'get repo check'):
86 continue
92 continue
87
93
88 if scmr is None:
94 if scmr is None:
89 log.error(
95 log.error(
90 '%s this repository is present in database but it '
96 '%s this repository is present in database but it '
91 'cannot be created as an scm instance' % dbr.repo_name
97 'cannot be created as an scm instance' % dbr.repo_name
92 )
98 )
93 continue
99 continue
94
100
95 last_change = scmr.last_change
101 last_change = scmr.last_change
96 tip = h.get_changeset_safe(scmr, 'tip')
102 tip = h.get_changeset_safe(scmr, 'tip')
97
103
98 tmp_d = {}
104 tmp_d = {}
99 tmp_d['name'] = dbr.repo_name
105 tmp_d['name'] = dbr.repo_name
100 tmp_d['name_sort'] = tmp_d['name'].lower()
106 tmp_d['name_sort'] = tmp_d['name'].lower()
101 tmp_d['description'] = dbr.description
107 tmp_d['description'] = dbr.description
102 tmp_d['description_sort'] = tmp_d['description']
108 tmp_d['description_sort'] = tmp_d['description']
103 tmp_d['last_change'] = last_change
109 tmp_d['last_change'] = last_change
104 tmp_d['last_change_sort'] = time.mktime(last_change.timetuple())
110 tmp_d['last_change_sort'] = time.mktime(last_change.timetuple())
105 tmp_d['tip'] = tip.raw_id
111 tmp_d['tip'] = tip.raw_id
106 tmp_d['tip_sort'] = tip.revision
112 tmp_d['tip_sort'] = tip.revision
107 tmp_d['rev'] = tip.revision
113 tmp_d['rev'] = tip.revision
108 tmp_d['contact'] = dbr.user.full_contact
114 tmp_d['contact'] = dbr.user.full_contact
109 tmp_d['contact_sort'] = tmp_d['contact']
115 tmp_d['contact_sort'] = tmp_d['contact']
110 tmp_d['owner_sort'] = tmp_d['contact']
116 tmp_d['owner_sort'] = tmp_d['contact']
111 tmp_d['repo_archives'] = list(scmr._get_archives())
117 tmp_d['repo_archives'] = list(scmr._get_archives())
112 tmp_d['last_msg'] = tip.message
118 tmp_d['last_msg'] = tip.message
113 tmp_d['author'] = tip.author
119 tmp_d['author'] = tip.author
114 tmp_d['dbrepo'] = dbr.get_dict()
120 tmp_d['dbrepo'] = dbr.get_dict()
115 tmp_d['dbrepo_fork'] = dbr.fork.get_dict() if dbr.fork else {}
121 tmp_d['dbrepo_fork'] = dbr.fork.get_dict() if dbr.fork else {}
116 yield tmp_d
122 yield tmp_d
117
123
118
124
119 class GroupList(object):
125 class GroupList(object):
120
126
121 def __init__(self, db_repo_group_list):
127 def __init__(self, db_repo_group_list):
122 self.db_repo_group_list = db_repo_group_list
128 self.db_repo_group_list = db_repo_group_list
123
129
124 def __len__(self):
130 def __len__(self):
125 return len(self.db_repo_group_list)
131 return len(self.db_repo_group_list)
126
132
127 def __repr__(self):
133 def __repr__(self):
128 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
134 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
129
135
130 def __iter__(self):
136 def __iter__(self):
131 for dbgr in self.db_repo_group_list:
137 for dbgr in self.db_repo_group_list:
132 # check permission at this level
138 # check permission at this level
133 if not HasReposGroupPermissionAny(
139 if not HasReposGroupPermissionAny(
134 'group.read', 'group.write', 'group.admin'
140 'group.read', 'group.write', 'group.admin'
135 )(dbgr.group_name, 'get group repo check'):
141 )(dbgr.group_name, 'get group repo check'):
136 continue
142 continue
137
143
138 yield dbgr
144 yield dbgr
139
145
140
146
141 class ScmModel(BaseModel):
147 class ScmModel(BaseModel):
142 """
148 """
143 Generic Scm Model
149 Generic Scm Model
144 """
150 """
145
151
146 def __get_repo(self, instance):
152 def __get_repo(self, instance):
147 cls = Repository
153 cls = Repository
148 if isinstance(instance, cls):
154 if isinstance(instance, cls):
149 return instance
155 return instance
150 elif isinstance(instance, int) or str(instance).isdigit():
156 elif isinstance(instance, int) or str(instance).isdigit():
151 return cls.get(instance)
157 return cls.get(instance)
152 elif isinstance(instance, basestring):
158 elif isinstance(instance, basestring):
153 return cls.get_by_repo_name(instance)
159 return cls.get_by_repo_name(instance)
154 elif instance:
160 elif instance:
155 raise Exception('given object must be int, basestr or Instance'
161 raise Exception('given object must be int, basestr or Instance'
156 ' of %s got %s' % (type(cls), type(instance)))
162 ' of %s got %s' % (type(cls), type(instance)))
157
163
158 @LazyProperty
164 @LazyProperty
159 def repos_path(self):
165 def repos_path(self):
160 """
166 """
161 Get's the repositories root path from database
167 Get's the repositories root path from database
162 """
168 """
163
169
164 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
170 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
165
171
166 return q.ui_value
172 return q.ui_value
167
173
168 def repo_scan(self, repos_path=None):
174 def repo_scan(self, repos_path=None):
169 """
175 """
170 Listing of repositories in given path. This path should not be a
176 Listing of repositories in given path. This path should not be a
171 repository itself. Return a dictionary of repository objects
177 repository itself. Return a dictionary of repository objects
172
178
173 :param repos_path: path to directory containing repositories
179 :param repos_path: path to directory containing repositories
174 """
180 """
175
181
176 if repos_path is None:
182 if repos_path is None:
177 repos_path = self.repos_path
183 repos_path = self.repos_path
178
184
179 log.info('scanning for repositories in %s' % repos_path)
185 log.info('scanning for repositories in %s' % repos_path)
180
186
181 baseui = make_ui('db')
187 baseui = make_ui('db')
182 repos = {}
188 repos = {}
183
189
184 for name, path in get_filesystem_repos(repos_path, recursive=True):
190 for name, path in get_filesystem_repos(repos_path, recursive=True):
185 # skip removed repos
191 # skip removed repos
186 if REMOVED_REPO_PAT.match(name):
192 if REMOVED_REPO_PAT.match(name):
187 continue
193 continue
188
194
189 # name need to be decomposed and put back together using the /
195 # name need to be decomposed and put back together using the /
190 # since this is internal storage separator for rhodecode
196 # since this is internal storage separator for rhodecode
191 name = Repository.url_sep().join(name.split(os.sep))
197 name = Repository.url_sep().join(name.split(os.sep))
192
198
193 try:
199 try:
194 if name in repos:
200 if name in repos:
195 raise RepositoryError('Duplicate repository name %s '
201 raise RepositoryError('Duplicate repository name %s '
196 'found in %s' % (name, path))
202 'found in %s' % (name, path))
197 else:
203 else:
198
204
199 klass = get_backend(path[0])
205 klass = get_backend(path[0])
200
206
201 if path[0] == 'hg' and path[0] in BACKENDS.keys():
207 if path[0] == 'hg' and path[0] in BACKENDS.keys():
202 repos[name] = klass(safe_str(path[1]), baseui=baseui)
208 repos[name] = klass(safe_str(path[1]), baseui=baseui)
203
209
204 if path[0] == 'git' and path[0] in BACKENDS.keys():
210 if path[0] == 'git' and path[0] in BACKENDS.keys():
205 repos[name] = klass(path[1])
211 repos[name] = klass(path[1])
206 except OSError:
212 except OSError:
207 continue
213 continue
208
214
209 return repos
215 return repos
210
216
211 def get_repos(self, all_repos=None, sort_key=None):
217 def get_repos(self, all_repos=None, sort_key=None):
212 """
218 """
213 Get all repos from db and for each repo create it's
219 Get all repos from db and for each repo create it's
214 backend instance and fill that backed with information from database
220 backend instance and fill that backed with information from database
215
221
216 :param all_repos: list of repository names as strings
222 :param all_repos: list of repository names as strings
217 give specific repositories list, good for filtering
223 give specific repositories list, good for filtering
218 """
224 """
219 if all_repos is None:
225 if all_repos is None:
220 all_repos = self.sa.query(Repository)\
226 all_repos = self.sa.query(Repository)\
221 .filter(Repository.group_id == None)\
227 .filter(Repository.group_id == None)\
222 .order_by(Repository.repo_name).all()
228 .order_by(func.lower(Repository.repo_name)).all()
223
229
224 repo_iter = CachedRepoList(all_repos, repos_path=self.repos_path,
230 repo_iter = CachedRepoList(all_repos, repos_path=self.repos_path,
225 order_by=sort_key)
231 order_by=sort_key)
226
232
227 return repo_iter
233 return repo_iter
228
234
229 def get_repos_groups(self, all_groups=None):
235 def get_repos_groups(self, all_groups=None):
230 if all_groups is None:
236 if all_groups is None:
231 all_groups = RepoGroup.query()\
237 all_groups = RepoGroup.query()\
232 .filter(RepoGroup.group_parent_id == None).all()
238 .filter(RepoGroup.group_parent_id == None).all()
233 group_iter = GroupList(all_groups)
239 group_iter = GroupList(all_groups)
234
240
235 return group_iter
241 return group_iter
236
242
237 def mark_for_invalidation(self, repo_name):
243 def mark_for_invalidation(self, repo_name):
238 """
244 """
239 Puts cache invalidation task into db for
245 Puts cache invalidation task into db for
240 further global cache invalidation
246 further global cache invalidation
241
247
242 :param repo_name: this repo that should invalidation take place
248 :param repo_name: this repo that should invalidation take place
243 """
249 """
244 CacheInvalidation.set_invalidate(repo_name)
250 CacheInvalidation.set_invalidate(repo_name)
245
251
246 def toggle_following_repo(self, follow_repo_id, user_id):
252 def toggle_following_repo(self, follow_repo_id, user_id):
247
253
248 f = self.sa.query(UserFollowing)\
254 f = self.sa.query(UserFollowing)\
249 .filter(UserFollowing.follows_repo_id == follow_repo_id)\
255 .filter(UserFollowing.follows_repo_id == follow_repo_id)\
250 .filter(UserFollowing.user_id == user_id).scalar()
256 .filter(UserFollowing.user_id == user_id).scalar()
251
257
252 if f is not None:
258 if f is not None:
253 try:
259 try:
254 self.sa.delete(f)
260 self.sa.delete(f)
255 action_logger(UserTemp(user_id),
261 action_logger(UserTemp(user_id),
256 'stopped_following_repo',
262 'stopped_following_repo',
257 RepoTemp(follow_repo_id))
263 RepoTemp(follow_repo_id))
258 return
264 return
259 except:
265 except:
260 log.error(traceback.format_exc())
266 log.error(traceback.format_exc())
261 raise
267 raise
262
268
263 try:
269 try:
264 f = UserFollowing()
270 f = UserFollowing()
265 f.user_id = user_id
271 f.user_id = user_id
266 f.follows_repo_id = follow_repo_id
272 f.follows_repo_id = follow_repo_id
267 self.sa.add(f)
273 self.sa.add(f)
268
274
269 action_logger(UserTemp(user_id),
275 action_logger(UserTemp(user_id),
270 'started_following_repo',
276 'started_following_repo',
271 RepoTemp(follow_repo_id))
277 RepoTemp(follow_repo_id))
272 except:
278 except:
273 log.error(traceback.format_exc())
279 log.error(traceback.format_exc())
274 raise
280 raise
275
281
276 def toggle_following_user(self, follow_user_id, user_id):
282 def toggle_following_user(self, follow_user_id, user_id):
277 f = self.sa.query(UserFollowing)\
283 f = self.sa.query(UserFollowing)\
278 .filter(UserFollowing.follows_user_id == follow_user_id)\
284 .filter(UserFollowing.follows_user_id == follow_user_id)\
279 .filter(UserFollowing.user_id == user_id).scalar()
285 .filter(UserFollowing.user_id == user_id).scalar()
280
286
281 if f is not None:
287 if f is not None:
282 try:
288 try:
283 self.sa.delete(f)
289 self.sa.delete(f)
284 return
290 return
285 except:
291 except:
286 log.error(traceback.format_exc())
292 log.error(traceback.format_exc())
287 raise
293 raise
288
294
289 try:
295 try:
290 f = UserFollowing()
296 f = UserFollowing()
291 f.user_id = user_id
297 f.user_id = user_id
292 f.follows_user_id = follow_user_id
298 f.follows_user_id = follow_user_id
293 self.sa.add(f)
299 self.sa.add(f)
294 except:
300 except:
295 log.error(traceback.format_exc())
301 log.error(traceback.format_exc())
296 raise
302 raise
297
303
298 def is_following_repo(self, repo_name, user_id, cache=False):
304 def is_following_repo(self, repo_name, user_id, cache=False):
299 r = self.sa.query(Repository)\
305 r = self.sa.query(Repository)\
300 .filter(Repository.repo_name == repo_name).scalar()
306 .filter(Repository.repo_name == repo_name).scalar()
301
307
302 f = self.sa.query(UserFollowing)\
308 f = self.sa.query(UserFollowing)\
303 .filter(UserFollowing.follows_repository == r)\
309 .filter(UserFollowing.follows_repository == r)\
304 .filter(UserFollowing.user_id == user_id).scalar()
310 .filter(UserFollowing.user_id == user_id).scalar()
305
311
306 return f is not None
312 return f is not None
307
313
308 def is_following_user(self, username, user_id, cache=False):
314 def is_following_user(self, username, user_id, cache=False):
309 u = User.get_by_username(username)
315 u = User.get_by_username(username)
310
316
311 f = self.sa.query(UserFollowing)\
317 f = self.sa.query(UserFollowing)\
312 .filter(UserFollowing.follows_user == u)\
318 .filter(UserFollowing.follows_user == u)\
313 .filter(UserFollowing.user_id == user_id).scalar()
319 .filter(UserFollowing.user_id == user_id).scalar()
314
320
315 return f is not None
321 return f is not None
316
322
317 def get_followers(self, repo_id):
323 def get_followers(self, repo_id):
318 if not isinstance(repo_id, int):
324 if not isinstance(repo_id, int):
319 repo_id = getattr(Repository.get_by_repo_name(repo_id), 'repo_id')
325 repo_id = getattr(Repository.get_by_repo_name(repo_id), 'repo_id')
320
326
321 return self.sa.query(UserFollowing)\
327 return self.sa.query(UserFollowing)\
322 .filter(UserFollowing.follows_repo_id == repo_id).count()
328 .filter(UserFollowing.follows_repo_id == repo_id).count()
323
329
324 def get_forks(self, repo_id):
330 def get_forks(self, repo_id):
325 if not isinstance(repo_id, int):
331 if not isinstance(repo_id, int):
326 repo_id = getattr(Repository.get_by_repo_name(repo_id), 'repo_id')
332 repo_id = getattr(Repository.get_by_repo_name(repo_id), 'repo_id')
327
333
328 return self.sa.query(Repository)\
334 return self.sa.query(Repository)\
329 .filter(Repository.fork_id == repo_id).count()
335 .filter(Repository.fork_id == repo_id).count()
330
336
331 def mark_as_fork(self, repo, fork, user):
337 def mark_as_fork(self, repo, fork, user):
332 repo = self.__get_repo(repo)
338 repo = self.__get_repo(repo)
333 fork = self.__get_repo(fork)
339 fork = self.__get_repo(fork)
334 repo.fork = fork
340 repo.fork = fork
335 self.sa.add(repo)
341 self.sa.add(repo)
336 return repo
342 return repo
337
343
338 def pull_changes(self, repo_name, username):
344 def pull_changes(self, repo_name, username):
339 dbrepo = Repository.get_by_repo_name(repo_name)
345 dbrepo = Repository.get_by_repo_name(repo_name)
340 clone_uri = dbrepo.clone_uri
346 clone_uri = dbrepo.clone_uri
341 if not clone_uri:
347 if not clone_uri:
342 raise Exception("This repository doesn't have a clone uri")
348 raise Exception("This repository doesn't have a clone uri")
343
349
344 repo = dbrepo.scm_instance
350 repo = dbrepo.scm_instance
345 try:
351 try:
346 extras = {
352 extras = {
347 'ip': '',
353 'ip': '',
348 'username': username,
354 'username': username,
349 'action': 'push_remote',
355 'action': 'push_remote',
350 'repository': repo_name,
356 'repository': repo_name,
351 'scm': repo.alias,
357 'scm': repo.alias,
352 }
358 }
353
359
354 # inject ui extra param to log this action via push logger
360 # inject ui extra param to log this action via push logger
355 for k, v in extras.items():
361 for k, v in extras.items():
356 repo._repo.ui.setconfig('rhodecode_extras', k, v)
362 repo._repo.ui.setconfig('rhodecode_extras', k, v)
357
363
358 repo.pull(clone_uri)
364 repo.pull(clone_uri)
359 self.mark_for_invalidation(repo_name)
365 self.mark_for_invalidation(repo_name)
360 except:
366 except:
361 log.error(traceback.format_exc())
367 log.error(traceback.format_exc())
362 raise
368 raise
363
369
364 def commit_change(self, repo, repo_name, cs, user, author, message,
370 def commit_change(self, repo, repo_name, cs, user, author, message,
365 content, f_path):
371 content, f_path):
366
372
367 if repo.alias == 'hg':
373 if repo.alias == 'hg':
368 from rhodecode.lib.vcs.backends.hg import \
374 from rhodecode.lib.vcs.backends.hg import \
369 MercurialInMemoryChangeset as IMC
375 MercurialInMemoryChangeset as IMC
370 elif repo.alias == 'git':
376 elif repo.alias == 'git':
371 from rhodecode.lib.vcs.backends.git import \
377 from rhodecode.lib.vcs.backends.git import \
372 GitInMemoryChangeset as IMC
378 GitInMemoryChangeset as IMC
373
379
374 # decoding here will force that we have proper encoded values
380 # decoding here will force that we have proper encoded values
375 # in any other case this will throw exceptions and deny commit
381 # in any other case this will throw exceptions and deny commit
376 content = safe_str(content)
382 content = safe_str(content)
377 path = safe_str(f_path)
383 path = safe_str(f_path)
378 # message and author needs to be unicode
384 # message and author needs to be unicode
379 # proper backend should then translate that into required type
385 # proper backend should then translate that into required type
380 message = safe_unicode(message)
386 message = safe_unicode(message)
381 author = safe_unicode(author)
387 author = safe_unicode(author)
382 m = IMC(repo)
388 m = IMC(repo)
383 m.change(FileNode(path, content))
389 m.change(FileNode(path, content))
384 tip = m.commit(message=message,
390 tip = m.commit(message=message,
385 author=author,
391 author=author,
386 parents=[cs], branch=cs.branch)
392 parents=[cs], branch=cs.branch)
387
393
388 new_cs = tip.short_id
394 new_cs = tip.short_id
389 action = 'push_local:%s' % new_cs
395 action = 'push_local:%s' % new_cs
390
396
391 action_logger(user, action, repo_name)
397 action_logger(user, action, repo_name)
392
398
393 self.mark_for_invalidation(repo_name)
399 self.mark_for_invalidation(repo_name)
394
400
395 def create_node(self, repo, repo_name, cs, user, author, message, content,
401 def create_node(self, repo, repo_name, cs, user, author, message, content,
396 f_path):
402 f_path):
397 if repo.alias == 'hg':
403 if repo.alias == 'hg':
398 from rhodecode.lib.vcs.backends.hg import MercurialInMemoryChangeset as IMC
404 from rhodecode.lib.vcs.backends.hg import MercurialInMemoryChangeset as IMC
399 elif repo.alias == 'git':
405 elif repo.alias == 'git':
400 from rhodecode.lib.vcs.backends.git import GitInMemoryChangeset as IMC
406 from rhodecode.lib.vcs.backends.git import GitInMemoryChangeset as IMC
401 # decoding here will force that we have proper encoded values
407 # decoding here will force that we have proper encoded values
402 # in any other case this will throw exceptions and deny commit
408 # in any other case this will throw exceptions and deny commit
403
409
404 if isinstance(content, (basestring,)):
410 if isinstance(content, (basestring,)):
405 content = safe_str(content)
411 content = safe_str(content)
406 elif isinstance(content, (file, cStringIO.OutputType,)):
412 elif isinstance(content, (file, cStringIO.OutputType,)):
407 content = content.read()
413 content = content.read()
408 else:
414 else:
409 raise Exception('Content is of unrecognized type %s' % (
415 raise Exception('Content is of unrecognized type %s' % (
410 type(content)
416 type(content)
411 ))
417 ))
412
418
413 message = safe_unicode(message)
419 message = safe_unicode(message)
414 author = safe_unicode(author)
420 author = safe_unicode(author)
415 path = safe_str(f_path)
421 path = safe_str(f_path)
416 m = IMC(repo)
422 m = IMC(repo)
417
423
418 if isinstance(cs, EmptyChangeset):
424 if isinstance(cs, EmptyChangeset):
419 # EmptyChangeset means we we're editing empty repository
425 # EmptyChangeset means we we're editing empty repository
420 parents = None
426 parents = None
421 else:
427 else:
422 parents = [cs]
428 parents = [cs]
423
429
424 m.add(FileNode(path, content=content))
430 m.add(FileNode(path, content=content))
425 tip = m.commit(message=message,
431 tip = m.commit(message=message,
426 author=author,
432 author=author,
427 parents=parents, branch=cs.branch)
433 parents=parents, branch=cs.branch)
428 new_cs = tip.short_id
434 new_cs = tip.short_id
429 action = 'push_local:%s' % new_cs
435 action = 'push_local:%s' % new_cs
430
436
431 action_logger(user, action, repo_name)
437 action_logger(user, action, repo_name)
432
438
433 self.mark_for_invalidation(repo_name)
439 self.mark_for_invalidation(repo_name)
434
440
435 def get_nodes(self, repo_name, revision, root_path='/', flat=True):
441 def get_nodes(self, repo_name, revision, root_path='/', flat=True):
436 """
442 """
437 recursive walk in root dir and return a set of all path in that dir
443 recursive walk in root dir and return a set of all path in that dir
438 based on repository walk function
444 based on repository walk function
439
445
440 :param repo_name: name of repository
446 :param repo_name: name of repository
441 :param revision: revision for which to list nodes
447 :param revision: revision for which to list nodes
442 :param root_path: root path to list
448 :param root_path: root path to list
443 :param flat: return as a list, if False returns a dict with decription
449 :param flat: return as a list, if False returns a dict with decription
444
450
445 """
451 """
446 _files = list()
452 _files = list()
447 _dirs = list()
453 _dirs = list()
448 try:
454 try:
449 _repo = self.__get_repo(repo_name)
455 _repo = self.__get_repo(repo_name)
450 changeset = _repo.scm_instance.get_changeset(revision)
456 changeset = _repo.scm_instance.get_changeset(revision)
451 root_path = root_path.lstrip('/')
457 root_path = root_path.lstrip('/')
452 for topnode, dirs, files in changeset.walk(root_path):
458 for topnode, dirs, files in changeset.walk(root_path):
453 for f in files:
459 for f in files:
454 _files.append(f.path if flat else {"name": f.path,
460 _files.append(f.path if flat else {"name": f.path,
455 "type": "file"})
461 "type": "file"})
456 for d in dirs:
462 for d in dirs:
457 _dirs.append(d.path if flat else {"name": d.path,
463 _dirs.append(d.path if flat else {"name": d.path,
458 "type": "dir"})
464 "type": "dir"})
459 except RepositoryError:
465 except RepositoryError:
460 log.debug(traceback.format_exc())
466 log.debug(traceback.format_exc())
461 raise
467 raise
462
468
463 return _dirs, _files
469 return _dirs, _files
464
470
465 def get_unread_journal(self):
471 def get_unread_journal(self):
466 return self.sa.query(UserLog).count()
472 return self.sa.query(UserLog).count()
@@ -1,313 +1,229 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('My account')} ${c.rhodecode_user.username} - ${c.rhodecode_name}
5 ${_('My account')} ${c.rhodecode_user.username} - ${c.rhodecode_name}
6 </%def>
6 </%def>
7
7
8 <%def name="breadcrumbs_links()">
8 <%def name="breadcrumbs_links()">
9 ${_('My Account')}
9 ${_('My Account')}
10 </%def>
10 </%def>
11
11
12 <%def name="page_nav()">
12 <%def name="page_nav()">
13 ${self.menu('admin')}
13 ${self.menu('admin')}
14 </%def>
14 </%def>
15
15
16 <%def name="main()">
16 <%def name="main()">
17
17
18 <div class="box box-left">
18 <div class="box box-left">
19 <!-- box / title -->
19 <!-- box / title -->
20 <div class="title">
20 <div class="title">
21 ${self.breadcrumbs()}
21 ${self.breadcrumbs()}
22 </div>
22 </div>
23 <!-- end box / title -->
23 <!-- end box / title -->
24 <div>
24 ${c.form|n}
25 ${h.form(url('admin_settings_my_account_update'),method='put')}
26 <div class="form">
27
28 <div class="field">
29 <div class="gravatar_box">
30 <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(c.user.email)}"/></div>
31 <p>
32 %if c.use_gravatar:
33 <strong>${_('Change your avatar at')} <a href="http://gravatar.com">gravatar.com</a></strong>
34 <br/>${_('Using')} ${c.user.email}
35 %else:
36 <br/>${c.user.email}
37 %endif
38 </p>
39 </div>
40 </div>
41 <div class="field">
42 <div class="label">
43 <label>${_('API key')}</label> ${c.user.api_key}
44 </div>
45 </div>
46 <div class="fields">
47 <div class="field">
48 <div class="label">
49 <label for="username">${_('Username')}:</label>
50 </div>
51 <div class="input">
52 ${h.text('username',class_="medium")}
53 </div>
54 </div>
55
56 <div class="field">
57 <div class="label">
58 <label for="new_password">${_('New password')}:</label>
59 </div>
60 <div class="input">
61 ${h.password('new_password',class_="medium",autocomplete="off")}
62 </div>
63 </div>
64
65 <div class="field">
66 <div class="label">
67 <label for="password_confirmation">${_('New password confirmation')}:</label>
68 </div>
69 <div class="input">
70 ${h.password('password_confirmation',class_="medium",autocomplete="off")}
71 </div>
72 </div>
73
74 <div class="field">
75 <div class="label">
76 <label for="name">${_('First Name')}:</label>
77 </div>
78 <div class="input">
79 ${h.text('name',class_="medium")}
80 </div>
81 </div>
82
83 <div class="field">
84 <div class="label">
85 <label for="lastname">${_('Last Name')}:</label>
86 </div>
87 <div class="input">
88 ${h.text('lastname',class_="medium")}
89 </div>
90 </div>
91
92 <div class="field">
93 <div class="label">
94 <label for="email">${_('Email')}:</label>
95 </div>
96 <div class="input">
97 ${h.text('email',class_="medium")}
98 </div>
99 </div>
100
101 <div class="buttons">
102 ${h.submit('save',_('Save'),class_="ui-button")}
103 ${h.reset('reset',_('Reset'),class_="ui-button")}
104 </div>
105 </div>
106 </div>
107 ${h.end_form()}
108 </div>
109 </div>
25 </div>
110
26
111 <div class="box box-right">
27 <div class="box box-right">
112 <!-- box / title -->
28 <!-- box / title -->
113 <div class="title">
29 <div class="title">
114 <h5>
30 <h5>
115 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" value="${_('quick filter...')}"/>
31 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" value="${_('quick filter...')}"/>
116 <a id="show_my" class="link-white" href="#my">${_('My repos')}</a> / <a id="show_perms" class="link-white" href="#perms">${_('My permissions')}</a>
32 <a id="show_my" class="link-white" href="#my">${_('My repos')}</a> / <a id="show_perms" class="link-white" href="#perms">${_('My permissions')}</a>
117 </h5>
33 </h5>
118 %if h.HasPermissionAny('hg.admin','hg.create.repository')():
34 %if h.HasPermissionAny('hg.admin','hg.create.repository')():
119 <ul class="links">
35 <ul class="links">
120 <li>
36 <li>
121 <span>${h.link_to(_('ADD'),h.url('admin_settings_create_repository'))}</span>
37 <span>${h.link_to(_('ADD'),h.url('admin_settings_create_repository'))}</span>
122 </li>
38 </li>
123 </ul>
39 </ul>
124 %endif
40 %endif
125 </div>
41 </div>
126 <!-- end box / title -->
42 <!-- end box / title -->
127 <div id="my" class="table">
43 <div id="my" class="table">
128 <div id='repos_list_wrap' class="yui-skin-sam">
44 <div id='repos_list_wrap' class="yui-skin-sam">
129 <table id="repos_list">
45 <table id="repos_list">
130 <thead>
46 <thead>
131 <tr>
47 <tr>
132 <th></th>
48 <th></th>
133 <th class="left">${_('Name')}</th>
49 <th class="left">${_('Name')}</th>
134 <th class="left">${_('Revision')}</th>
50 <th class="left">${_('Revision')}</th>
135 <th class="left">${_('Action')}</th>
51 <th class="left">${_('Action')}</th>
136 <th class="left">${_('Action')}</th>
52 <th class="left">${_('Action')}</th>
137 </thead>
53 </thead>
138 <tbody>
54 <tbody>
139 <%namespace name="dt" file="/data_table/_dt_elements.html"/>
55 <%namespace name="dt" file="/data_table/_dt_elements.html"/>
140 %if c.user_repos:
56 %if c.user_repos:
141 %for repo in c.user_repos:
57 %for repo in c.user_repos:
142 <tr>
58 <tr>
143 ##QUICK MENU
59 ##QUICK MENU
144 <td class="quick_repo_menu">
60 <td class="quick_repo_menu">
145 ${dt.quick_menu(repo['name'])}
61 ${dt.quick_menu(repo['name'])}
146 </td>
62 </td>
147 ##REPO NAME AND ICONS
63 ##REPO NAME AND ICONS
148 <td class="reponame">
64 <td class="reponame">
149 ${dt.repo_name(repo['name'],repo['dbrepo']['repo_type'],repo['dbrepo']['private'],repo['dbrepo_fork'].get('repo_name'))}
65 ${dt.repo_name(repo['name'],repo['dbrepo']['repo_type'],repo['dbrepo']['private'],repo['dbrepo_fork'].get('repo_name'))}
150 </td>
66 </td>
151 ##LAST REVISION
67 ##LAST REVISION
152 <td>
68 <td>
153 ${dt.revision(repo['name'],repo['rev'],repo['tip'],repo['author'],repo['last_msg'])}
69 ${dt.revision(repo['name'],repo['rev'],repo['tip'],repo['author'],repo['last_msg'])}
154 </td>
70 </td>
155 <td><a href="${h.url('repo_settings_home',repo_name=repo['name'])}" title="${_('edit')}"><img class="icon" alt="${_('private')}" src="${h.url('/images/icons/application_form_edit.png')}"/></a></td>
71 <td><a href="${h.url('repo_settings_home',repo_name=repo['name'])}" title="${_('edit')}"><img class="icon" alt="${_('private')}" src="${h.url('/images/icons/application_form_edit.png')}"/></a></td>
156 <td>
72 <td>
157 ${h.form(url('repo_settings_delete', repo_name=repo['name']),method='delete')}
73 ${h.form(url('repo_settings_delete', repo_name=repo['name']),method='delete')}
158 ${h.submit('remove_%s' % repo['name'],'',class_="delete_icon action_button",onclick="return confirm('"+_('Confirm to delete this repository: %s') % repo['name']+"');")}
74 ${h.submit('remove_%s' % repo['name'],'',class_="delete_icon action_button",onclick="return confirm('"+_('Confirm to delete this repository: %s') % repo['name']+"');")}
159 ${h.end_form()}
75 ${h.end_form()}
160 </td>
76 </td>
161 </tr>
77 </tr>
162 %endfor
78 %endfor
163 %else:
79 %else:
164 <div style="padding:5px 0px 10px 0px;">
80 <div style="padding:5px 0px 10px 0px;">
165 ${_('No repositories yet')}
81 ${_('No repositories yet')}
166 %if h.HasPermissionAny('hg.admin','hg.create.repository')():
82 %if h.HasPermissionAny('hg.admin','hg.create.repository')():
167 ${h.link_to(_('create one now'),h.url('admin_settings_create_repository'),class_="ui-btn")}
83 ${h.link_to(_('create one now'),h.url('admin_settings_create_repository'),class_="ui-btn")}
168 %endif
84 %endif
169 </div>
85 </div>
170 %endif
86 %endif
171 </tbody>
87 </tbody>
172 </table>
88 </table>
173 </div>
89 </div>
174 </div>
90 </div>
175 <div id="perms" class="table" style="display:none">
91 <div id="perms" class="table" style="display:none">
176 %for section in sorted(c.rhodecode_user.permissions.keys()):
92 %for section in sorted(c.rhodecode_user.permissions.keys()):
177 <div class="perms_section_head">${section.replace("_"," ").capitalize()}</div>
93 <div class="perms_section_head">${section.replace("_"," ").capitalize()}</div>
178
94
179 <div id='tbl_list_wrap_${section}' class="yui-skin-sam">
95 <div id='tbl_list_wrap_${section}' class="yui-skin-sam">
180 <table id="tbl_list_${section}">
96 <table id="tbl_list_${section}">
181 <thead>
97 <thead>
182 <tr>
98 <tr>
183 <th class="left">${_('Name')}</th>
99 <th class="left">${_('Name')}</th>
184 <th class="left">${_('Permission')}</th>
100 <th class="left">${_('Permission')}</th>
185 </thead>
101 </thead>
186 <tbody>
102 <tbody>
187 %for k in c.rhodecode_user.permissions[section]:
103 %for k in c.rhodecode_user.permissions[section]:
188 <%
104 <%
189 if section != 'global':
105 if section != 'global':
190 section_perm = c.rhodecode_user.permissions[section].get(k)
106 section_perm = c.rhodecode_user.permissions[section].get(k)
191 _perm = section_perm.split('.')[-1]
107 _perm = section_perm.split('.')[-1]
192 else:
108 else:
193 _perm = section_perm = None
109 _perm = section_perm = None
194 %>
110 %>
195 %if _perm not in ['none']:
111 %if _perm not in ['none']:
196 <tr>
112 <tr>
197 <td>
113 <td>
198 %if section == 'repositories':
114 %if section == 'repositories':
199 <a href="${h.url('summary_home',repo_name=k)}">${k}</a>
115 <a href="${h.url('summary_home',repo_name=k)}">${k}</a>
200 %elif section == 'repositories_groups':
116 %elif section == 'repositories_groups':
201 <a href="${h.url('repos_group_home',group_name=k)}">${k}</a>
117 <a href="${h.url('repos_group_home',group_name=k)}">${k}</a>
202 %else:
118 %else:
203 ${k}
119 ${k}
204 %endif
120 %endif
205 </td>
121 </td>
206 <td>
122 <td>
207 %if section == 'global':
123 %if section == 'global':
208 ${h.bool2icon(True)}
124 ${h.bool2icon(True)}
209 %else:
125 %else:
210 <span class="perm_tag ${_perm}">${section_perm}</span>
126 <span class="perm_tag ${_perm}">${section_perm}</span>
211 %endif
127 %endif
212 </td>
128 </td>
213 </tr>
129 </tr>
214 %endif
130 %endif
215 %endfor
131 %endfor
216 </tbody>
132 </tbody>
217 </table>
133 </table>
218 </div>
134 </div>
219 %endfor
135 %endfor
220 </div>
136 </div>
221 </div>
137 </div>
222 <script type="text/javascript">
138 <script type="text/javascript">
223 var filter_activate = function(){
139 var filter_activate = function(){
224 var nodes = YUQ('#my tr td a.repo_name');
140 var nodes = YUQ('#my tr td a.repo_name');
225 var func = function(node){
141 var func = function(node){
226 return node.parentNode.parentNode.parentNode.parentNode;
142 return node.parentNode.parentNode.parentNode.parentNode;
227 }
143 }
228 q_filter('q_filter',YUQ('#my tr td a.repo_name'),func);
144 q_filter('q_filter',YUQ('#my tr td a.repo_name'),func);
229 }
145 }
230
146
231 YUE.on('show_my','click',function(e){
147 YUE.on('show_my','click',function(e){
232 YUD.setStyle('perms','display','none');
148 YUD.setStyle('perms','display','none');
233 YUD.setStyle('my','display','');
149 YUD.setStyle('my','display','');
234 YUD.get('q_filter').removeAttribute('disabled');
150 YUD.get('q_filter').removeAttribute('disabled');
235 filter_activate();
151 filter_activate();
236 YUE.preventDefault(e);
152 YUE.preventDefault(e);
237 })
153 })
238 YUE.on('show_perms','click',function(e){
154 YUE.on('show_perms','click',function(e){
239 YUD.setStyle('my','display','none');
155 YUD.setStyle('my','display','none');
240 YUD.setStyle('perms','display','');
156 YUD.setStyle('perms','display','');
241 YUD.setAttribute('q_filter','disabled','disabled');
157 YUD.setAttribute('q_filter','disabled','disabled');
242 YUE.preventDefault(e);
158 YUE.preventDefault(e);
243 })
159 })
244
160
245
161
246 // main table sorting
162 // main table sorting
247 var myColumnDefs = [
163 var myColumnDefs = [
248 {key:"menu",label:"",sortable:false,className:"quick_repo_menu hidden"},
164 {key:"menu",label:"",sortable:false,className:"quick_repo_menu hidden"},
249 {key:"name",label:"${_('Name')}",sortable:true,
165 {key:"name",label:"${_('Name')}",sortable:true,
250 sortOptions: { sortFunction: nameSort }},
166 sortOptions: { sortFunction: nameSort }},
251 {key:"tip",label:"${_('Tip')}",sortable:true,
167 {key:"tip",label:"${_('Tip')}",sortable:true,
252 sortOptions: { sortFunction: revisionSort }},
168 sortOptions: { sortFunction: revisionSort }},
253 {key:"action1",label:"",sortable:false},
169 {key:"action1",label:"",sortable:false},
254 {key:"action2",label:"",sortable:false},
170 {key:"action2",label:"",sortable:false},
255 ];
171 ];
256
172
257 var myDataSource = new YAHOO.util.DataSource(YUD.get("repos_list"));
173 var myDataSource = new YAHOO.util.DataSource(YUD.get("repos_list"));
258 myDataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
174 myDataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
259 myDataSource.responseSchema = {
175 myDataSource.responseSchema = {
260 fields: [
176 fields: [
261 {key:"menu"},
177 {key:"menu"},
262 {key:"name"},
178 {key:"name"},
263 {key:"tip"},
179 {key:"tip"},
264 {key:"action1"},
180 {key:"action1"},
265 {key:"action2"},
181 {key:"action2"},
266 ]
182 ]
267 };
183 };
268 var trans_defs = {
184 var trans_defs = {
269 sortedBy:{key:"name",dir:"asc"},
185 sortedBy:{key:"name",dir:"asc"},
270 MSG_SORTASC:"${_('Click to sort ascending')}",
186 MSG_SORTASC:"${_('Click to sort ascending')}",
271 MSG_SORTDESC:"${_('Click to sort descending')}",
187 MSG_SORTDESC:"${_('Click to sort descending')}",
272 MSG_EMPTY:"${_('No records found.')}",
188 MSG_EMPTY:"${_('No records found.')}",
273 MSG_ERROR:"${_('Data error.')}",
189 MSG_ERROR:"${_('Data error.')}",
274 MSG_LOADING:"${_('Loading...')}",
190 MSG_LOADING:"${_('Loading...')}",
275 }
191 }
276 var myDataTable = new YAHOO.widget.DataTable("repos_list_wrap", myColumnDefs, myDataSource,trans_defs);
192 var myDataTable = new YAHOO.widget.DataTable("repos_list_wrap", myColumnDefs, myDataSource,trans_defs);
277 myDataTable.subscribe('postRenderEvent',function(oArgs) {
193 myDataTable.subscribe('postRenderEvent',function(oArgs) {
278 tooltip_activate();
194 tooltip_activate();
279 quick_repo_menu();
195 quick_repo_menu();
280 filter_activate();
196 filter_activate();
281 });
197 });
282
198
283 var permsColumnDefs = [
199 var permsColumnDefs = [
284 {key:"name",label:"${_('Name')}",sortable:true, sortOptions: { sortFunction: permNameSort }},
200 {key:"name",label:"${_('Name')}",sortable:true, sortOptions: { sortFunction: permNameSort }},
285 {key:"perm",label:"${_('Permission')}",sortable:false,},
201 {key:"perm",label:"${_('Permission')}",sortable:false,},
286 ];
202 ];
287
203
288 // perms repos table
204 // perms repos table
289 var myDataSource2 = new YAHOO.util.DataSource(YUD.get("tbl_list_repositories"));
205 var myDataSource2 = new YAHOO.util.DataSource(YUD.get("tbl_list_repositories"));
290 myDataSource2.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
206 myDataSource2.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
291 myDataSource2.responseSchema = {
207 myDataSource2.responseSchema = {
292 fields: [
208 fields: [
293 {key:"name"},
209 {key:"name"},
294 {key:"perm"},
210 {key:"perm"},
295 ]
211 ]
296 };
212 };
297
213
298 new YAHOO.widget.DataTable("tbl_list_wrap_repositories", permsColumnDefs, myDataSource2, trans_defs);
214 new YAHOO.widget.DataTable("tbl_list_wrap_repositories", permsColumnDefs, myDataSource2, trans_defs);
299
215
300 //perms groups table
216 //perms groups table
301 var myDataSource3 = new YAHOO.util.DataSource(YUD.get("tbl_list_repositories_groups"));
217 var myDataSource3 = new YAHOO.util.DataSource(YUD.get("tbl_list_repositories_groups"));
302 myDataSource3.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
218 myDataSource3.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
303 myDataSource3.responseSchema = {
219 myDataSource3.responseSchema = {
304 fields: [
220 fields: [
305 {key:"name"},
221 {key:"name"},
306 {key:"perm"},
222 {key:"perm"},
307 ]
223 ]
308 };
224 };
309
225
310 new YAHOO.widget.DataTable("tbl_list_wrap_repositories_groups", permsColumnDefs, myDataSource3, trans_defs);
226 new YAHOO.widget.DataTable("tbl_list_wrap_repositories_groups", permsColumnDefs, myDataSource3, trans_defs);
311
227
312 </script>
228 </script>
313 </%def>
229 </%def>
General Comments 0
You need to be logged in to leave comments. Login now