##// END OF EJS Templates
Added more advanced hook management into rhodecode admin settings
marcink -
r1460:b5034881 beta
parent child Browse files
Show More
@@ -0,0 +1,96
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
3
4 <%def name="title()">
5 ${_('Settings administration')} - ${c.rhodecode_name}
6 </%def>
7
8 <%def name="breadcrumbs_links()">
9 ${h.link_to(_('Admin'),h.url('admin_home'))} &raquo; ${_('Settings')}
10 </%def>
11
12 <%def name="page_nav()">
13 ${self.menu('admin')}
14 </%def>
15
16 <%def name="main()">
17 <div class="box">
18 <!-- box / title -->
19 <div class="title">
20 ${self.breadcrumbs()}
21 </div>
22 <!-- end box / title -->
23
24 <h3>${_('Built in hooks - read only')}</h3>
25 <div class="form">
26 <div class="fields">
27 % for hook in c.hooks:
28 <div class="field">
29 <div class="label label">
30 <label for="${hook.ui_key}">${hook.ui_key}</label>
31 </div>
32 <div class="input" style="margin-left:280px">
33 ${h.text(hook.ui_key,hook.ui_value,size=60,readonly="readonly")}
34 </div>
35 </div>
36 % endfor
37 </div>
38 </div>
39
40 <h3>${_('Custom hooks')}</h3>
41 ${h.form(url('admin_setting', setting_id='hooks'),method='put')}
42 <div class="form">
43 <div class="fields">
44
45 % for hook in c.custom_hooks:
46 <div class="field" id="${'id%s' % hook.ui_id }">
47 <div class="label label">
48 <label for="${hook.ui_key}">${hook.ui_key}</label>
49 </div>
50 <div class="input" style="margin-left:280px">
51 ${h.hidden('hook_ui_key',hook.ui_key)}
52 ${h.hidden('hook_ui_value',hook.ui_value)}
53 ${h.text('hook_ui_value_new',hook.ui_value,size=60)}
54 <span class="delete_icon action_button"
55 onclick="ajaxActionHook(${hook.ui_id},'${'id%s' % hook.ui_id }')">
56 ${_('remove')}
57 </span>
58 </div>
59 </div>
60 % endfor
61
62 <div class="field">
63 <div class="input" style="margin-left:-180px;position: absolute;">
64 <div class="input">
65 ${h.text('new_hook_ui_key',size=30)}
66 </div>
67 </div>
68 <div class="input" style="margin-left:280px">
69 ${h.text('new_hook_ui_value',size=60)}
70 </div>
71 </div>
72 <div class="buttons" style="margin-left:280px">
73 ${h.submit('save','Save',class_="ui-button")}
74 </div>
75 </div>
76 </div>
77 ${h.end_form()}
78 </div>
79 <script type="text/javascript">
80 function ajaxActionHook(hook_id,field_id) {
81 var sUrl = "${h.url('admin_setting', setting_id='hooks')}";
82 var callback = {
83 success: function (o) {
84 var elem = YUD.get(""+field_id);
85 elem.parentNode.removeChild(elem);
86 },
87 failure: function (o) {
88 alert("${_('Failed to remove hook')}");
89 },
90 };
91 var postData = '_method=delete&hook_id=' + hook_id;
92 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);
93 };
94 </script>
95
96 </%def>
@@ -1,365 +1,407
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) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 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
29
30 from sqlalchemy import func
30 from sqlalchemy import func
31 from formencode import htmlfill
31 from formencode import htmlfill
32 from pylons import request, session, tmpl_context as c, url, config
32 from pylons import request, session, tmpl_context as c, url, config
33 from pylons.controllers.util import abort, redirect
33 from pylons.controllers.util import abort, redirect
34 from pylons.i18n.translation import _
34 from pylons.i18n.translation import _
35
35
36 from rhodecode.lib import helpers as h
36 from rhodecode.lib import helpers as h
37 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
37 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
38 HasPermissionAnyDecorator, NotAnonymous
38 HasPermissionAnyDecorator, NotAnonymous
39 from rhodecode.lib.base import BaseController, render
39 from rhodecode.lib.base import BaseController, render
40 from rhodecode.lib.celerylib import tasks, run_task
40 from rhodecode.lib.celerylib import tasks, run_task
41 from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
41 from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
42 set_rhodecode_config, repo_name_slug
42 set_rhodecode_config, repo_name_slug
43 from rhodecode.model.db import RhodeCodeUi, Repository, Group, \
43 from rhodecode.model.db import RhodeCodeUi, Repository, Group, \
44 RhodeCodeSettings
44 RhodeCodeSettings
45 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
45 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
46 ApplicationUiSettingsForm
46 ApplicationUiSettingsForm
47 from rhodecode.model.scm import ScmModel
47 from rhodecode.model.scm import ScmModel
48 from rhodecode.model.user import UserModel
48 from rhodecode.model.user import UserModel
49
49
50 log = logging.getLogger(__name__)
50 log = logging.getLogger(__name__)
51
51
52
52
53 class SettingsController(BaseController):
53 class SettingsController(BaseController):
54 """REST Controller styled on the Atom Publishing Protocol"""
54 """REST Controller styled on the Atom Publishing Protocol"""
55 # To properly map this controller, ensure your config/routing.py
55 # To properly map this controller, ensure your config/routing.py
56 # file has a resource setup:
56 # file has a resource setup:
57 # map.resource('setting', 'settings', controller='admin/settings',
57 # map.resource('setting', 'settings', controller='admin/settings',
58 # path_prefix='/admin', name_prefix='admin_')
58 # path_prefix='/admin', name_prefix='admin_')
59
59
60 @LoginRequired()
60 @LoginRequired()
61 def __before__(self):
61 def __before__(self):
62 c.admin_user = session.get('admin_user')
62 c.admin_user = session.get('admin_user')
63 c.admin_username = session.get('admin_username')
63 c.admin_username = session.get('admin_username')
64 super(SettingsController, self).__before__()
64 super(SettingsController, self).__before__()
65
65
66 @HasPermissionAllDecorator('hg.admin')
66 @HasPermissionAllDecorator('hg.admin')
67 def index(self, format='html'):
67 def index(self, format='html'):
68 """GET /admin/settings: All items in the collection"""
68 """GET /admin/settings: All items in the collection"""
69 # url('admin_settings')
69 # url('admin_settings')
70
70
71 defaults = RhodeCodeSettings.get_app_settings()
71 defaults = RhodeCodeSettings.get_app_settings()
72 defaults.update(self.get_hg_ui_settings())
72 defaults.update(self.get_hg_ui_settings())
73 return htmlfill.render(
73 return htmlfill.render(
74 render('admin/settings/settings.html'),
74 render('admin/settings/settings.html'),
75 defaults=defaults,
75 defaults=defaults,
76 encoding="UTF-8",
76 encoding="UTF-8",
77 force_defaults=False
77 force_defaults=False
78 )
78 )
79
79
80 @HasPermissionAllDecorator('hg.admin')
80 @HasPermissionAllDecorator('hg.admin')
81 def create(self):
81 def create(self):
82 """POST /admin/settings: Create a new item"""
82 """POST /admin/settings: Create a new item"""
83 # url('admin_settings')
83 # url('admin_settings')
84
84
85 @HasPermissionAllDecorator('hg.admin')
85 @HasPermissionAllDecorator('hg.admin')
86 def new(self, format='html'):
86 def new(self, format='html'):
87 """GET /admin/settings/new: Form to create a new item"""
87 """GET /admin/settings/new: Form to create a new item"""
88 # url('admin_new_setting')
88 # url('admin_new_setting')
89
89
90 @HasPermissionAllDecorator('hg.admin')
90 @HasPermissionAllDecorator('hg.admin')
91 def update(self, setting_id):
91 def update(self, setting_id):
92 """PUT /admin/settings/setting_id: Update an existing item"""
92 """PUT /admin/settings/setting_id: Update an existing item"""
93 # Forms posted to this method should contain a hidden field:
93 # Forms posted to this method should contain a hidden field:
94 # <input type="hidden" name="_method" value="PUT" />
94 # <input type="hidden" name="_method" value="PUT" />
95 # Or using helpers:
95 # Or using helpers:
96 # h.form(url('admin_setting', setting_id=ID),
96 # h.form(url('admin_setting', setting_id=ID),
97 # method='put')
97 # method='put')
98 # url('admin_setting', setting_id=ID)
98 # url('admin_setting', setting_id=ID)
99 if setting_id == 'mapping':
99 if setting_id == 'mapping':
100 rm_obsolete = request.POST.get('destroy', False)
100 rm_obsolete = request.POST.get('destroy', False)
101 log.debug('Rescanning directories with destroy=%s', rm_obsolete)
101 log.debug('Rescanning directories with destroy=%s', rm_obsolete)
102 initial = ScmModel().repo_scan()
102 initial = ScmModel().repo_scan()
103 log.debug('invalidating all repositories')
103 log.debug('invalidating all repositories')
104 for repo_name in initial.keys():
104 for repo_name in initial.keys():
105 invalidate_cache('get_repo_cached_%s' % repo_name)
105 invalidate_cache('get_repo_cached_%s' % repo_name)
106
106
107 added, removed = repo2db_mapper(initial, rm_obsolete)
107 added, removed = repo2db_mapper(initial, rm_obsolete)
108
108
109 h.flash(_('Repositories successfully'
109 h.flash(_('Repositories successfully'
110 ' rescanned added: %s,removed: %s') % (added, removed),
110 ' rescanned added: %s,removed: %s') % (added, removed),
111 category='success')
111 category='success')
112
112
113 if setting_id == 'whoosh':
113 if setting_id == 'whoosh':
114 repo_location = self.get_hg_ui_settings()['paths_root_path']
114 repo_location = self.get_hg_ui_settings()['paths_root_path']
115 full_index = request.POST.get('full_index', False)
115 full_index = request.POST.get('full_index', False)
116 run_task(tasks.whoosh_index, repo_location, full_index)
116 run_task(tasks.whoosh_index, repo_location, full_index)
117
117
118 h.flash(_('Whoosh reindex task scheduled'), category='success')
118 h.flash(_('Whoosh reindex task scheduled'), category='success')
119 if setting_id == 'global':
119 if setting_id == 'global':
120
120
121 application_form = ApplicationSettingsForm()()
121 application_form = ApplicationSettingsForm()()
122 try:
122 try:
123 form_result = application_form.to_python(dict(request.POST))
123 form_result = application_form.to_python(dict(request.POST))
124
124
125 try:
125 try:
126 hgsettings1 = RhodeCodeSettings.get_by_name('title')
126 hgsettings1 = RhodeCodeSettings.get_by_name('title')
127 hgsettings1.app_settings_value = \
127 hgsettings1.app_settings_value = \
128 form_result['rhodecode_title']
128 form_result['rhodecode_title']
129
129
130 hgsettings2 = RhodeCodeSettings.get_by_name('realm')
130 hgsettings2 = RhodeCodeSettings.get_by_name('realm')
131 hgsettings2.app_settings_value = \
131 hgsettings2.app_settings_value = \
132 form_result['rhodecode_realm']
132 form_result['rhodecode_realm']
133
133
134 hgsettings3 = RhodeCodeSettings.get_by_name('ga_code')
134 hgsettings3 = RhodeCodeSettings.get_by_name('ga_code')
135 hgsettings3.app_settings_value = \
135 hgsettings3.app_settings_value = \
136 form_result['rhodecode_ga_code']
136 form_result['rhodecode_ga_code']
137
137
138 self.sa.add(hgsettings1)
138 self.sa.add(hgsettings1)
139 self.sa.add(hgsettings2)
139 self.sa.add(hgsettings2)
140 self.sa.add(hgsettings3)
140 self.sa.add(hgsettings3)
141 self.sa.commit()
141 self.sa.commit()
142 set_rhodecode_config(config)
142 set_rhodecode_config(config)
143 h.flash(_('Updated application settings'),
143 h.flash(_('Updated application settings'),
144 category='success')
144 category='success')
145
145
146 except Exception:
146 except Exception:
147 log.error(traceback.format_exc())
147 log.error(traceback.format_exc())
148 h.flash(_('error occurred during updating '
148 h.flash(_('error occurred during updating '
149 'application settings'),
149 'application settings'),
150 category='error')
150 category='error')
151
151
152 self.sa.rollback()
152 self.sa.rollback()
153
153
154 except formencode.Invalid, errors:
154 except formencode.Invalid, errors:
155 return htmlfill.render(
155 return htmlfill.render(
156 render('admin/settings/settings.html'),
156 render('admin/settings/settings.html'),
157 defaults=errors.value,
157 defaults=errors.value,
158 errors=errors.error_dict or {},
158 errors=errors.error_dict or {},
159 prefix_error=False,
159 prefix_error=False,
160 encoding="UTF-8")
160 encoding="UTF-8")
161
161
162 if setting_id == 'mercurial':
162 if setting_id == 'mercurial':
163 application_form = ApplicationUiSettingsForm()()
163 application_form = ApplicationUiSettingsForm()()
164 try:
164 try:
165 form_result = application_form.to_python(dict(request.POST))
165 form_result = application_form.to_python(dict(request.POST))
166
166
167 try:
167 try:
168
168
169 hgsettings1 = self.sa.query(RhodeCodeUi)\
169 hgsettings1 = self.sa.query(RhodeCodeUi)\
170 .filter(RhodeCodeUi.ui_key == 'push_ssl').one()
170 .filter(RhodeCodeUi.ui_key == 'push_ssl').one()
171 hgsettings1.ui_value = form_result['web_push_ssl']
171 hgsettings1.ui_value = form_result['web_push_ssl']
172
172
173 hgsettings2 = self.sa.query(RhodeCodeUi)\
173 hgsettings2 = self.sa.query(RhodeCodeUi)\
174 .filter(RhodeCodeUi.ui_key == '/').one()
174 .filter(RhodeCodeUi.ui_key == '/').one()
175 hgsettings2.ui_value = form_result['paths_root_path']
175 hgsettings2.ui_value = form_result['paths_root_path']
176
176
177 #HOOKS
177 #HOOKS
178 hgsettings3 = self.sa.query(RhodeCodeUi)\
178 hgsettings3 = self.sa.query(RhodeCodeUi)\
179 .filter(RhodeCodeUi.ui_key == 'changegroup.update').one()
179 .filter(RhodeCodeUi.ui_key == 'changegroup.update').one()
180 hgsettings3.ui_active = \
180 hgsettings3.ui_active = \
181 bool(form_result['hooks_changegroup_update'])
181 bool(form_result['hooks_changegroup_update'])
182
182
183 hgsettings4 = self.sa.query(RhodeCodeUi)\
183 hgsettings4 = self.sa.query(RhodeCodeUi)\
184 .filter(RhodeCodeUi.ui_key ==
184 .filter(RhodeCodeUi.ui_key ==
185 'changegroup.repo_size').one()
185 'changegroup.repo_size').one()
186 hgsettings4.ui_active = \
186 hgsettings4.ui_active = \
187 bool(form_result['hooks_changegroup_repo_size'])
187 bool(form_result['hooks_changegroup_repo_size'])
188
188
189 hgsettings5 = self.sa.query(RhodeCodeUi)\
189 hgsettings5 = self.sa.query(RhodeCodeUi)\
190 .filter(RhodeCodeUi.ui_key ==
190 .filter(RhodeCodeUi.ui_key ==
191 'pretxnchangegroup.push_logger').one()
191 'pretxnchangegroup.push_logger').one()
192 hgsettings5.ui_active = \
192 hgsettings5.ui_active = \
193 bool(form_result['hooks_pretxnchangegroup'
193 bool(form_result['hooks_pretxnchangegroup'
194 '_push_logger'])
194 '_push_logger'])
195
195
196 hgsettings6 = self.sa.query(RhodeCodeUi)\
196 hgsettings6 = self.sa.query(RhodeCodeUi)\
197 .filter(RhodeCodeUi.ui_key ==
197 .filter(RhodeCodeUi.ui_key ==
198 'preoutgoing.pull_logger').one()
198 'preoutgoing.pull_logger').one()
199 hgsettings6.ui_active = \
199 hgsettings6.ui_active = \
200 bool(form_result['hooks_preoutgoing_pull_logger'])
200 bool(form_result['hooks_preoutgoing_pull_logger'])
201
201
202 self.sa.add(hgsettings1)
202 self.sa.add(hgsettings1)
203 self.sa.add(hgsettings2)
203 self.sa.add(hgsettings2)
204 self.sa.add(hgsettings3)
204 self.sa.add(hgsettings3)
205 self.sa.add(hgsettings4)
205 self.sa.add(hgsettings4)
206 self.sa.add(hgsettings5)
206 self.sa.add(hgsettings5)
207 self.sa.add(hgsettings6)
207 self.sa.add(hgsettings6)
208 self.sa.commit()
208 self.sa.commit()
209
209
210 h.flash(_('Updated mercurial settings'),
210 h.flash(_('Updated mercurial settings'),
211 category='success')
211 category='success')
212
212
213 except:
213 except:
214 log.error(traceback.format_exc())
214 log.error(traceback.format_exc())
215 h.flash(_('error occurred during updating '
215 h.flash(_('error occurred during updating '
216 'application settings'), category='error')
216 'application settings'), category='error')
217
217
218 self.sa.rollback()
218 self.sa.rollback()
219
219
220 except formencode.Invalid, errors:
220 except formencode.Invalid, errors:
221 return htmlfill.render(
221 return htmlfill.render(
222 render('admin/settings/settings.html'),
222 render('admin/settings/settings.html'),
223 defaults=errors.value,
223 defaults=errors.value,
224 errors=errors.error_dict or {},
224 errors=errors.error_dict or {},
225 prefix_error=False,
225 prefix_error=False,
226 encoding="UTF-8")
226 encoding="UTF-8")
227
227
228
229 if setting_id == 'hooks':
230 ui_key = request.POST.get('new_hook_ui_key')
231 ui_value = request.POST.get('new_hook_ui_value')
232 try:
233
234 if ui_value and ui_key:
235 RhodeCodeUi.create_or_update_hook(ui_key, ui_value)
236 h.flash(_('Added new hook'),
237 category='success')
238
239 # check for edits
240 update = False
241 _d = request.POST.dict_of_lists()
242 for k, v in zip(_d.get('hook_ui_key',[]), _d.get('hook_ui_value_new',[])):
243 RhodeCodeUi.create_or_update_hook(k, v)
244 update = True
245
246 if update:
247 h.flash(_('Updated hooks'), category='success')
248
249 except:
250 log.error(traceback.format_exc())
251 h.flash(_('error occurred during hook creation'),
252 category='error')
253
254 return redirect(url('admin_edit_setting', setting_id='hooks'))
255
228 return redirect(url('admin_settings'))
256 return redirect(url('admin_settings'))
229
257
230 @HasPermissionAllDecorator('hg.admin')
258 @HasPermissionAllDecorator('hg.admin')
231 def delete(self, setting_id):
259 def delete(self, setting_id):
232 """DELETE /admin/settings/setting_id: Delete an existing item"""
260 """DELETE /admin/settings/setting_id: Delete an existing item"""
233 # Forms posted to this method should contain a hidden field:
261 # Forms posted to this method should contain a hidden field:
234 # <input type="hidden" name="_method" value="DELETE" />
262 # <input type="hidden" name="_method" value="DELETE" />
235 # Or using helpers:
263 # Or using helpers:
236 # h.form(url('admin_setting', setting_id=ID),
264 # h.form(url('admin_setting', setting_id=ID),
237 # method='delete')
265 # method='delete')
238 # url('admin_setting', setting_id=ID)
266 # url('admin_setting', setting_id=ID)
239
267 if setting_id == 'hooks':
268 hook_id = request.POST.get('hook_id')
269 RhodeCodeUi.delete(hook_id)
270
271
240 @HasPermissionAllDecorator('hg.admin')
272 @HasPermissionAllDecorator('hg.admin')
241 def show(self, setting_id, format='html'):
273 def show(self, setting_id, format='html'):
242 """
274 """
243 GET /admin/settings/setting_id: Show a specific item"""
275 GET /admin/settings/setting_id: Show a specific item"""
244 # url('admin_setting', setting_id=ID)
276 # url('admin_setting', setting_id=ID)
245
277
246 @HasPermissionAllDecorator('hg.admin')
278 @HasPermissionAllDecorator('hg.admin')
247 def edit(self, setting_id, format='html'):
279 def edit(self, setting_id, format='html'):
248 """
280 """
249 GET /admin/settings/setting_id/edit: Form to
281 GET /admin/settings/setting_id/edit: Form to
250 edit an existing item"""
282 edit an existing item"""
251 # url('admin_edit_setting', setting_id=ID)
283 # url('admin_edit_setting', setting_id=ID)
252
284
285 c.hooks = RhodeCodeUi.get_builtin_hooks()
286 c.custom_hooks = RhodeCodeUi.get_custom_hooks()
287
288 return htmlfill.render(
289 render('admin/settings/hooks.html'),
290 defaults={},
291 encoding="UTF-8",
292 force_defaults=False
293 )
294
253 @NotAnonymous()
295 @NotAnonymous()
254 def my_account(self):
296 def my_account(self):
255 """
297 """
256 GET /_admin/my_account Displays info about my account
298 GET /_admin/my_account Displays info about my account
257 """
299 """
258 # url('admin_settings_my_account')
300 # url('admin_settings_my_account')
259
301
260 c.user = UserModel().get(self.rhodecode_user.user_id, cache=False)
302 c.user = UserModel().get(self.rhodecode_user.user_id, cache=False)
261 all_repos = self.sa.query(Repository)\
303 all_repos = self.sa.query(Repository)\
262 .filter(Repository.user_id == c.user.user_id)\
304 .filter(Repository.user_id == c.user.user_id)\
263 .order_by(func.lower(Repository.repo_name)).all()
305 .order_by(func.lower(Repository.repo_name)).all()
264
306
265 c.user_repos = ScmModel().get_repos(all_repos)
307 c.user_repos = ScmModel().get_repos(all_repos)
266
308
267 if c.user.username == 'default':
309 if c.user.username == 'default':
268 h.flash(_("You can't edit this user since it's"
310 h.flash(_("You can't edit this user since it's"
269 " crucial for entire application"), category='warning')
311 " crucial for entire application"), category='warning')
270 return redirect(url('users'))
312 return redirect(url('users'))
271
313
272 defaults = c.user.get_dict()
314 defaults = c.user.get_dict()
273 return htmlfill.render(
315 return htmlfill.render(
274 render('admin/users/user_edit_my_account.html'),
316 render('admin/users/user_edit_my_account.html'),
275 defaults=defaults,
317 defaults=defaults,
276 encoding="UTF-8",
318 encoding="UTF-8",
277 force_defaults=False
319 force_defaults=False
278 )
320 )
279
321
280 def my_account_update(self):
322 def my_account_update(self):
281 """PUT /_admin/my_account_update: Update an existing item"""
323 """PUT /_admin/my_account_update: Update an existing item"""
282 # Forms posted to this method should contain a hidden field:
324 # Forms posted to this method should contain a hidden field:
283 # <input type="hidden" name="_method" value="PUT" />
325 # <input type="hidden" name="_method" value="PUT" />
284 # Or using helpers:
326 # Or using helpers:
285 # h.form(url('admin_settings_my_account_update'),
327 # h.form(url('admin_settings_my_account_update'),
286 # method='put')
328 # method='put')
287 # url('admin_settings_my_account_update', id=ID)
329 # url('admin_settings_my_account_update', id=ID)
288 user_model = UserModel()
330 user_model = UserModel()
289 uid = self.rhodecode_user.user_id
331 uid = self.rhodecode_user.user_id
290 _form = UserForm(edit=True,
332 _form = UserForm(edit=True,
291 old_data={'user_id': uid,
333 old_data={'user_id': uid,
292 'email': self.rhodecode_user.email})()
334 'email': self.rhodecode_user.email})()
293 form_result = {}
335 form_result = {}
294 try:
336 try:
295 form_result = _form.to_python(dict(request.POST))
337 form_result = _form.to_python(dict(request.POST))
296 user_model.update_my_account(uid, form_result)
338 user_model.update_my_account(uid, form_result)
297 h.flash(_('Your account was updated successfully'),
339 h.flash(_('Your account was updated successfully'),
298 category='success')
340 category='success')
299
341
300 except formencode.Invalid, errors:
342 except formencode.Invalid, errors:
301 c.user = user_model.get(self.rhodecode_user.user_id, cache=False)
343 c.user = user_model.get(self.rhodecode_user.user_id, cache=False)
302 c.user = UserModel().get(self.rhodecode_user.user_id, cache=False)
344 c.user = UserModel().get(self.rhodecode_user.user_id, cache=False)
303 all_repos = self.sa.query(Repository)\
345 all_repos = self.sa.query(Repository)\
304 .filter(Repository.user_id == c.user.user_id)\
346 .filter(Repository.user_id == c.user.user_id)\
305 .order_by(func.lower(Repository.repo_name))\
347 .order_by(func.lower(Repository.repo_name))\
306 .all()
348 .all()
307 c.user_repos = ScmModel().get_repos(all_repos)
349 c.user_repos = ScmModel().get_repos(all_repos)
308
350
309 return htmlfill.render(
351 return htmlfill.render(
310 render('admin/users/user_edit_my_account.html'),
352 render('admin/users/user_edit_my_account.html'),
311 defaults=errors.value,
353 defaults=errors.value,
312 errors=errors.error_dict or {},
354 errors=errors.error_dict or {},
313 prefix_error=False,
355 prefix_error=False,
314 encoding="UTF-8")
356 encoding="UTF-8")
315 except Exception:
357 except Exception:
316 log.error(traceback.format_exc())
358 log.error(traceback.format_exc())
317 h.flash(_('error occurred during update of user %s') \
359 h.flash(_('error occurred during update of user %s') \
318 % form_result.get('username'), category='error')
360 % form_result.get('username'), category='error')
319
361
320 return redirect(url('my_account'))
362 return redirect(url('my_account'))
321
363
322 @NotAnonymous()
364 @NotAnonymous()
323 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
365 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
324 def create_repository(self):
366 def create_repository(self):
325 """GET /_admin/create_repository: Form to create a new item"""
367 """GET /_admin/create_repository: Form to create a new item"""
326
368
327 c.repo_groups = [('', '')]
369 c.repo_groups = [('', '')]
328 parents_link = lambda k: h.literal('&raquo;'.join(
370 parents_link = lambda k: h.literal('&raquo;'.join(
329 map(lambda k: k.group_name,
371 map(lambda k: k.group_name,
330 k.parents + [k])
372 k.parents + [k])
331 )
373 )
332 )
374 )
333
375
334 c.repo_groups.extend([(x.group_id, parents_link(x)) for \
376 c.repo_groups.extend([(x.group_id, parents_link(x)) for \
335 x in self.sa.query(Group).all()])
377 x in self.sa.query(Group).all()])
336 c.repo_groups = sorted(c.repo_groups,
378 c.repo_groups = sorted(c.repo_groups,
337 key=lambda t: t[1].split('&raquo;')[0])
379 key=lambda t: t[1].split('&raquo;')[0])
338 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
380 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
339
381
340 new_repo = request.GET.get('repo', '')
382 new_repo = request.GET.get('repo', '')
341 c.new_repo = repo_name_slug(new_repo)
383 c.new_repo = repo_name_slug(new_repo)
342
384
343 return render('admin/repos/repo_add_create_repository.html')
385 return render('admin/repos/repo_add_create_repository.html')
344
386
345 def get_hg_ui_settings(self):
387 def get_hg_ui_settings(self):
346 ret = self.sa.query(RhodeCodeUi).all()
388 ret = self.sa.query(RhodeCodeUi).all()
347
389
348 if not ret:
390 if not ret:
349 raise Exception('Could not get application ui settings !')
391 raise Exception('Could not get application ui settings !')
350 settings = {}
392 settings = {}
351 for each in ret:
393 for each in ret:
352 k = each.ui_key
394 k = each.ui_key
353 v = each.ui_value
395 v = each.ui_value
354 if k == '/':
396 if k == '/':
355 k = 'root_path'
397 k = 'root_path'
356
398
357 if k.find('.') != -1:
399 if k.find('.') != -1:
358 k = k.replace('.', '_')
400 k = k.replace('.', '_')
359
401
360 if each.ui_section == 'hooks':
402 if each.ui_section == 'hooks':
361 v = each.ui_active
403 v = each.ui_active
362
404
363 settings[each.ui_section + '_' + k] = v
405 settings[each.ui_section + '_' + k] = v
364
406
365 return settings
407 return settings
@@ -1,512 +1,512
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.db_manage
3 rhodecode.lib.db_manage
4 ~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Database creation, and setup module for RhodeCode. Used for creation
6 Database creation, and setup module for RhodeCode. Used for creation
7 of database as well as for migration operations
7 of database as well as for migration operations
8
8
9 :created_on: Apr 10, 2010
9 :created_on: Apr 10, 2010
10 :author: marcink
10 :author: marcink
11 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
12 :license: GPLv3, see COPYING for more details.
12 :license: GPLv3, see COPYING for more details.
13 """
13 """
14 # This program is free software: you can redistribute it and/or modify
14 # This program is free software: you can redistribute it and/or modify
15 # it under the terms of the GNU General Public License as published by
15 # it under the terms of the GNU General Public License as published by
16 # the Free Software Foundation, either version 3 of the License, or
16 # the Free Software Foundation, either version 3 of the License, or
17 # (at your option) any later version.
17 # (at your option) any later version.
18 #
18 #
19 # This program is distributed in the hope that it will be useful,
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
22 # GNU General Public License for more details.
23 #
23 #
24 # You should have received a copy of the GNU General Public License
24 # You should have received a copy of the GNU General Public License
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26
26
27 import os
27 import os
28 import sys
28 import sys
29 import uuid
29 import uuid
30 import logging
30 import logging
31 from os.path import dirname as dn, join as jn
31 from os.path import dirname as dn, join as jn
32
32
33 from rhodecode import __dbversion__
33 from rhodecode import __dbversion__
34 from rhodecode.model import meta
34 from rhodecode.model import meta
35
35
36 from rhodecode.lib.auth import get_crypt_password, generate_api_key
36 from rhodecode.lib.auth import get_crypt_password, generate_api_key
37 from rhodecode.lib.utils import ask_ok
37 from rhodecode.lib.utils import ask_ok
38 from rhodecode.model import init_model
38 from rhodecode.model import init_model
39 from rhodecode.model.db import User, Permission, RhodeCodeUi, \
39 from rhodecode.model.db import User, Permission, RhodeCodeUi, \
40 RhodeCodeSettings, UserToPerm, DbMigrateVersion
40 RhodeCodeSettings, UserToPerm, DbMigrateVersion
41
41
42 from sqlalchemy.engine import create_engine
42 from sqlalchemy.engine import create_engine
43
43
44 log = logging.getLogger(__name__)
44 log = logging.getLogger(__name__)
45
45
46
46
47 class DbManage(object):
47 class DbManage(object):
48 def __init__(self, log_sql, dbconf, root, tests=False):
48 def __init__(self, log_sql, dbconf, root, tests=False):
49 self.dbname = dbconf.split('/')[-1]
49 self.dbname = dbconf.split('/')[-1]
50 self.tests = tests
50 self.tests = tests
51 self.root = root
51 self.root = root
52 self.dburi = dbconf
52 self.dburi = dbconf
53 self.log_sql = log_sql
53 self.log_sql = log_sql
54 self.db_exists = False
54 self.db_exists = False
55 self.init_db()
55 self.init_db()
56
56
57 def init_db(self):
57 def init_db(self):
58 engine = create_engine(self.dburi, echo=self.log_sql)
58 engine = create_engine(self.dburi, echo=self.log_sql)
59 init_model(engine)
59 init_model(engine)
60 self.sa = meta.Session()
60 self.sa = meta.Session()
61
61
62 def create_tables(self, override=False):
62 def create_tables(self, override=False):
63 """Create a auth database
63 """Create a auth database
64 """
64 """
65
65
66 log.info("Any existing database is going to be destroyed")
66 log.info("Any existing database is going to be destroyed")
67 if self.tests:
67 if self.tests:
68 destroy = True
68 destroy = True
69 else:
69 else:
70 destroy = ask_ok('Are you sure to destroy old database ? [y/n]')
70 destroy = ask_ok('Are you sure to destroy old database ? [y/n]')
71 if not destroy:
71 if not destroy:
72 sys.exit()
72 sys.exit()
73 if destroy:
73 if destroy:
74 meta.Base.metadata.drop_all()
74 meta.Base.metadata.drop_all()
75
75
76 checkfirst = not override
76 checkfirst = not override
77 meta.Base.metadata.create_all(checkfirst=checkfirst)
77 meta.Base.metadata.create_all(checkfirst=checkfirst)
78 log.info('Created tables for %s', self.dbname)
78 log.info('Created tables for %s', self.dbname)
79
79
80 def set_db_version(self):
80 def set_db_version(self):
81 try:
81 try:
82 ver = DbMigrateVersion()
82 ver = DbMigrateVersion()
83 ver.version = __dbversion__
83 ver.version = __dbversion__
84 ver.repository_id = 'rhodecode_db_migrations'
84 ver.repository_id = 'rhodecode_db_migrations'
85 ver.repository_path = 'versions'
85 ver.repository_path = 'versions'
86 self.sa.add(ver)
86 self.sa.add(ver)
87 self.sa.commit()
87 self.sa.commit()
88 except:
88 except:
89 self.sa.rollback()
89 self.sa.rollback()
90 raise
90 raise
91 log.info('db version set to: %s', __dbversion__)
91 log.info('db version set to: %s', __dbversion__)
92
92
93 def upgrade(self):
93 def upgrade(self):
94 """Upgrades given database schema to given revision following
94 """Upgrades given database schema to given revision following
95 all needed steps, to perform the upgrade
95 all needed steps, to perform the upgrade
96
96
97 """
97 """
98
98
99 from rhodecode.lib.dbmigrate.migrate.versioning import api
99 from rhodecode.lib.dbmigrate.migrate.versioning import api
100 from rhodecode.lib.dbmigrate.migrate.exceptions import \
100 from rhodecode.lib.dbmigrate.migrate.exceptions import \
101 DatabaseNotControlledError
101 DatabaseNotControlledError
102
102
103 upgrade = ask_ok('You are about to perform database upgrade, make '
103 upgrade = ask_ok('You are about to perform database upgrade, make '
104 'sure You backed up your database before. '
104 'sure You backed up your database before. '
105 'Continue ? [y/n]')
105 'Continue ? [y/n]')
106 if not upgrade:
106 if not upgrade:
107 sys.exit('Nothing done')
107 sys.exit('Nothing done')
108
108
109 repository_path = jn(dn(dn(dn(os.path.realpath(__file__)))),
109 repository_path = jn(dn(dn(dn(os.path.realpath(__file__)))),
110 'rhodecode/lib/dbmigrate')
110 'rhodecode/lib/dbmigrate')
111 db_uri = self.dburi
111 db_uri = self.dburi
112
112
113 try:
113 try:
114 curr_version = api.db_version(db_uri, repository_path)
114 curr_version = api.db_version(db_uri, repository_path)
115 msg = ('Found current database under version'
115 msg = ('Found current database under version'
116 ' control with version %s' % curr_version)
116 ' control with version %s' % curr_version)
117
117
118 except (RuntimeError, DatabaseNotControlledError):
118 except (RuntimeError, DatabaseNotControlledError):
119 curr_version = 1
119 curr_version = 1
120 msg = ('Current database is not under version control. Setting'
120 msg = ('Current database is not under version control. Setting'
121 ' as version %s' % curr_version)
121 ' as version %s' % curr_version)
122 api.version_control(db_uri, repository_path, curr_version)
122 api.version_control(db_uri, repository_path, curr_version)
123
123
124 print (msg)
124 print (msg)
125
125
126 if curr_version == __dbversion__:
126 if curr_version == __dbversion__:
127 sys.exit('This database is already at the newest version')
127 sys.exit('This database is already at the newest version')
128
128
129 #======================================================================
129 #======================================================================
130 # UPGRADE STEPS
130 # UPGRADE STEPS
131 #======================================================================
131 #======================================================================
132 class UpgradeSteps(object):
132 class UpgradeSteps(object):
133 """Those steps follow schema versions so for example schema
133 """Those steps follow schema versions so for example schema
134 for example schema with seq 002 == step_2 and so on.
134 for example schema with seq 002 == step_2 and so on.
135 """
135 """
136
136
137 def __init__(self, klass):
137 def __init__(self, klass):
138 self.klass = klass
138 self.klass = klass
139
139
140 def step_0(self):
140 def step_0(self):
141 #step 0 is the schema upgrade, and than follow proper upgrades
141 #step 0 is the schema upgrade, and than follow proper upgrades
142 print ('attempting to do database upgrade to version %s' \
142 print ('attempting to do database upgrade to version %s' \
143 % __dbversion__)
143 % __dbversion__)
144 api.upgrade(db_uri, repository_path, __dbversion__)
144 api.upgrade(db_uri, repository_path, __dbversion__)
145 print ('Schema upgrade completed')
145 print ('Schema upgrade completed')
146
146
147 def step_1(self):
147 def step_1(self):
148 pass
148 pass
149
149
150 def step_2(self):
150 def step_2(self):
151 print ('Patching repo paths for newer version of RhodeCode')
151 print ('Patching repo paths for newer version of RhodeCode')
152 self.klass.fix_repo_paths()
152 self.klass.fix_repo_paths()
153
153
154 print ('Patching default user of RhodeCode')
154 print ('Patching default user of RhodeCode')
155 self.klass.fix_default_user()
155 self.klass.fix_default_user()
156
156
157 log.info('Changing ui settings')
157 log.info('Changing ui settings')
158 self.klass.create_ui_settings()
158 self.klass.create_ui_settings()
159
159
160 def step_3(self):
160 def step_3(self):
161 print ('Adding additional settings into RhodeCode db')
161 print ('Adding additional settings into RhodeCode db')
162 self.klass.fix_settings()
162 self.klass.fix_settings()
163
163
164 upgrade_steps = [0] + range(curr_version + 1, __dbversion__ + 1)
164 upgrade_steps = [0] + range(curr_version + 1, __dbversion__ + 1)
165
165
166 #CALL THE PROPER ORDER OF STEPS TO PERFORM FULL UPGRADE
166 #CALL THE PROPER ORDER OF STEPS TO PERFORM FULL UPGRADE
167 for step in upgrade_steps:
167 for step in upgrade_steps:
168 print ('performing upgrade step %s' % step)
168 print ('performing upgrade step %s' % step)
169 callable = getattr(UpgradeSteps(self), 'step_%s' % step)()
169 callable = getattr(UpgradeSteps(self), 'step_%s' % step)()
170
170
171 def fix_repo_paths(self):
171 def fix_repo_paths(self):
172 """Fixes a old rhodecode version path into new one without a '*'
172 """Fixes a old rhodecode version path into new one without a '*'
173 """
173 """
174
174
175 paths = self.sa.query(RhodeCodeUi)\
175 paths = self.sa.query(RhodeCodeUi)\
176 .filter(RhodeCodeUi.ui_key == '/')\
176 .filter(RhodeCodeUi.ui_key == '/')\
177 .scalar()
177 .scalar()
178
178
179 paths.ui_value = paths.ui_value.replace('*', '')
179 paths.ui_value = paths.ui_value.replace('*', '')
180
180
181 try:
181 try:
182 self.sa.add(paths)
182 self.sa.add(paths)
183 self.sa.commit()
183 self.sa.commit()
184 except:
184 except:
185 self.sa.rollback()
185 self.sa.rollback()
186 raise
186 raise
187
187
188 def fix_default_user(self):
188 def fix_default_user(self):
189 """Fixes a old default user with some 'nicer' default values,
189 """Fixes a old default user with some 'nicer' default values,
190 used mostly for anonymous access
190 used mostly for anonymous access
191 """
191 """
192 def_user = self.sa.query(User)\
192 def_user = self.sa.query(User)\
193 .filter(User.username == 'default')\
193 .filter(User.username == 'default')\
194 .one()
194 .one()
195
195
196 def_user.name = 'Anonymous'
196 def_user.name = 'Anonymous'
197 def_user.lastname = 'User'
197 def_user.lastname = 'User'
198 def_user.email = 'anonymous@rhodecode.org'
198 def_user.email = 'anonymous@rhodecode.org'
199
199
200 try:
200 try:
201 self.sa.add(def_user)
201 self.sa.add(def_user)
202 self.sa.commit()
202 self.sa.commit()
203 except:
203 except:
204 self.sa.rollback()
204 self.sa.rollback()
205 raise
205 raise
206
206
207 def fix_settings(self):
207 def fix_settings(self):
208 """Fixes rhodecode settings adds ga_code key for google analytics
208 """Fixes rhodecode settings adds ga_code key for google analytics
209 """
209 """
210
210
211 hgsettings3 = RhodeCodeSettings('ga_code', '')
211 hgsettings3 = RhodeCodeSettings('ga_code', '')
212
212
213 try:
213 try:
214 self.sa.add(hgsettings3)
214 self.sa.add(hgsettings3)
215 self.sa.commit()
215 self.sa.commit()
216 except:
216 except:
217 self.sa.rollback()
217 self.sa.rollback()
218 raise
218 raise
219
219
220 def admin_prompt(self, second=False):
220 def admin_prompt(self, second=False):
221 if not self.tests:
221 if not self.tests:
222 import getpass
222 import getpass
223
223
224 def get_password():
224 def get_password():
225 password = getpass.getpass('Specify admin password '
225 password = getpass.getpass('Specify admin password '
226 '(min 6 chars):')
226 '(min 6 chars):')
227 confirm = getpass.getpass('Confirm password:')
227 confirm = getpass.getpass('Confirm password:')
228
228
229 if password != confirm:
229 if password != confirm:
230 log.error('passwords mismatch')
230 log.error('passwords mismatch')
231 return False
231 return False
232 if len(password) < 6:
232 if len(password) < 6:
233 log.error('password is to short use at least 6 characters')
233 log.error('password is to short use at least 6 characters')
234 return False
234 return False
235
235
236 return password
236 return password
237
237
238 username = raw_input('Specify admin username:')
238 username = raw_input('Specify admin username:')
239
239
240 password = get_password()
240 password = get_password()
241 if not password:
241 if not password:
242 #second try
242 #second try
243 password = get_password()
243 password = get_password()
244 if not password:
244 if not password:
245 sys.exit()
245 sys.exit()
246
246
247 email = raw_input('Specify admin email:')
247 email = raw_input('Specify admin email:')
248 self.create_user(username, password, email, True)
248 self.create_user(username, password, email, True)
249 else:
249 else:
250 log.info('creating admin and regular test users')
250 log.info('creating admin and regular test users')
251 self.create_user('test_admin', 'test12',
251 self.create_user('test_admin', 'test12',
252 'test_admin@mail.com', True)
252 'test_admin@mail.com', True)
253 self.create_user('test_regular', 'test12',
253 self.create_user('test_regular', 'test12',
254 'test_regular@mail.com', False)
254 'test_regular@mail.com', False)
255 self.create_user('test_regular2', 'test12',
255 self.create_user('test_regular2', 'test12',
256 'test_regular2@mail.com', False)
256 'test_regular2@mail.com', False)
257
257
258 def create_ui_settings(self):
258 def create_ui_settings(self):
259 """Creates ui settings, fills out hooks
259 """Creates ui settings, fills out hooks
260 and disables dotencode
260 and disables dotencode
261
261
262 """
262 """
263 #HOOKS
263 #HOOKS
264 hooks1_key = 'changegroup.update'
264 hooks1_key = RhodeCodeUi.HOOK_UPDATE
265 hooks1_ = self.sa.query(RhodeCodeUi)\
265 hooks1_ = self.sa.query(RhodeCodeUi)\
266 .filter(RhodeCodeUi.ui_key == hooks1_key).scalar()
266 .filter(RhodeCodeUi.ui_key == hooks1_key).scalar()
267
267
268 hooks1 = RhodeCodeUi() if hooks1_ is None else hooks1_
268 hooks1 = RhodeCodeUi() if hooks1_ is None else hooks1_
269 hooks1.ui_section = 'hooks'
269 hooks1.ui_section = 'hooks'
270 hooks1.ui_key = hooks1_key
270 hooks1.ui_key = hooks1_key
271 hooks1.ui_value = 'hg update >&2'
271 hooks1.ui_value = 'hg update >&2'
272 hooks1.ui_active = False
272 hooks1.ui_active = False
273
273
274 hooks2_key = 'changegroup.repo_size'
274 hooks2_key = RhodeCodeUi.HOOK_REPO_SIZE
275 hooks2_ = self.sa.query(RhodeCodeUi)\
275 hooks2_ = self.sa.query(RhodeCodeUi)\
276 .filter(RhodeCodeUi.ui_key == hooks2_key).scalar()
276 .filter(RhodeCodeUi.ui_key == hooks2_key).scalar()
277
277
278 hooks2 = RhodeCodeUi() if hooks2_ is None else hooks2_
278 hooks2 = RhodeCodeUi() if hooks2_ is None else hooks2_
279 hooks2.ui_section = 'hooks'
279 hooks2.ui_section = 'hooks'
280 hooks2.ui_key = hooks2_key
280 hooks2.ui_key = hooks2_key
281 hooks2.ui_value = 'python:rhodecode.lib.hooks.repo_size'
281 hooks2.ui_value = 'python:rhodecode.lib.hooks.repo_size'
282
282
283 hooks3 = RhodeCodeUi()
283 hooks3 = RhodeCodeUi()
284 hooks3.ui_section = 'hooks'
284 hooks3.ui_section = 'hooks'
285 hooks3.ui_key = 'pretxnchangegroup.push_logger'
285 hooks3.ui_key = RhodeCodeUi.HOOK_PUSH
286 hooks3.ui_value = 'python:rhodecode.lib.hooks.log_push_action'
286 hooks3.ui_value = 'python:rhodecode.lib.hooks.log_push_action'
287
287
288 hooks4 = RhodeCodeUi()
288 hooks4 = RhodeCodeUi()
289 hooks4.ui_section = 'hooks'
289 hooks4.ui_section = 'hooks'
290 hooks4.ui_key = 'preoutgoing.pull_logger'
290 hooks4.ui_key = RhodeCodeUi.HOOK_PULL
291 hooks4.ui_value = 'python:rhodecode.lib.hooks.log_pull_action'
291 hooks4.ui_value = 'python:rhodecode.lib.hooks.log_pull_action'
292
292
293 #For mercurial 1.7 set backward comapatibility with format
293 #For mercurial 1.7 set backward comapatibility with format
294 dotencode_disable = RhodeCodeUi()
294 dotencode_disable = RhodeCodeUi()
295 dotencode_disable.ui_section = 'format'
295 dotencode_disable.ui_section = 'format'
296 dotencode_disable.ui_key = 'dotencode'
296 dotencode_disable.ui_key = 'dotencode'
297 dotencode_disable.ui_value = 'false'
297 dotencode_disable.ui_value = 'false'
298
298
299 try:
299 try:
300 self.sa.add(hooks1)
300 self.sa.add(hooks1)
301 self.sa.add(hooks2)
301 self.sa.add(hooks2)
302 self.sa.add(hooks3)
302 self.sa.add(hooks3)
303 self.sa.add(hooks4)
303 self.sa.add(hooks4)
304 self.sa.add(dotencode_disable)
304 self.sa.add(dotencode_disable)
305 self.sa.commit()
305 self.sa.commit()
306 except:
306 except:
307 self.sa.rollback()
307 self.sa.rollback()
308 raise
308 raise
309
309
310 def create_ldap_options(self):
310 def create_ldap_options(self):
311 """Creates ldap settings"""
311 """Creates ldap settings"""
312
312
313 try:
313 try:
314 for k, v in [('ldap_active', 'false'), ('ldap_host', ''),
314 for k, v in [('ldap_active', 'false'), ('ldap_host', ''),
315 ('ldap_port', '389'), ('ldap_tls_kind', 'PLAIN'),
315 ('ldap_port', '389'), ('ldap_tls_kind', 'PLAIN'),
316 ('ldap_tls_reqcert', ''), ('ldap_dn_user', ''),
316 ('ldap_tls_reqcert', ''), ('ldap_dn_user', ''),
317 ('ldap_dn_pass', ''), ('ldap_base_dn', ''),
317 ('ldap_dn_pass', ''), ('ldap_base_dn', ''),
318 ('ldap_filter', ''), ('ldap_search_scope', ''),
318 ('ldap_filter', ''), ('ldap_search_scope', ''),
319 ('ldap_attr_login', ''), ('ldap_attr_firstname', ''),
319 ('ldap_attr_login', ''), ('ldap_attr_firstname', ''),
320 ('ldap_attr_lastname', ''), ('ldap_attr_email', '')]:
320 ('ldap_attr_lastname', ''), ('ldap_attr_email', '')]:
321
321
322 setting = RhodeCodeSettings(k, v)
322 setting = RhodeCodeSettings(k, v)
323 self.sa.add(setting)
323 self.sa.add(setting)
324 self.sa.commit()
324 self.sa.commit()
325 except:
325 except:
326 self.sa.rollback()
326 self.sa.rollback()
327 raise
327 raise
328
328
329 def config_prompt(self, test_repo_path='', retries=3):
329 def config_prompt(self, test_repo_path='', retries=3):
330 if retries == 3:
330 if retries == 3:
331 log.info('Setting up repositories config')
331 log.info('Setting up repositories config')
332
332
333 if not self.tests and not test_repo_path:
333 if not self.tests and not test_repo_path:
334 path = raw_input('Specify valid full path to your repositories'
334 path = raw_input('Specify valid full path to your repositories'
335 ' you can change this later in application settings:')
335 ' you can change this later in application settings:')
336 else:
336 else:
337 path = test_repo_path
337 path = test_repo_path
338 path_ok = True
338 path_ok = True
339
339
340 #check proper dir
340 #check proper dir
341 if not os.path.isdir(path):
341 if not os.path.isdir(path):
342 path_ok = False
342 path_ok = False
343 log.error('Given path %s is not a valid directory', path)
343 log.error('Given path %s is not a valid directory', path)
344
344
345 #check write access
345 #check write access
346 if not os.access(path, os.W_OK) and path_ok:
346 if not os.access(path, os.W_OK) and path_ok:
347 path_ok = False
347 path_ok = False
348 log.error('No write permission to given path %s', path)
348 log.error('No write permission to given path %s', path)
349
349
350
350
351 if retries == 0:
351 if retries == 0:
352 sys.exit('max retries reached')
352 sys.exit('max retries reached')
353 if path_ok is False:
353 if path_ok is False:
354 retries -= 1
354 retries -= 1
355 return self.config_prompt(test_repo_path, retries)
355 return self.config_prompt(test_repo_path, retries)
356
356
357 return path
357 return path
358
358
359 def create_settings(self, path):
359 def create_settings(self, path):
360
360
361 self.create_ui_settings()
361 self.create_ui_settings()
362
362
363 #HG UI OPTIONS
363 #HG UI OPTIONS
364 web1 = RhodeCodeUi()
364 web1 = RhodeCodeUi()
365 web1.ui_section = 'web'
365 web1.ui_section = 'web'
366 web1.ui_key = 'push_ssl'
366 web1.ui_key = 'push_ssl'
367 web1.ui_value = 'false'
367 web1.ui_value = 'false'
368
368
369 web2 = RhodeCodeUi()
369 web2 = RhodeCodeUi()
370 web2.ui_section = 'web'
370 web2.ui_section = 'web'
371 web2.ui_key = 'allow_archive'
371 web2.ui_key = 'allow_archive'
372 web2.ui_value = 'gz zip bz2'
372 web2.ui_value = 'gz zip bz2'
373
373
374 web3 = RhodeCodeUi()
374 web3 = RhodeCodeUi()
375 web3.ui_section = 'web'
375 web3.ui_section = 'web'
376 web3.ui_key = 'allow_push'
376 web3.ui_key = 'allow_push'
377 web3.ui_value = '*'
377 web3.ui_value = '*'
378
378
379 web4 = RhodeCodeUi()
379 web4 = RhodeCodeUi()
380 web4.ui_section = 'web'
380 web4.ui_section = 'web'
381 web4.ui_key = 'baseurl'
381 web4.ui_key = 'baseurl'
382 web4.ui_value = '/'
382 web4.ui_value = '/'
383
383
384 paths = RhodeCodeUi()
384 paths = RhodeCodeUi()
385 paths.ui_section = 'paths'
385 paths.ui_section = 'paths'
386 paths.ui_key = '/'
386 paths.ui_key = '/'
387 paths.ui_value = path
387 paths.ui_value = path
388
388
389 hgsettings1 = RhodeCodeSettings('realm', 'RhodeCode authentication')
389 hgsettings1 = RhodeCodeSettings('realm', 'RhodeCode authentication')
390 hgsettings2 = RhodeCodeSettings('title', 'RhodeCode')
390 hgsettings2 = RhodeCodeSettings('title', 'RhodeCode')
391 hgsettings3 = RhodeCodeSettings('ga_code', '')
391 hgsettings3 = RhodeCodeSettings('ga_code', '')
392
392
393 try:
393 try:
394 self.sa.add(web1)
394 self.sa.add(web1)
395 self.sa.add(web2)
395 self.sa.add(web2)
396 self.sa.add(web3)
396 self.sa.add(web3)
397 self.sa.add(web4)
397 self.sa.add(web4)
398 self.sa.add(paths)
398 self.sa.add(paths)
399 self.sa.add(hgsettings1)
399 self.sa.add(hgsettings1)
400 self.sa.add(hgsettings2)
400 self.sa.add(hgsettings2)
401 self.sa.add(hgsettings3)
401 self.sa.add(hgsettings3)
402
402
403 self.sa.commit()
403 self.sa.commit()
404 except:
404 except:
405 self.sa.rollback()
405 self.sa.rollback()
406 raise
406 raise
407
407
408 self.create_ldap_options()
408 self.create_ldap_options()
409
409
410 log.info('created ui config')
410 log.info('created ui config')
411
411
412 def create_user(self, username, password, email='', admin=False):
412 def create_user(self, username, password, email='', admin=False):
413 log.info('creating administrator user %s', username)
413 log.info('creating administrator user %s', username)
414 new_user = User()
414 new_user = User()
415 new_user.username = username
415 new_user.username = username
416 new_user.password = get_crypt_password(password)
416 new_user.password = get_crypt_password(password)
417 new_user.api_key = generate_api_key(username)
417 new_user.api_key = generate_api_key(username)
418 new_user.name = 'RhodeCode'
418 new_user.name = 'RhodeCode'
419 new_user.lastname = 'Admin'
419 new_user.lastname = 'Admin'
420 new_user.email = email
420 new_user.email = email
421 new_user.admin = admin
421 new_user.admin = admin
422 new_user.active = True
422 new_user.active = True
423
423
424 try:
424 try:
425 self.sa.add(new_user)
425 self.sa.add(new_user)
426 self.sa.commit()
426 self.sa.commit()
427 except:
427 except:
428 self.sa.rollback()
428 self.sa.rollback()
429 raise
429 raise
430
430
431 def create_default_user(self):
431 def create_default_user(self):
432 log.info('creating default user')
432 log.info('creating default user')
433 #create default user for handling default permissions.
433 #create default user for handling default permissions.
434 def_user = User()
434 def_user = User()
435 def_user.username = 'default'
435 def_user.username = 'default'
436 def_user.password = get_crypt_password(str(uuid.uuid1())[:8])
436 def_user.password = get_crypt_password(str(uuid.uuid1())[:8])
437 def_user.api_key = generate_api_key('default')
437 def_user.api_key = generate_api_key('default')
438 def_user.name = 'Anonymous'
438 def_user.name = 'Anonymous'
439 def_user.lastname = 'User'
439 def_user.lastname = 'User'
440 def_user.email = 'anonymous@rhodecode.org'
440 def_user.email = 'anonymous@rhodecode.org'
441 def_user.admin = False
441 def_user.admin = False
442 def_user.active = False
442 def_user.active = False
443 try:
443 try:
444 self.sa.add(def_user)
444 self.sa.add(def_user)
445 self.sa.commit()
445 self.sa.commit()
446 except:
446 except:
447 self.sa.rollback()
447 self.sa.rollback()
448 raise
448 raise
449
449
450 def create_permissions(self):
450 def create_permissions(self):
451 #module.(access|create|change|delete)_[name]
451 #module.(access|create|change|delete)_[name]
452 #module.(read|write|owner)
452 #module.(read|write|owner)
453 perms = [('repository.none', 'Repository no access'),
453 perms = [('repository.none', 'Repository no access'),
454 ('repository.read', 'Repository read access'),
454 ('repository.read', 'Repository read access'),
455 ('repository.write', 'Repository write access'),
455 ('repository.write', 'Repository write access'),
456 ('repository.admin', 'Repository admin access'),
456 ('repository.admin', 'Repository admin access'),
457 ('hg.admin', 'Hg Administrator'),
457 ('hg.admin', 'Hg Administrator'),
458 ('hg.create.repository', 'Repository create'),
458 ('hg.create.repository', 'Repository create'),
459 ('hg.create.none', 'Repository creation disabled'),
459 ('hg.create.none', 'Repository creation disabled'),
460 ('hg.register.none', 'Register disabled'),
460 ('hg.register.none', 'Register disabled'),
461 ('hg.register.manual_activate', 'Register new user with '
461 ('hg.register.manual_activate', 'Register new user with '
462 'RhodeCode without manual'
462 'RhodeCode without manual'
463 'activation'),
463 'activation'),
464
464
465 ('hg.register.auto_activate', 'Register new user with '
465 ('hg.register.auto_activate', 'Register new user with '
466 'RhodeCode without auto '
466 'RhodeCode without auto '
467 'activation'),
467 'activation'),
468 ]
468 ]
469
469
470 for p in perms:
470 for p in perms:
471 new_perm = Permission()
471 new_perm = Permission()
472 new_perm.permission_name = p[0]
472 new_perm.permission_name = p[0]
473 new_perm.permission_longname = p[1]
473 new_perm.permission_longname = p[1]
474 try:
474 try:
475 self.sa.add(new_perm)
475 self.sa.add(new_perm)
476 self.sa.commit()
476 self.sa.commit()
477 except:
477 except:
478 self.sa.rollback()
478 self.sa.rollback()
479 raise
479 raise
480
480
481 def populate_default_permissions(self):
481 def populate_default_permissions(self):
482 log.info('creating default user permissions')
482 log.info('creating default user permissions')
483
483
484 default_user = self.sa.query(User)\
484 default_user = self.sa.query(User)\
485 .filter(User.username == 'default').scalar()
485 .filter(User.username == 'default').scalar()
486
486
487 reg_perm = UserToPerm()
487 reg_perm = UserToPerm()
488 reg_perm.user = default_user
488 reg_perm.user = default_user
489 reg_perm.permission = self.sa.query(Permission)\
489 reg_perm.permission = self.sa.query(Permission)\
490 .filter(Permission.permission_name == 'hg.register.manual_activate')\
490 .filter(Permission.permission_name == 'hg.register.manual_activate')\
491 .scalar()
491 .scalar()
492
492
493 create_repo_perm = UserToPerm()
493 create_repo_perm = UserToPerm()
494 create_repo_perm.user = default_user
494 create_repo_perm.user = default_user
495 create_repo_perm.permission = self.sa.query(Permission)\
495 create_repo_perm.permission = self.sa.query(Permission)\
496 .filter(Permission.permission_name == 'hg.create.repository')\
496 .filter(Permission.permission_name == 'hg.create.repository')\
497 .scalar()
497 .scalar()
498
498
499 default_repo_perm = UserToPerm()
499 default_repo_perm = UserToPerm()
500 default_repo_perm.user = default_user
500 default_repo_perm.user = default_user
501 default_repo_perm.permission = self.sa.query(Permission)\
501 default_repo_perm.permission = self.sa.query(Permission)\
502 .filter(Permission.permission_name == 'repository.read')\
502 .filter(Permission.permission_name == 'repository.read')\
503 .scalar()
503 .scalar()
504
504
505 try:
505 try:
506 self.sa.add(reg_perm)
506 self.sa.add(reg_perm)
507 self.sa.add(create_repo_perm)
507 self.sa.add(create_repo_perm)
508 self.sa.add(default_repo_perm)
508 self.sa.add(default_repo_perm)
509 self.sa.commit()
509 self.sa.commit()
510 except:
510 except:
511 self.sa.rollback()
511 self.sa.rollback()
512 raise
512 raise
@@ -1,875 +1,916
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) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 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 from datetime import date
30 from datetime import date
31
31
32 from sqlalchemy import *
32 from sqlalchemy import *
33 from sqlalchemy.exc import DatabaseError
33 from sqlalchemy.exc import DatabaseError
34 from sqlalchemy.orm import relationship, backref, joinedload, class_mapper
34 from sqlalchemy.orm import relationship, backref, joinedload, class_mapper
35 from sqlalchemy.orm.interfaces import MapperExtension
35 from sqlalchemy.orm.interfaces import MapperExtension
36
36
37 from beaker.cache import cache_region, region_invalidate
37 from beaker.cache import cache_region, region_invalidate
38
38
39 from vcs import get_backend
39 from vcs import get_backend
40 from vcs.utils.helpers import get_scm
40 from vcs.utils.helpers import get_scm
41 from vcs.exceptions import RepositoryError, VCSError
41 from vcs.exceptions import RepositoryError, VCSError
42 from vcs.utils.lazy import LazyProperty
42 from vcs.utils.lazy import LazyProperty
43 from vcs.nodes import FileNode
43 from vcs.nodes import FileNode
44
44
45 from rhodecode.lib.exceptions import UsersGroupsAssignedException
45 from rhodecode.lib.exceptions import UsersGroupsAssignedException
46 from rhodecode.lib import str2bool, json, safe_str
46 from rhodecode.lib import str2bool, json, safe_str
47 from rhodecode.model.meta import Base, Session
47 from rhodecode.model.meta import Base, Session
48 from rhodecode.model.caching_query import FromCache
48 from rhodecode.model.caching_query import FromCache
49
49
50 log = logging.getLogger(__name__)
50 log = logging.getLogger(__name__)
51
51
52 #==============================================================================
52 #==============================================================================
53 # BASE CLASSES
53 # BASE CLASSES
54 #==============================================================================
54 #==============================================================================
55
55
56 class ModelSerializer(json.JSONEncoder):
56 class ModelSerializer(json.JSONEncoder):
57 """
57 """
58 Simple Serializer for JSON,
58 Simple Serializer for JSON,
59
59
60 usage::
60 usage::
61
61
62 to make object customized for serialization implement a __json__
62 to make object customized for serialization implement a __json__
63 method that will return a dict for serialization into json
63 method that will return a dict for serialization into json
64
64
65 example::
65 example::
66
66
67 class Task(object):
67 class Task(object):
68
68
69 def __init__(self, name, value):
69 def __init__(self, name, value):
70 self.name = name
70 self.name = name
71 self.value = value
71 self.value = value
72
72
73 def __json__(self):
73 def __json__(self):
74 return dict(name=self.name,
74 return dict(name=self.name,
75 value=self.value)
75 value=self.value)
76
76
77 """
77 """
78
78
79 def default(self, obj):
79 def default(self, obj):
80
80
81 if hasattr(obj, '__json__'):
81 if hasattr(obj, '__json__'):
82 return obj.__json__()
82 return obj.__json__()
83 else:
83 else:
84 return json.JSONEncoder.default(self, obj)
84 return json.JSONEncoder.default(self, obj)
85
85
86 class BaseModel(object):
86 class BaseModel(object):
87 """Base Model for all classess
87 """Base Model for all classess
88
88
89 """
89 """
90
90
91 @classmethod
91 @classmethod
92 def _get_keys(cls):
92 def _get_keys(cls):
93 """return column names for this model """
93 """return column names for this model """
94 return class_mapper(cls).c.keys()
94 return class_mapper(cls).c.keys()
95
95
96 def get_dict(self):
96 def get_dict(self):
97 """return dict with keys and values corresponding
97 """return dict with keys and values corresponding
98 to this model data """
98 to this model data """
99
99
100 d = {}
100 d = {}
101 for k in self._get_keys():
101 for k in self._get_keys():
102 d[k] = getattr(self, k)
102 d[k] = getattr(self, k)
103 return d
103 return d
104
104
105 def get_appstruct(self):
105 def get_appstruct(self):
106 """return list with keys and values tupples corresponding
106 """return list with keys and values tupples corresponding
107 to this model data """
107 to this model data """
108
108
109 l = []
109 l = []
110 for k in self._get_keys():
110 for k in self._get_keys():
111 l.append((k, getattr(self, k),))
111 l.append((k, getattr(self, k),))
112 return l
112 return l
113
113
114 def populate_obj(self, populate_dict):
114 def populate_obj(self, populate_dict):
115 """populate model with data from given populate_dict"""
115 """populate model with data from given populate_dict"""
116
116
117 for k in self._get_keys():
117 for k in self._get_keys():
118 if k in populate_dict:
118 if k in populate_dict:
119 setattr(self, k, populate_dict[k])
119 setattr(self, k, populate_dict[k])
120
120
121 @classmethod
121 @classmethod
122 def query(cls):
122 def query(cls):
123 return Session.query(cls)
123 return Session.query(cls)
124
124
125 @classmethod
125 @classmethod
126 def get(cls, id_):
126 def get(cls, id_):
127 return Session.query(cls).get(id_)
127 return Session.query(cls).get(id_)
128
128
129 @classmethod
130 def delete(cls, id_):
131 obj = Session.query(cls).get(id_)
132 Session.delete(obj)
133 Session.commit()
134
129
135
130 class RhodeCodeSettings(Base, BaseModel):
136 class RhodeCodeSettings(Base, BaseModel):
131 __tablename__ = 'rhodecode_settings'
137 __tablename__ = 'rhodecode_settings'
132 __table_args__ = (UniqueConstraint('app_settings_name'), {'extend_existing':True})
138 __table_args__ = (UniqueConstraint('app_settings_name'), {'extend_existing':True})
133 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
139 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
134 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
140 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
135 app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
141 app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
136
142
137 def __init__(self, k='', v=''):
143 def __init__(self, k='', v=''):
138 self.app_settings_name = k
144 self.app_settings_name = k
139 self.app_settings_value = v
145 self.app_settings_value = v
140
146
141 def __repr__(self):
147 def __repr__(self):
142 return "<%s('%s:%s')>" % (self.__class__.__name__,
148 return "<%s('%s:%s')>" % (self.__class__.__name__,
143 self.app_settings_name, self.app_settings_value)
149 self.app_settings_name, self.app_settings_value)
144
150
145
151
146 @classmethod
152 @classmethod
147 def get_by_name(cls, ldap_key):
153 def get_by_name(cls, ldap_key):
148 return Session.query(cls)\
154 return Session.query(cls)\
149 .filter(cls.app_settings_name == ldap_key).scalar()
155 .filter(cls.app_settings_name == ldap_key).scalar()
150
156
151 @classmethod
157 @classmethod
152 def get_app_settings(cls, cache=False):
158 def get_app_settings(cls, cache=False):
153
159
154 ret = Session.query(cls)
160 ret = Session.query(cls)
155
161
156 if cache:
162 if cache:
157 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
163 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
158
164
159 if not ret:
165 if not ret:
160 raise Exception('Could not get application settings !')
166 raise Exception('Could not get application settings !')
161 settings = {}
167 settings = {}
162 for each in ret:
168 for each in ret:
163 settings['rhodecode_' + each.app_settings_name] = \
169 settings['rhodecode_' + each.app_settings_name] = \
164 each.app_settings_value
170 each.app_settings_value
165
171
166 return settings
172 return settings
167
173
168 @classmethod
174 @classmethod
169 def get_ldap_settings(cls, cache=False):
175 def get_ldap_settings(cls, cache=False):
170 ret = Session.query(cls)\
176 ret = Session.query(cls)\
171 .filter(cls.app_settings_name.startswith('ldap_'))\
177 .filter(cls.app_settings_name.startswith('ldap_'))\
172 .all()
178 .all()
173 fd = {}
179 fd = {}
174 for row in ret:
180 for row in ret:
175 fd.update({row.app_settings_name:row.app_settings_value})
181 fd.update({row.app_settings_name:row.app_settings_value})
176
182
177 fd.update({'ldap_active':str2bool(fd.get('ldap_active'))})
183 fd.update({'ldap_active':str2bool(fd.get('ldap_active'))})
178
184
179 return fd
185 return fd
180
186
181
187
182 class RhodeCodeUi(Base, BaseModel):
188 class RhodeCodeUi(Base, BaseModel):
183 __tablename__ = 'rhodecode_ui'
189 __tablename__ = 'rhodecode_ui'
184 __table_args__ = {'extend_existing':True}
190 __table_args__ = (UniqueConstraint('ui_key'), {'extend_existing':True})
191
192 HOOK_UPDATE = 'changegroup.update'
193 HOOK_REPO_SIZE = 'changegroup.repo_size'
194 HOOK_PUSH = 'pretxnchangegroup.push_logger'
195 HOOK_PULL = 'preoutgoing.pull_logger'
196
185 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
197 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
186 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
198 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
187 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
199 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
188 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
200 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
189 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
201 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
190
202
191
203
192 @classmethod
204 @classmethod
193 def get_by_key(cls, key):
205 def get_by_key(cls, key):
194 return Session.query(cls).filter(cls.ui_key == key)
206 return Session.query(cls).filter(cls.ui_key == key)
195
207
196
208
209 @classmethod
210 def get_builtin_hooks(cls):
211 q = cls.query()
212 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
213 cls.HOOK_REPO_SIZE,
214 cls.HOOK_PUSH, cls.HOOK_PULL]))
215 return q.all()
216
217 @classmethod
218 def get_custom_hooks(cls):
219 q = cls.query()
220 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
221 cls.HOOK_REPO_SIZE,
222 cls.HOOK_PUSH, cls.HOOK_PULL]))
223 q = q.filter(cls.ui_section == 'hooks')
224 return q.all()
225
226 @classmethod
227 def create_or_update_hook(cls, key, val):
228 new_ui = cls.get_by_key(key).scalar() or cls()
229 new_ui.ui_section = 'hooks'
230 new_ui.ui_active = True
231 new_ui.ui_key = key
232 new_ui.ui_value = val
233
234 Session.add(new_ui)
235 Session.commit()
236
237
197 class User(Base, BaseModel):
238 class User(Base, BaseModel):
198 __tablename__ = 'users'
239 __tablename__ = 'users'
199 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'extend_existing':True})
240 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'extend_existing':True})
200 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
241 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
201 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
242 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
202 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
243 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
203 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
244 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
204 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
245 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
205 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
246 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
206 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
247 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
207 email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
248 email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
208 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
249 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
209 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
250 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
210 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
251 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
211
252
212 user_log = relationship('UserLog', cascade='all')
253 user_log = relationship('UserLog', cascade='all')
213 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
254 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
214
255
215 repositories = relationship('Repository')
256 repositories = relationship('Repository')
216 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
257 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
217 repo_to_perm = relationship('RepoToPerm', primaryjoin='RepoToPerm.user_id==User.user_id', cascade='all')
258 repo_to_perm = relationship('RepoToPerm', primaryjoin='RepoToPerm.user_id==User.user_id', cascade='all')
218
259
219 group_member = relationship('UsersGroupMember', cascade='all')
260 group_member = relationship('UsersGroupMember', cascade='all')
220
261
221 @property
262 @property
222 def full_contact(self):
263 def full_contact(self):
223 return '%s %s <%s>' % (self.name, self.lastname, self.email)
264 return '%s %s <%s>' % (self.name, self.lastname, self.email)
224
265
225 @property
266 @property
226 def short_contact(self):
267 def short_contact(self):
227 return '%s %s' % (self.name, self.lastname)
268 return '%s %s' % (self.name, self.lastname)
228
269
229 @property
270 @property
230 def is_admin(self):
271 def is_admin(self):
231 return self.admin
272 return self.admin
232
273
233 def __repr__(self):
274 def __repr__(self):
234 try:
275 try:
235 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
276 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
236 self.user_id, self.username)
277 self.user_id, self.username)
237 except:
278 except:
238 return self.__class__.__name__
279 return self.__class__.__name__
239
280
240 @classmethod
281 @classmethod
241 def by_username(cls, username, case_insensitive=False):
282 def by_username(cls, username, case_insensitive=False):
242 if case_insensitive:
283 if case_insensitive:
243 return Session.query(cls).filter(cls.username.like(username)).one()
284 return Session.query(cls).filter(cls.username.like(username)).one()
244 else:
285 else:
245 return Session.query(cls).filter(cls.username == username).one()
286 return Session.query(cls).filter(cls.username == username).one()
246
287
247 @classmethod
288 @classmethod
248 def get_by_api_key(cls, api_key):
289 def get_by_api_key(cls, api_key):
249 return Session.query(cls).filter(cls.api_key == api_key).one()
290 return Session.query(cls).filter(cls.api_key == api_key).one()
250
291
251
292
252 def update_lastlogin(self):
293 def update_lastlogin(self):
253 """Update user lastlogin"""
294 """Update user lastlogin"""
254
295
255 self.last_login = datetime.datetime.now()
296 self.last_login = datetime.datetime.now()
256 Session.add(self)
297 Session.add(self)
257 Session.commit()
298 Session.commit()
258 log.debug('updated user %s lastlogin', self.username)
299 log.debug('updated user %s lastlogin', self.username)
259
300
260
301
261 class UserLog(Base, BaseModel):
302 class UserLog(Base, BaseModel):
262 __tablename__ = 'user_logs'
303 __tablename__ = 'user_logs'
263 __table_args__ = {'extend_existing':True}
304 __table_args__ = {'extend_existing':True}
264 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
305 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
265 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
306 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
266 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
307 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
267 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
308 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
268 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
309 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
269 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
310 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
270 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
311 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
271
312
272 @property
313 @property
273 def action_as_day(self):
314 def action_as_day(self):
274 return date(*self.action_date.timetuple()[:3])
315 return date(*self.action_date.timetuple()[:3])
275
316
276 user = relationship('User')
317 user = relationship('User')
277 repository = relationship('Repository')
318 repository = relationship('Repository')
278
319
279
320
280 class UsersGroup(Base, BaseModel):
321 class UsersGroup(Base, BaseModel):
281 __tablename__ = 'users_groups'
322 __tablename__ = 'users_groups'
282 __table_args__ = {'extend_existing':True}
323 __table_args__ = {'extend_existing':True}
283
324
284 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
325 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
285 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
326 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
286 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
327 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
287
328
288 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
329 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
289
330
290 def __repr__(self):
331 def __repr__(self):
291 return '<userGroup(%s)>' % (self.users_group_name)
332 return '<userGroup(%s)>' % (self.users_group_name)
292
333
293 @classmethod
334 @classmethod
294 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
335 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
295 if case_insensitive:
336 if case_insensitive:
296 gr = Session.query(cls)\
337 gr = Session.query(cls)\
297 .filter(cls.users_group_name.ilike(group_name))
338 .filter(cls.users_group_name.ilike(group_name))
298 else:
339 else:
299 gr = Session.query(UsersGroup)\
340 gr = Session.query(UsersGroup)\
300 .filter(UsersGroup.users_group_name == group_name)
341 .filter(UsersGroup.users_group_name == group_name)
301 if cache:
342 if cache:
302 gr = gr.options(FromCache("sql_cache_short",
343 gr = gr.options(FromCache("sql_cache_short",
303 "get_user_%s" % group_name))
344 "get_user_%s" % group_name))
304 return gr.scalar()
345 return gr.scalar()
305
346
306
347
307 @classmethod
348 @classmethod
308 def get(cls, users_group_id, cache=False):
349 def get(cls, users_group_id, cache=False):
309 users_group = Session.query(cls)
350 users_group = Session.query(cls)
310 if cache:
351 if cache:
311 users_group = users_group.options(FromCache("sql_cache_short",
352 users_group = users_group.options(FromCache("sql_cache_short",
312 "get_users_group_%s" % users_group_id))
353 "get_users_group_%s" % users_group_id))
313 return users_group.get(users_group_id)
354 return users_group.get(users_group_id)
314
355
315 @classmethod
356 @classmethod
316 def create(cls, form_data):
357 def create(cls, form_data):
317 try:
358 try:
318 new_users_group = cls()
359 new_users_group = cls()
319 for k, v in form_data.items():
360 for k, v in form_data.items():
320 setattr(new_users_group, k, v)
361 setattr(new_users_group, k, v)
321
362
322 Session.add(new_users_group)
363 Session.add(new_users_group)
323 Session.commit()
364 Session.commit()
324 except:
365 except:
325 log.error(traceback.format_exc())
366 log.error(traceback.format_exc())
326 Session.rollback()
367 Session.rollback()
327 raise
368 raise
328
369
329 @classmethod
370 @classmethod
330 def update(cls, users_group_id, form_data):
371 def update(cls, users_group_id, form_data):
331
372
332 try:
373 try:
333 users_group = cls.get(users_group_id, cache=False)
374 users_group = cls.get(users_group_id, cache=False)
334
375
335 for k, v in form_data.items():
376 for k, v in form_data.items():
336 if k == 'users_group_members':
377 if k == 'users_group_members':
337 users_group.members = []
378 users_group.members = []
338 Session.flush()
379 Session.flush()
339 members_list = []
380 members_list = []
340 if v:
381 if v:
341 for u_id in set(v):
382 for u_id in set(v):
342 members_list.append(UsersGroupMember(
383 members_list.append(UsersGroupMember(
343 users_group_id,
384 users_group_id,
344 u_id))
385 u_id))
345 setattr(users_group, 'members', members_list)
386 setattr(users_group, 'members', members_list)
346 setattr(users_group, k, v)
387 setattr(users_group, k, v)
347
388
348 Session.add(users_group)
389 Session.add(users_group)
349 Session.commit()
390 Session.commit()
350 except:
391 except:
351 log.error(traceback.format_exc())
392 log.error(traceback.format_exc())
352 Session.rollback()
393 Session.rollback()
353 raise
394 raise
354
395
355 @classmethod
396 @classmethod
356 def delete(cls, users_group_id):
397 def delete(cls, users_group_id):
357 try:
398 try:
358
399
359 # check if this group is not assigned to repo
400 # check if this group is not assigned to repo
360 assigned_groups = UsersGroupRepoToPerm.query()\
401 assigned_groups = UsersGroupRepoToPerm.query()\
361 .filter(UsersGroupRepoToPerm.users_group_id ==
402 .filter(UsersGroupRepoToPerm.users_group_id ==
362 users_group_id).all()
403 users_group_id).all()
363
404
364 if assigned_groups:
405 if assigned_groups:
365 raise UsersGroupsAssignedException('Group assigned to %s' %
406 raise UsersGroupsAssignedException('Group assigned to %s' %
366 assigned_groups)
407 assigned_groups)
367
408
368 users_group = cls.get(users_group_id, cache=False)
409 users_group = cls.get(users_group_id, cache=False)
369 Session.delete(users_group)
410 Session.delete(users_group)
370 Session.commit()
411 Session.commit()
371 except:
412 except:
372 log.error(traceback.format_exc())
413 log.error(traceback.format_exc())
373 Session.rollback()
414 Session.rollback()
374 raise
415 raise
375
416
376
417
377 class UsersGroupMember(Base, BaseModel):
418 class UsersGroupMember(Base, BaseModel):
378 __tablename__ = 'users_groups_members'
419 __tablename__ = 'users_groups_members'
379 __table_args__ = {'extend_existing':True}
420 __table_args__ = {'extend_existing':True}
380
421
381 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
422 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
382 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
423 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
383 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
424 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
384
425
385 user = relationship('User', lazy='joined')
426 user = relationship('User', lazy='joined')
386 users_group = relationship('UsersGroup')
427 users_group = relationship('UsersGroup')
387
428
388 def __init__(self, gr_id='', u_id=''):
429 def __init__(self, gr_id='', u_id=''):
389 self.users_group_id = gr_id
430 self.users_group_id = gr_id
390 self.user_id = u_id
431 self.user_id = u_id
391
432
392 class Repository(Base, BaseModel):
433 class Repository(Base, BaseModel):
393 __tablename__ = 'repositories'
434 __tablename__ = 'repositories'
394 __table_args__ = (UniqueConstraint('repo_name'), {'extend_existing':True},)
435 __table_args__ = (UniqueConstraint('repo_name'), {'extend_existing':True},)
395
436
396 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
437 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
397 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
438 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
398 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
439 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
399 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
440 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
400 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
441 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
401 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
442 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
402 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
443 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
403 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
444 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
404 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
445 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
405 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
446 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
406
447
407 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
448 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
408 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
449 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
409
450
410
451
411 user = relationship('User')
452 user = relationship('User')
412 fork = relationship('Repository', remote_side=repo_id)
453 fork = relationship('Repository', remote_side=repo_id)
413 group = relationship('Group')
454 group = relationship('Group')
414 repo_to_perm = relationship('RepoToPerm', cascade='all', order_by='RepoToPerm.repo_to_perm_id')
455 repo_to_perm = relationship('RepoToPerm', cascade='all', order_by='RepoToPerm.repo_to_perm_id')
415 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
456 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
416 stats = relationship('Statistics', cascade='all', uselist=False)
457 stats = relationship('Statistics', cascade='all', uselist=False)
417
458
418 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
459 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
419
460
420 logs = relationship('UserLog', cascade='all')
461 logs = relationship('UserLog', cascade='all')
421
462
422 def __repr__(self):
463 def __repr__(self):
423 return "<%s('%s:%s')>" % (self.__class__.__name__,
464 return "<%s('%s:%s')>" % (self.__class__.__name__,
424 self.repo_id, self.repo_name)
465 self.repo_id, self.repo_name)
425
466
426 @classmethod
467 @classmethod
427 def by_repo_name(cls, repo_name):
468 def by_repo_name(cls, repo_name):
428 q = Session.query(cls).filter(cls.repo_name == repo_name)
469 q = Session.query(cls).filter(cls.repo_name == repo_name)
429
470
430 q = q.options(joinedload(Repository.fork))\
471 q = q.options(joinedload(Repository.fork))\
431 .options(joinedload(Repository.user))\
472 .options(joinedload(Repository.user))\
432 .options(joinedload(Repository.group))\
473 .options(joinedload(Repository.group))\
433
474
434 return q.one()
475 return q.one()
435
476
436 @classmethod
477 @classmethod
437 def get_repo_forks(cls, repo_id):
478 def get_repo_forks(cls, repo_id):
438 return Session.query(cls).filter(Repository.fork_id == repo_id)
479 return Session.query(cls).filter(Repository.fork_id == repo_id)
439
480
440 @property
481 @property
441 def just_name(self):
482 def just_name(self):
442 return self.repo_name.split(os.sep)[-1]
483 return self.repo_name.split(os.sep)[-1]
443
484
444 @property
485 @property
445 def groups_with_parents(self):
486 def groups_with_parents(self):
446 groups = []
487 groups = []
447 if self.group is None:
488 if self.group is None:
448 return groups
489 return groups
449
490
450 cur_gr = self.group
491 cur_gr = self.group
451 groups.insert(0, cur_gr)
492 groups.insert(0, cur_gr)
452 while 1:
493 while 1:
453 gr = getattr(cur_gr, 'parent_group', None)
494 gr = getattr(cur_gr, 'parent_group', None)
454 cur_gr = cur_gr.parent_group
495 cur_gr = cur_gr.parent_group
455 if gr is None:
496 if gr is None:
456 break
497 break
457 groups.insert(0, gr)
498 groups.insert(0, gr)
458
499
459 return groups
500 return groups
460
501
461 @property
502 @property
462 def groups_and_repo(self):
503 def groups_and_repo(self):
463 return self.groups_with_parents, self.just_name
504 return self.groups_with_parents, self.just_name
464
505
465 @LazyProperty
506 @LazyProperty
466 def repo_path(self):
507 def repo_path(self):
467 """
508 """
468 Returns base full path for that repository means where it actually
509 Returns base full path for that repository means where it actually
469 exists on a filesystem
510 exists on a filesystem
470 """
511 """
471 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/')
512 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/')
472 q.options(FromCache("sql_cache_short", "repository_repo_path"))
513 q.options(FromCache("sql_cache_short", "repository_repo_path"))
473 return q.one().ui_value
514 return q.one().ui_value
474
515
475 @property
516 @property
476 def repo_full_path(self):
517 def repo_full_path(self):
477 p = [self.repo_path]
518 p = [self.repo_path]
478 # we need to split the name by / since this is how we store the
519 # we need to split the name by / since this is how we store the
479 # names in the database, but that eventually needs to be converted
520 # names in the database, but that eventually needs to be converted
480 # into a valid system path
521 # into a valid system path
481 p += self.repo_name.split('/')
522 p += self.repo_name.split('/')
482 return os.path.join(*p)
523 return os.path.join(*p)
483
524
484 @property
525 @property
485 def _ui(self):
526 def _ui(self):
486 """
527 """
487 Creates an db based ui object for this repository
528 Creates an db based ui object for this repository
488 """
529 """
489 from mercurial import ui
530 from mercurial import ui
490 from mercurial import config
531 from mercurial import config
491 baseui = ui.ui()
532 baseui = ui.ui()
492
533
493 #clean the baseui object
534 #clean the baseui object
494 baseui._ocfg = config.config()
535 baseui._ocfg = config.config()
495 baseui._ucfg = config.config()
536 baseui._ucfg = config.config()
496 baseui._tcfg = config.config()
537 baseui._tcfg = config.config()
497
538
498
539
499 ret = Session.query(RhodeCodeUi)\
540 ret = Session.query(RhodeCodeUi)\
500 .options(FromCache("sql_cache_short",
541 .options(FromCache("sql_cache_short",
501 "repository_repo_ui")).all()
542 "repository_repo_ui")).all()
502
543
503 hg_ui = ret
544 hg_ui = ret
504 for ui_ in hg_ui:
545 for ui_ in hg_ui:
505 if ui_.ui_active:
546 if ui_.ui_active:
506 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
547 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
507 ui_.ui_key, ui_.ui_value)
548 ui_.ui_key, ui_.ui_value)
508 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
549 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
509
550
510 return baseui
551 return baseui
511
552
512 #==========================================================================
553 #==========================================================================
513 # SCM CACHE INSTANCE
554 # SCM CACHE INSTANCE
514 #==========================================================================
555 #==========================================================================
515
556
516 @property
557 @property
517 def invalidate(self):
558 def invalidate(self):
518 """
559 """
519 Returns Invalidation object if this repo should be invalidated
560 Returns Invalidation object if this repo should be invalidated
520 None otherwise. `cache_active = False` means that this cache
561 None otherwise. `cache_active = False` means that this cache
521 state is not valid and needs to be invalidated
562 state is not valid and needs to be invalidated
522 """
563 """
523 return Session.query(CacheInvalidation)\
564 return Session.query(CacheInvalidation)\
524 .filter(CacheInvalidation.cache_key == self.repo_name)\
565 .filter(CacheInvalidation.cache_key == self.repo_name)\
525 .filter(CacheInvalidation.cache_active == False)\
566 .filter(CacheInvalidation.cache_active == False)\
526 .scalar()
567 .scalar()
527
568
528 def set_invalidate(self):
569 def set_invalidate(self):
529 """
570 """
530 set a cache for invalidation for this instance
571 set a cache for invalidation for this instance
531 """
572 """
532 inv = Session.query(CacheInvalidation)\
573 inv = Session.query(CacheInvalidation)\
533 .filter(CacheInvalidation.cache_key == self.repo_name)\
574 .filter(CacheInvalidation.cache_key == self.repo_name)\
534 .scalar()
575 .scalar()
535
576
536 if inv is None:
577 if inv is None:
537 inv = CacheInvalidation(self.repo_name)
578 inv = CacheInvalidation(self.repo_name)
538 inv.cache_active = True
579 inv.cache_active = True
539 Session.add(inv)
580 Session.add(inv)
540 Session.commit()
581 Session.commit()
541
582
542 @property
583 @property
543 def scm_instance(self):
584 def scm_instance(self):
544 return self.__get_instance()
585 return self.__get_instance()
545
586
546 @property
587 @property
547 def scm_instance_cached(self):
588 def scm_instance_cached(self):
548 @cache_region('long_term')
589 @cache_region('long_term')
549 def _c(repo_name):
590 def _c(repo_name):
550 return self.__get_instance()
591 return self.__get_instance()
551
592
552 # TODO: remove this trick when beaker 1.6 is released
593 # TODO: remove this trick when beaker 1.6 is released
553 # and have fixed this issue with not supporting unicode keys
594 # and have fixed this issue with not supporting unicode keys
554 rn = safe_str(self.repo_name)
595 rn = safe_str(self.repo_name)
555
596
556 inv = self.invalidate
597 inv = self.invalidate
557 if inv is not None:
598 if inv is not None:
558 region_invalidate(_c, None, rn)
599 region_invalidate(_c, None, rn)
559 # update our cache
600 # update our cache
560 inv.cache_active = True
601 inv.cache_active = True
561 Session.add(inv)
602 Session.add(inv)
562 Session.commit()
603 Session.commit()
563
604
564 return _c(rn)
605 return _c(rn)
565
606
566 def __get_instance(self):
607 def __get_instance(self):
567
608
568 repo_full_path = self.repo_full_path
609 repo_full_path = self.repo_full_path
569
610
570 try:
611 try:
571 alias = get_scm(repo_full_path)[0]
612 alias = get_scm(repo_full_path)[0]
572 log.debug('Creating instance of %s repository', alias)
613 log.debug('Creating instance of %s repository', alias)
573 backend = get_backend(alias)
614 backend = get_backend(alias)
574 except VCSError:
615 except VCSError:
575 log.error(traceback.format_exc())
616 log.error(traceback.format_exc())
576 log.error('Perhaps this repository is in db and not in '
617 log.error('Perhaps this repository is in db and not in '
577 'filesystem run rescan repositories with '
618 'filesystem run rescan repositories with '
578 '"destroy old data " option from admin panel')
619 '"destroy old data " option from admin panel')
579 return
620 return
580
621
581 if alias == 'hg':
622 if alias == 'hg':
582
623
583 repo = backend(safe_str(repo_full_path), create=False,
624 repo = backend(safe_str(repo_full_path), create=False,
584 baseui=self._ui)
625 baseui=self._ui)
585 #skip hidden web repository
626 #skip hidden web repository
586 if repo._get_hidden():
627 if repo._get_hidden():
587 return
628 return
588 else:
629 else:
589 repo = backend(repo_full_path, create=False)
630 repo = backend(repo_full_path, create=False)
590
631
591 return repo
632 return repo
592
633
593
634
594 class Group(Base, BaseModel):
635 class Group(Base, BaseModel):
595 __tablename__ = 'groups'
636 __tablename__ = 'groups'
596 __table_args__ = (UniqueConstraint('group_name', 'group_parent_id'),
637 __table_args__ = (UniqueConstraint('group_name', 'group_parent_id'),
597 CheckConstraint('group_id != group_parent_id'), {'extend_existing':True},)
638 CheckConstraint('group_id != group_parent_id'), {'extend_existing':True},)
598 __mapper_args__ = {'order_by':'group_name'}
639 __mapper_args__ = {'order_by':'group_name'}
599
640
600 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
641 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
601 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
642 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
602 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
643 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
603 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
644 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
604
645
605 parent_group = relationship('Group', remote_side=group_id)
646 parent_group = relationship('Group', remote_side=group_id)
606
647
607
648
608 def __init__(self, group_name='', parent_group=None):
649 def __init__(self, group_name='', parent_group=None):
609 self.group_name = group_name
650 self.group_name = group_name
610 self.parent_group = parent_group
651 self.parent_group = parent_group
611
652
612 def __repr__(self):
653 def __repr__(self):
613 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
654 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
614 self.group_name)
655 self.group_name)
615
656
616 @classmethod
657 @classmethod
617 def url_sep(cls):
658 def url_sep(cls):
618 return '/'
659 return '/'
619
660
620 @property
661 @property
621 def parents(self):
662 def parents(self):
622 parents_recursion_limit = 5
663 parents_recursion_limit = 5
623 groups = []
664 groups = []
624 if self.parent_group is None:
665 if self.parent_group is None:
625 return groups
666 return groups
626 cur_gr = self.parent_group
667 cur_gr = self.parent_group
627 groups.insert(0, cur_gr)
668 groups.insert(0, cur_gr)
628 cnt = 0
669 cnt = 0
629 while 1:
670 while 1:
630 cnt += 1
671 cnt += 1
631 gr = getattr(cur_gr, 'parent_group', None)
672 gr = getattr(cur_gr, 'parent_group', None)
632 cur_gr = cur_gr.parent_group
673 cur_gr = cur_gr.parent_group
633 if gr is None:
674 if gr is None:
634 break
675 break
635 if cnt == parents_recursion_limit:
676 if cnt == parents_recursion_limit:
636 # this will prevent accidental infinit loops
677 # this will prevent accidental infinit loops
637 log.error('group nested more than %s' %
678 log.error('group nested more than %s' %
638 parents_recursion_limit)
679 parents_recursion_limit)
639 break
680 break
640
681
641 groups.insert(0, gr)
682 groups.insert(0, gr)
642 return groups
683 return groups
643
684
644 @property
685 @property
645 def children(self):
686 def children(self):
646 return Session.query(Group).filter(Group.parent_group == self)
687 return Session.query(Group).filter(Group.parent_group == self)
647
688
648 @property
689 @property
649 def full_path(self):
690 def full_path(self):
650 return Group.url_sep().join([g.group_name for g in self.parents] +
691 return Group.url_sep().join([g.group_name for g in self.parents] +
651 [self.group_name])
692 [self.group_name])
652
693
653 @property
694 @property
654 def repositories(self):
695 def repositories(self):
655 return Session.query(Repository).filter(Repository.group == self)
696 return Session.query(Repository).filter(Repository.group == self)
656
697
657 @property
698 @property
658 def repositories_recursive_count(self):
699 def repositories_recursive_count(self):
659 cnt = self.repositories.count()
700 cnt = self.repositories.count()
660
701
661 def children_count(group):
702 def children_count(group):
662 cnt = 0
703 cnt = 0
663 for child in group.children:
704 for child in group.children:
664 cnt += child.repositories.count()
705 cnt += child.repositories.count()
665 cnt += children_count(child)
706 cnt += children_count(child)
666 return cnt
707 return cnt
667
708
668 return cnt + children_count(self)
709 return cnt + children_count(self)
669
710
670 class Permission(Base, BaseModel):
711 class Permission(Base, BaseModel):
671 __tablename__ = 'permissions'
712 __tablename__ = 'permissions'
672 __table_args__ = {'extend_existing':True}
713 __table_args__ = {'extend_existing':True}
673 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
714 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
674 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
715 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
675 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
716 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
676
717
677 def __repr__(self):
718 def __repr__(self):
678 return "<%s('%s:%s')>" % (self.__class__.__name__,
719 return "<%s('%s:%s')>" % (self.__class__.__name__,
679 self.permission_id, self.permission_name)
720 self.permission_id, self.permission_name)
680
721
681 @classmethod
722 @classmethod
682 def get_by_key(cls, key):
723 def get_by_key(cls, key):
683 return Session.query(cls).filter(cls.permission_name == key).scalar()
724 return Session.query(cls).filter(cls.permission_name == key).scalar()
684
725
685 class RepoToPerm(Base, BaseModel):
726 class RepoToPerm(Base, BaseModel):
686 __tablename__ = 'repo_to_perm'
727 __tablename__ = 'repo_to_perm'
687 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'extend_existing':True})
728 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'extend_existing':True})
688 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
729 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
689 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
730 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
690 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
731 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
691 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
732 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
692
733
693 user = relationship('User')
734 user = relationship('User')
694 permission = relationship('Permission')
735 permission = relationship('Permission')
695 repository = relationship('Repository')
736 repository = relationship('Repository')
696
737
697 class UserToPerm(Base, BaseModel):
738 class UserToPerm(Base, BaseModel):
698 __tablename__ = 'user_to_perm'
739 __tablename__ = 'user_to_perm'
699 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'extend_existing':True})
740 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'extend_existing':True})
700 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
741 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
701 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
742 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
702 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
743 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
703
744
704 user = relationship('User')
745 user = relationship('User')
705 permission = relationship('Permission')
746 permission = relationship('Permission')
706
747
707 @classmethod
748 @classmethod
708 def has_perm(cls, user_id, perm):
749 def has_perm(cls, user_id, perm):
709 if not isinstance(perm, Permission):
750 if not isinstance(perm, Permission):
710 raise Exception('perm needs to be an instance of Permission class')
751 raise Exception('perm needs to be an instance of Permission class')
711
752
712 return Session.query(cls).filter(cls.user_id == user_id)\
753 return Session.query(cls).filter(cls.user_id == user_id)\
713 .filter(cls.permission == perm).scalar() is not None
754 .filter(cls.permission == perm).scalar() is not None
714
755
715 @classmethod
756 @classmethod
716 def grant_perm(cls, user_id, perm):
757 def grant_perm(cls, user_id, perm):
717 if not isinstance(perm, Permission):
758 if not isinstance(perm, Permission):
718 raise Exception('perm needs to be an instance of Permission class')
759 raise Exception('perm needs to be an instance of Permission class')
719
760
720 new = cls()
761 new = cls()
721 new.user_id = user_id
762 new.user_id = user_id
722 new.permission = perm
763 new.permission = perm
723 try:
764 try:
724 Session.add(new)
765 Session.add(new)
725 Session.commit()
766 Session.commit()
726 except:
767 except:
727 Session.rollback()
768 Session.rollback()
728
769
729
770
730 @classmethod
771 @classmethod
731 def revoke_perm(cls, user_id, perm):
772 def revoke_perm(cls, user_id, perm):
732 if not isinstance(perm, Permission):
773 if not isinstance(perm, Permission):
733 raise Exception('perm needs to be an instance of Permission class')
774 raise Exception('perm needs to be an instance of Permission class')
734
775
735 try:
776 try:
736 Session.query(cls).filter(cls.user_id == user_id)\
777 Session.query(cls).filter(cls.user_id == user_id)\
737 .filter(cls.permission == perm).delete()
778 .filter(cls.permission == perm).delete()
738 Session.commit()
779 Session.commit()
739 except:
780 except:
740 Session.rollback()
781 Session.rollback()
741
782
742 class UsersGroupRepoToPerm(Base, BaseModel):
783 class UsersGroupRepoToPerm(Base, BaseModel):
743 __tablename__ = 'users_group_repo_to_perm'
784 __tablename__ = 'users_group_repo_to_perm'
744 __table_args__ = (UniqueConstraint('repository_id', 'users_group_id', 'permission_id'), {'extend_existing':True})
785 __table_args__ = (UniqueConstraint('repository_id', 'users_group_id', 'permission_id'), {'extend_existing':True})
745 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
786 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
746 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
787 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
747 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
788 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
748 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
789 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
749
790
750 users_group = relationship('UsersGroup')
791 users_group = relationship('UsersGroup')
751 permission = relationship('Permission')
792 permission = relationship('Permission')
752 repository = relationship('Repository')
793 repository = relationship('Repository')
753
794
754 def __repr__(self):
795 def __repr__(self):
755 return '<userGroup:%s => %s >' % (self.users_group, self.repository)
796 return '<userGroup:%s => %s >' % (self.users_group, self.repository)
756
797
757 class UsersGroupToPerm(Base, BaseModel):
798 class UsersGroupToPerm(Base, BaseModel):
758 __tablename__ = 'users_group_to_perm'
799 __tablename__ = 'users_group_to_perm'
759 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
800 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
760 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
801 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
761 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
802 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
762
803
763 users_group = relationship('UsersGroup')
804 users_group = relationship('UsersGroup')
764 permission = relationship('Permission')
805 permission = relationship('Permission')
765
806
766
807
767 @classmethod
808 @classmethod
768 def has_perm(cls, users_group_id, perm):
809 def has_perm(cls, users_group_id, perm):
769 if not isinstance(perm, Permission):
810 if not isinstance(perm, Permission):
770 raise Exception('perm needs to be an instance of Permission class')
811 raise Exception('perm needs to be an instance of Permission class')
771
812
772 return Session.query(cls).filter(cls.users_group_id ==
813 return Session.query(cls).filter(cls.users_group_id ==
773 users_group_id)\
814 users_group_id)\
774 .filter(cls.permission == perm)\
815 .filter(cls.permission == perm)\
775 .scalar() is not None
816 .scalar() is not None
776
817
777 @classmethod
818 @classmethod
778 def grant_perm(cls, users_group_id, perm):
819 def grant_perm(cls, users_group_id, perm):
779 if not isinstance(perm, Permission):
820 if not isinstance(perm, Permission):
780 raise Exception('perm needs to be an instance of Permission class')
821 raise Exception('perm needs to be an instance of Permission class')
781
822
782 new = cls()
823 new = cls()
783 new.users_group_id = users_group_id
824 new.users_group_id = users_group_id
784 new.permission = perm
825 new.permission = perm
785 try:
826 try:
786 Session.add(new)
827 Session.add(new)
787 Session.commit()
828 Session.commit()
788 except:
829 except:
789 Session.rollback()
830 Session.rollback()
790
831
791
832
792 @classmethod
833 @classmethod
793 def revoke_perm(cls, users_group_id, perm):
834 def revoke_perm(cls, users_group_id, perm):
794 if not isinstance(perm, Permission):
835 if not isinstance(perm, Permission):
795 raise Exception('perm needs to be an instance of Permission class')
836 raise Exception('perm needs to be an instance of Permission class')
796
837
797 try:
838 try:
798 Session.query(cls).filter(cls.users_group_id == users_group_id)\
839 Session.query(cls).filter(cls.users_group_id == users_group_id)\
799 .filter(cls.permission == perm).delete()
840 .filter(cls.permission == perm).delete()
800 Session.commit()
841 Session.commit()
801 except:
842 except:
802 Session.rollback()
843 Session.rollback()
803
844
804
845
805 class GroupToPerm(Base, BaseModel):
846 class GroupToPerm(Base, BaseModel):
806 __tablename__ = 'group_to_perm'
847 __tablename__ = 'group_to_perm'
807 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
848 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
808
849
809 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
850 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
810 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
851 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
811 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
852 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
812 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
853 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
813
854
814 user = relationship('User')
855 user = relationship('User')
815 permission = relationship('Permission')
856 permission = relationship('Permission')
816 group = relationship('Group')
857 group = relationship('Group')
817
858
818 class Statistics(Base, BaseModel):
859 class Statistics(Base, BaseModel):
819 __tablename__ = 'statistics'
860 __tablename__ = 'statistics'
820 __table_args__ = (UniqueConstraint('repository_id'), {'extend_existing':True})
861 __table_args__ = (UniqueConstraint('repository_id'), {'extend_existing':True})
821 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
862 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
822 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
863 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
823 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
864 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
824 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
865 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
825 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
866 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
826 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
867 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
827
868
828 repository = relationship('Repository', single_parent=True)
869 repository = relationship('Repository', single_parent=True)
829
870
830 class UserFollowing(Base, BaseModel):
871 class UserFollowing(Base, BaseModel):
831 __tablename__ = 'user_followings'
872 __tablename__ = 'user_followings'
832 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
873 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
833 UniqueConstraint('user_id', 'follows_user_id')
874 UniqueConstraint('user_id', 'follows_user_id')
834 , {'extend_existing':True})
875 , {'extend_existing':True})
835
876
836 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
877 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
837 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
878 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
838 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
879 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
839 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
880 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
840 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
881 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
841
882
842 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
883 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
843
884
844 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
885 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
845 follows_repository = relationship('Repository', order_by='Repository.repo_name')
886 follows_repository = relationship('Repository', order_by='Repository.repo_name')
846
887
847
888
848 @classmethod
889 @classmethod
849 def get_repo_followers(cls, repo_id):
890 def get_repo_followers(cls, repo_id):
850 return Session.query(cls).filter(cls.follows_repo_id == repo_id)
891 return Session.query(cls).filter(cls.follows_repo_id == repo_id)
851
892
852 class CacheInvalidation(Base, BaseModel):
893 class CacheInvalidation(Base, BaseModel):
853 __tablename__ = 'cache_invalidation'
894 __tablename__ = 'cache_invalidation'
854 __table_args__ = (UniqueConstraint('cache_key'), {'extend_existing':True})
895 __table_args__ = (UniqueConstraint('cache_key'), {'extend_existing':True})
855 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
896 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
856 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
897 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
857 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
898 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
858 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
899 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
859
900
860
901
861 def __init__(self, cache_key, cache_args=''):
902 def __init__(self, cache_key, cache_args=''):
862 self.cache_key = cache_key
903 self.cache_key = cache_key
863 self.cache_args = cache_args
904 self.cache_args = cache_args
864 self.cache_active = False
905 self.cache_active = False
865
906
866 def __repr__(self):
907 def __repr__(self):
867 return "<%s('%s:%s')>" % (self.__class__.__name__,
908 return "<%s('%s:%s')>" % (self.__class__.__name__,
868 self.cache_id, self.cache_key)
909 self.cache_id, self.cache_key)
869
910
870 class DbMigrateVersion(Base, BaseModel):
911 class DbMigrateVersion(Base, BaseModel):
871 __tablename__ = 'db_migrate_version'
912 __tablename__ = 'db_migrate_version'
872 __table_args__ = {'extend_existing':True}
913 __table_args__ = {'extend_existing':True}
873 repository_id = Column('repository_id', String(250), primary_key=True)
914 repository_id = Column('repository_id', String(250), primary_key=True)
874 repository_path = Column('repository_path', Text)
915 repository_path = Column('repository_path', Text)
875 version = Column('version', Integer)
916 version = Column('version', Integer)
@@ -1,189 +1,192
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Settings administration')} - ${c.rhodecode_name}
5 ${_('Settings administration')} - ${c.rhodecode_name}
6 </%def>
6 </%def>
7
7
8 <%def name="breadcrumbs_links()">
8 <%def name="breadcrumbs_links()">
9 ${h.link_to(_('Admin'),h.url('admin_home'))} &raquo; ${_('Settings')}
9 ${h.link_to(_('Admin'),h.url('admin_home'))} &raquo; ${_('Settings')}
10 </%def>
10 </%def>
11
11
12 <%def name="page_nav()">
12 <%def name="page_nav()">
13 ${self.menu('admin')}
13 ${self.menu('admin')}
14 </%def>
14 </%def>
15
15
16 <%def name="main()">
16 <%def name="main()">
17 <div class="box">
17 <div class="box">
18 <!-- box / title -->
18 <!-- box / title -->
19 <div class="title">
19 <div class="title">
20 ${self.breadcrumbs()}
20 ${self.breadcrumbs()}
21 </div>
21 </div>
22 <!-- end box / title -->
22 <!-- end box / title -->
23
23
24 <h3>${_('Remap and rescan repositories')}</h3>
24 <h3>${_('Remap and rescan repositories')}</h3>
25 ${h.form(url('admin_setting', setting_id='mapping'),method='put')}
25 ${h.form(url('admin_setting', setting_id='mapping'),method='put')}
26 <div class="form">
26 <div class="form">
27 <!-- fields -->
27 <!-- fields -->
28
28
29 <div class="fields">
29 <div class="fields">
30 <div class="field">
30 <div class="field">
31 <div class="label label-checkbox">
31 <div class="label label-checkbox">
32 <label for="destroy">${_('rescan option')}:</label>
32 <label for="destroy">${_('rescan option')}:</label>
33 </div>
33 </div>
34 <div class="checkboxes">
34 <div class="checkboxes">
35 <div class="checkbox">
35 <div class="checkbox">
36 ${h.checkbox('destroy',True)}
36 ${h.checkbox('destroy',True)}
37 <label for="checkbox-1">
37 <label for="checkbox-1">
38 <span class="tooltip" title="${h.tooltip(_('In case a repository was deleted from filesystem and there are leftovers in the database check this option to scan obsolete data in database and remove it.'))}">
38 <span class="tooltip" title="${h.tooltip(_('In case a repository was deleted from filesystem and there are leftovers in the database check this option to scan obsolete data in database and remove it.'))}">
39 ${_('destroy old data')}</span> </label>
39 ${_('destroy old data')}</span> </label>
40 </div>
40 </div>
41 </div>
41 </div>
42 </div>
42 </div>
43
43
44 <div class="buttons">
44 <div class="buttons">
45 ${h.submit('rescan','Rescan repositories',class_="ui-button")}
45 ${h.submit('rescan','Rescan repositories',class_="ui-button")}
46 </div>
46 </div>
47 </div>
47 </div>
48 </div>
48 </div>
49 ${h.end_form()}
49 ${h.end_form()}
50
50
51 <h3>${_('Whoosh indexing')}</h3>
51 <h3>${_('Whoosh indexing')}</h3>
52 ${h.form(url('admin_setting', setting_id='whoosh'),method='put')}
52 ${h.form(url('admin_setting', setting_id='whoosh'),method='put')}
53 <div class="form">
53 <div class="form">
54 <!-- fields -->
54 <!-- fields -->
55
55
56 <div class="fields">
56 <div class="fields">
57 <div class="field">
57 <div class="field">
58 <div class="label label-checkbox">
58 <div class="label label-checkbox">
59 <label for="destroy">${_('index build option')}:</label>
59 <label for="destroy">${_('index build option')}:</label>
60 </div>
60 </div>
61 <div class="checkboxes">
61 <div class="checkboxes">
62 <div class="checkbox">
62 <div class="checkbox">
63 ${h.checkbox('full_index',True)}
63 ${h.checkbox('full_index',True)}
64 <label for="checkbox-1">${_('build from scratch')}</label>
64 <label for="checkbox-1">${_('build from scratch')}</label>
65 </div>
65 </div>
66 </div>
66 </div>
67 </div>
67 </div>
68
68
69 <div class="buttons">
69 <div class="buttons">
70 ${h.submit('reindex','Reindex',class_="ui-button")}
70 ${h.submit('reindex','Reindex',class_="ui-button")}
71 </div>
71 </div>
72 </div>
72 </div>
73 </div>
73 </div>
74 ${h.end_form()}
74 ${h.end_form()}
75
75
76 <h3>${_('Global application settings')}</h3>
76 <h3>${_('Global application settings')}</h3>
77 ${h.form(url('admin_setting', setting_id='global'),method='put')}
77 ${h.form(url('admin_setting', setting_id='global'),method='put')}
78 <div class="form">
78 <div class="form">
79 <!-- fields -->
79 <!-- fields -->
80
80
81 <div class="fields">
81 <div class="fields">
82
82
83 <div class="field">
83 <div class="field">
84 <div class="label">
84 <div class="label">
85 <label for="rhodecode_title">${_('Application name')}:</label>
85 <label for="rhodecode_title">${_('Application name')}:</label>
86 </div>
86 </div>
87 <div class="input">
87 <div class="input">
88 ${h.text('rhodecode_title',size=30)}
88 ${h.text('rhodecode_title',size=30)}
89 </div>
89 </div>
90 </div>
90 </div>
91
91
92 <div class="field">
92 <div class="field">
93 <div class="label">
93 <div class="label">
94 <label for="rhodecode_realm">${_('Realm text')}:</label>
94 <label for="rhodecode_realm">${_('Realm text')}:</label>
95 </div>
95 </div>
96 <div class="input">
96 <div class="input">
97 ${h.text('rhodecode_realm',size=30)}
97 ${h.text('rhodecode_realm',size=30)}
98 </div>
98 </div>
99 </div>
99 </div>
100
100
101 <div class="field">
101 <div class="field">
102 <div class="label">
102 <div class="label">
103 <label for="ga_code">${_('GA code')}:</label>
103 <label for="ga_code">${_('GA code')}:</label>
104 </div>
104 </div>
105 <div class="input">
105 <div class="input">
106 ${h.text('rhodecode_ga_code',size=30)}
106 ${h.text('rhodecode_ga_code',size=30)}
107 </div>
107 </div>
108 </div>
108 </div>
109
109
110 <div class="buttons">
110 <div class="buttons">
111 ${h.submit('save','Save settings',class_="ui-button")}
111 ${h.submit('save','Save settings',class_="ui-button")}
112 ${h.reset('reset','Reset',class_="ui-button")}
112 ${h.reset('reset','Reset',class_="ui-button")}
113 </div>
113 </div>
114 </div>
114 </div>
115 </div>
115 </div>
116 ${h.end_form()}
116 ${h.end_form()}
117
117
118 <h3>${_('Mercurial settings')}</h3>
118 <h3>${_('Mercurial settings')}</h3>
119 ${h.form(url('admin_setting', setting_id='mercurial'),method='put')}
119 ${h.form(url('admin_setting', setting_id='mercurial'),method='put')}
120 <div class="form">
120 <div class="form">
121 <!-- fields -->
121 <!-- fields -->
122
122
123 <div class="fields">
123 <div class="fields">
124
124
125 <div class="field">
125 <div class="field">
126 <div class="label label-checkbox">
126 <div class="label label-checkbox">
127 <label for="web_push_ssl">${_('Web')}:</label>
127 <label for="web_push_ssl">${_('Web')}:</label>
128 </div>
128 </div>
129 <div class="checkboxes">
129 <div class="checkboxes">
130 <div class="checkbox">
130 <div class="checkbox">
131 ${h.checkbox('web_push_ssl','true')}
131 ${h.checkbox('web_push_ssl','true')}
132 <label for="web_push_ssl">${_('require ssl for pushing')}</label>
132 <label for="web_push_ssl">${_('require ssl for pushing')}</label>
133 </div>
133 </div>
134 </div>
134 </div>
135 </div>
135 </div>
136
136
137 <div class="field">
137 <div class="field">
138 <div class="label label-checkbox">
138 <div class="label label-checkbox">
139 <label for="web_push_ssl">${_('Hooks')}:</label>
139 <label for="web_push_ssl">${_('Hooks')}:</label>
140 </div>
140 </div>
141 <div class="input">
142 ${h.link_to('advanced setup',url('admin_edit_setting',setting_id='hooks'))}
143 </div>
141 <div class="checkboxes">
144 <div class="checkboxes">
142 <div class="checkbox">
145 <div class="checkbox">
143 ${h.checkbox('hooks_changegroup_update','True')}
146 ${h.checkbox('hooks_changegroup_update','True')}
144 <label for="hooks_changegroup_update">${_('Update repository after push (hg update)')}</label>
147 <label for="hooks_changegroup_update">${_('Update repository after push (hg update)')}</label>
145 </div>
148 </div>
146 <div class="checkbox">
149 <div class="checkbox">
147 ${h.checkbox('hooks_changegroup_repo_size','True')}
150 ${h.checkbox('hooks_changegroup_repo_size','True')}
148 <label for="hooks_changegroup_repo_size">${_('Show repository size after push')}</label>
151 <label for="hooks_changegroup_repo_size">${_('Show repository size after push')}</label>
149 </div>
152 </div>
150 <div class="checkbox">
153 <div class="checkbox">
151 ${h.checkbox('hooks_pretxnchangegroup_push_logger','True')}
154 ${h.checkbox('hooks_pretxnchangegroup_push_logger','True')}
152 <label for="hooks_pretxnchangegroup_push_logger">${_('Log user push commands')}</label>
155 <label for="hooks_pretxnchangegroup_push_logger">${_('Log user push commands')}</label>
153 </div>
156 </div>
154 <div class="checkbox">
157 <div class="checkbox">
155 ${h.checkbox('hooks_preoutgoing_pull_logger','True')}
158 ${h.checkbox('hooks_preoutgoing_pull_logger','True')}
156 <label for="hooks_preoutgoing_pull_logger">${_('Log user pull commands')}</label>
159 <label for="hooks_preoutgoing_pull_logger">${_('Log user pull commands')}</label>
157 </div>
160 </div>
158 </div>
161 </div>
159 </div>
162 </div>
160
163
161 <div class="field">
164 <div class="field">
162 <div class="label">
165 <div class="label">
163 <label for="paths_root_path">${_('Repositories location')}:</label>
166 <label for="paths_root_path">${_('Repositories location')}:</label>
164 </div>
167 </div>
165 <div class="input">
168 <div class="input">
166 ${h.text('paths_root_path',size=30,readonly="readonly")}
169 ${h.text('paths_root_path',size=30,readonly="readonly")}
167 <span id="path_unlock" class="tooltip"
170 <span id="path_unlock" class="tooltip"
168 title="${h.tooltip(_('This a crucial application setting. If You really sure you need to change this, you must restart application in order to make this settings take effect. Click this label to unlock.'))}">
171 title="${h.tooltip(_('This a crucial application setting. If You really sure you need to change this, you must restart application in order to make this settings take effect. Click this label to unlock.'))}">
169 ${_('unlock')}</span>
172 ${_('unlock')}</span>
170 </div>
173 </div>
171 </div>
174 </div>
172
175
173 <div class="buttons">
176 <div class="buttons">
174 ${h.submit('save','Save settings',class_="ui-button")}
177 ${h.submit('save','Save settings',class_="ui-button")}
175 ${h.reset('reset','Reset',class_="ui-button")}
178 ${h.reset('reset','Reset',class_="ui-button")}
176 </div>
179 </div>
177 </div>
180 </div>
178 </div>
181 </div>
179 ${h.end_form()}
182 ${h.end_form()}
180
183
181 <script type="text/javascript">
184 <script type="text/javascript">
182 YAHOO.util.Event.onDOMReady(function(){
185 YAHOO.util.Event.onDOMReady(function(){
183 YAHOO.util.Event.addListener('path_unlock','click',function(){
186 YAHOO.util.Event.addListener('path_unlock','click',function(){
184 YAHOO.util.Dom.get('paths_root_path').removeAttribute('readonly');
187 YAHOO.util.Dom.get('paths_root_path').removeAttribute('readonly');
185 });
188 });
186 });
189 });
187 </script>
190 </script>
188 </div>
191 </div>
189 </%def>
192 </%def>
General Comments 0
You need to be logged in to leave comments. Login now