##// END OF EJS Templates
Added hooks managment into application settings
marcink -
r395:e8af467b default
parent child Browse files
Show More
@@ -1,265 +1,280 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # settings controller for pylons
3 # settings controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on July 14, 2010
21 Created on July 14, 2010
22 settings controller for pylons
22 settings controller for pylons
23 @author: marcink
23 @author: marcink
24 """
24 """
25 from formencode import htmlfill
25 from formencode import htmlfill
26 from pylons import request, session, tmpl_context as c, url, app_globals as g, \
26 from pylons import request, session, tmpl_context as c, url, app_globals as g, \
27 config
27 config
28 from pylons.controllers.util import abort, redirect
28 from pylons.controllers.util import abort, redirect
29 from pylons.i18n.translation import _
29 from pylons.i18n.translation import _
30 from pylons_app.lib import helpers as h
30 from pylons_app.lib import helpers as h
31 from pylons_app.lib.auth import LoginRequired, HasPermissionAllDecorator, \
31 from pylons_app.lib.auth import LoginRequired, HasPermissionAllDecorator, \
32 HasPermissionAnyDecorator
32 HasPermissionAnyDecorator
33 from pylons_app.lib.base import BaseController, render
33 from pylons_app.lib.base import BaseController, render
34 from pylons_app.lib.utils import repo2db_mapper, invalidate_cache, \
34 from pylons_app.lib.utils import repo2db_mapper, invalidate_cache, \
35 set_hg_app_config, get_hg_settings, get_hg_ui_settings, make_ui
35 set_hg_app_config, get_hg_settings, get_hg_ui_settings, make_ui
36 from pylons_app.model.db import User, UserLog, HgAppSettings, HgAppUi
36 from pylons_app.model.db import User, UserLog, HgAppSettings, HgAppUi
37 from pylons_app.model.forms import UserForm, ApplicationSettingsForm, \
37 from pylons_app.model.forms import UserForm, ApplicationSettingsForm, \
38 ApplicationUiSettingsForm
38 ApplicationUiSettingsForm
39 from pylons_app.model.hg_model import HgModel
39 from pylons_app.model.hg_model import HgModel
40 from pylons_app.model.user_model import UserModel
40 from pylons_app.model.user_model import UserModel
41 import formencode
41 import formencode
42 import logging
42 import logging
43 import traceback
43 import traceback
44
44
45 log = logging.getLogger(__name__)
45 log = logging.getLogger(__name__)
46
46
47
47
48 class SettingsController(BaseController):
48 class SettingsController(BaseController):
49 """REST Controller styled on the Atom Publishing Protocol"""
49 """REST Controller styled on the Atom Publishing Protocol"""
50 # To properly map this controller, ensure your config/routing.py
50 # To properly map this controller, ensure your config/routing.py
51 # file has a resource setup:
51 # file has a resource setup:
52 # map.resource('setting', 'settings', controller='admin/settings',
52 # map.resource('setting', 'settings', controller='admin/settings',
53 # path_prefix='/admin', name_prefix='admin_')
53 # path_prefix='/admin', name_prefix='admin_')
54
54
55
55
56 @LoginRequired()
56 @LoginRequired()
57 def __before__(self):
57 def __before__(self):
58 c.admin_user = session.get('admin_user')
58 c.admin_user = session.get('admin_user')
59 c.admin_username = session.get('admin_username')
59 c.admin_username = session.get('admin_username')
60 super(SettingsController, self).__before__()
60 super(SettingsController, self).__before__()
61
61
62
62
63 @HasPermissionAllDecorator('hg.admin')
63 @HasPermissionAllDecorator('hg.admin')
64 def index(self, format='html'):
64 def index(self, format='html'):
65 """GET /admin/settings: All items in the collection"""
65 """GET /admin/settings: All items in the collection"""
66 # url('admin_settings')
66 # url('admin_settings')
67
67
68 defaults = get_hg_settings()
68 defaults = get_hg_settings()
69 defaults.update(get_hg_ui_settings())
69 defaults.update(get_hg_ui_settings())
70 return htmlfill.render(
70 return htmlfill.render(
71 render('admin/settings/settings.html'),
71 render('admin/settings/settings.html'),
72 defaults=defaults,
72 defaults=defaults,
73 encoding="UTF-8",
73 encoding="UTF-8",
74 force_defaults=False
74 force_defaults=False
75 )
75 )
76
76
77 @HasPermissionAllDecorator('hg.admin')
77 @HasPermissionAllDecorator('hg.admin')
78 def create(self):
78 def create(self):
79 """POST /admin/settings: Create a new item"""
79 """POST /admin/settings: Create a new item"""
80 # url('admin_settings')
80 # url('admin_settings')
81
81
82 @HasPermissionAllDecorator('hg.admin')
82 @HasPermissionAllDecorator('hg.admin')
83 def new(self, format='html'):
83 def new(self, format='html'):
84 """GET /admin/settings/new: Form to create a new item"""
84 """GET /admin/settings/new: Form to create a new item"""
85 # url('admin_new_setting')
85 # url('admin_new_setting')
86
86
87 @HasPermissionAllDecorator('hg.admin')
87 @HasPermissionAllDecorator('hg.admin')
88 def update(self, setting_id):
88 def update(self, setting_id):
89 """PUT /admin/settings/setting_id: Update an existing item"""
89 """PUT /admin/settings/setting_id: Update an existing item"""
90 # Forms posted to this method should contain a hidden field:
90 # Forms posted to this method should contain a hidden field:
91 # <input type="hidden" name="_method" value="PUT" />
91 # <input type="hidden" name="_method" value="PUT" />
92 # Or using helpers:
92 # Or using helpers:
93 # h.form(url('admin_setting', setting_id=ID),
93 # h.form(url('admin_setting', setting_id=ID),
94 # method='put')
94 # method='put')
95 # url('admin_setting', setting_id=ID)
95 # url('admin_setting', setting_id=ID)
96 if setting_id == 'mapping':
96 if setting_id == 'mapping':
97 rm_obsolete = request.POST.get('destroy', False)
97 rm_obsolete = request.POST.get('destroy', False)
98 log.debug('Rescanning directories with destroy=%s', rm_obsolete)
98 log.debug('Rescanning directories with destroy=%s', rm_obsolete)
99
99
100 initial = HgModel.repo_scan(g.paths[0][0], g.paths[0][1], g.baseui)
100 initial = HgModel.repo_scan(g.paths[0][0], g.paths[0][1], g.baseui)
101 repo2db_mapper(initial, rm_obsolete)
101 repo2db_mapper(initial, rm_obsolete)
102 invalidate_cache('cached_repo_list')
102 invalidate_cache('cached_repo_list')
103 h.flash(_('Repositories sucessfully rescanned'), category='success')
103 h.flash(_('Repositories sucessfully rescanned'), category='success')
104
104
105 if setting_id == 'global':
105 if setting_id == 'global':
106
106
107 application_form = ApplicationSettingsForm()()
107 application_form = ApplicationSettingsForm()()
108 try:
108 try:
109 form_result = application_form.to_python(dict(request.POST))
109 form_result = application_form.to_python(dict(request.POST))
110
110
111 try:
111 try:
112 hgsettings1 = self.sa.query(HgAppSettings)\
112 hgsettings1 = self.sa.query(HgAppSettings)\
113 .filter(HgAppSettings.app_settings_name == 'title').one()
113 .filter(HgAppSettings.app_settings_name == 'title').one()
114 hgsettings1.app_settings_value = form_result['hg_app_title']
114 hgsettings1.app_settings_value = form_result['hg_app_title']
115
115
116 hgsettings2 = self.sa.query(HgAppSettings)\
116 hgsettings2 = self.sa.query(HgAppSettings)\
117 .filter(HgAppSettings.app_settings_name == 'realm').one()
117 .filter(HgAppSettings.app_settings_name == 'realm').one()
118 hgsettings2.app_settings_value = form_result['hg_app_realm']
118 hgsettings2.app_settings_value = form_result['hg_app_realm']
119
119
120
120
121 self.sa.add(hgsettings1)
121 self.sa.add(hgsettings1)
122 self.sa.add(hgsettings2)
122 self.sa.add(hgsettings2)
123 self.sa.commit()
123 self.sa.commit()
124 set_hg_app_config(config)
124 set_hg_app_config(config)
125 h.flash(_('Updated application settings'),
125 h.flash(_('Updated application settings'),
126 category='success')
126 category='success')
127
127
128 except:
128 except:
129 log.error(traceback.format_exc())
129 log.error(traceback.format_exc())
130 h.flash(_('error occured during updating application settings'),
130 h.flash(_('error occured during updating application settings'),
131 category='error')
131 category='error')
132
132
133 self.sa.rollback()
133 self.sa.rollback()
134
134
135
135
136 except formencode.Invalid as errors:
136 except formencode.Invalid as errors:
137 return htmlfill.render(
137 return htmlfill.render(
138 render('admin/settings/settings.html'),
138 render('admin/settings/settings.html'),
139 defaults=errors.value,
139 defaults=errors.value,
140 errors=errors.error_dict or {},
140 errors=errors.error_dict or {},
141 prefix_error=False,
141 prefix_error=False,
142 encoding="UTF-8")
142 encoding="UTF-8")
143
143
144 if setting_id == 'mercurial':
144 if setting_id == 'mercurial':
145 application_form = ApplicationUiSettingsForm()()
145 application_form = ApplicationUiSettingsForm()()
146 try:
146 try:
147 form_result = application_form.to_python(dict(request.POST))
147 form_result = application_form.to_python(dict(request.POST))
148
148
149 try:
149 try:
150
150
151 hgsettings1 = self.sa.query(HgAppUi)\
151 hgsettings1 = self.sa.query(HgAppUi)\
152 .filter(HgAppUi.ui_key == 'push_ssl').one()
152 .filter(HgAppUi.ui_key == 'push_ssl').one()
153 hgsettings1.ui_value = form_result['web_push_ssl']
153 hgsettings1.ui_value = form_result['web_push_ssl']
154
154
155 hgsettings2 = self.sa.query(HgAppUi)\
155 hgsettings2 = self.sa.query(HgAppUi)\
156 .filter(HgAppUi.ui_key == '/').one()
156 .filter(HgAppUi.ui_key == '/').one()
157 hgsettings2.ui_value = form_result['paths_root_path']
157 hgsettings2.ui_value = form_result['paths_root_path']
158
158
159
160 #HOOKS
161 hgsettings3 = self.sa.query(HgAppUi)\
162 .filter(HgAppUi.ui_key == 'changegroup.update').one()
163 hgsettings3.ui_active = bool(form_result['hooks_changegroup_update'])
164
165 hgsettings4 = self.sa.query(HgAppUi)\
166 .filter(HgAppUi.ui_key == 'changegroup.repo_size').one()
167 hgsettings4.ui_active = bool(form_result['hooks_changegroup_repo_size'])
168
169
170
171
159 self.sa.add(hgsettings1)
172 self.sa.add(hgsettings1)
160 self.sa.add(hgsettings2)
173 self.sa.add(hgsettings2)
174 self.sa.add(hgsettings3)
175 self.sa.add(hgsettings4)
161 self.sa.commit()
176 self.sa.commit()
162
177
163 h.flash(_('Updated application settings'),
178 h.flash(_('Updated application settings'),
164 category='success')
179 category='success')
165
180
166 except:
181 except:
167 log.error(traceback.format_exc())
182 log.error(traceback.format_exc())
168 h.flash(_('error occured during updating application settings'),
183 h.flash(_('error occured during updating application settings'),
169 category='error')
184 category='error')
170
185
171 self.sa.rollback()
186 self.sa.rollback()
172
187
173
188
174 except formencode.Invalid as errors:
189 except formencode.Invalid as errors:
175 return htmlfill.render(
190 return htmlfill.render(
176 render('admin/settings/settings.html'),
191 render('admin/settings/settings.html'),
177 defaults=errors.value,
192 defaults=errors.value,
178 errors=errors.error_dict or {},
193 errors=errors.error_dict or {},
179 prefix_error=False,
194 prefix_error=False,
180 encoding="UTF-8")
195 encoding="UTF-8")
181
196
182
197
183
198
184 return redirect(url('admin_settings'))
199 return redirect(url('admin_settings'))
185
200
186 @HasPermissionAllDecorator('hg.admin')
201 @HasPermissionAllDecorator('hg.admin')
187 def delete(self, setting_id):
202 def delete(self, setting_id):
188 """DELETE /admin/settings/setting_id: Delete an existing item"""
203 """DELETE /admin/settings/setting_id: Delete an existing item"""
189 # Forms posted to this method should contain a hidden field:
204 # Forms posted to this method should contain a hidden field:
190 # <input type="hidden" name="_method" value="DELETE" />
205 # <input type="hidden" name="_method" value="DELETE" />
191 # Or using helpers:
206 # Or using helpers:
192 # h.form(url('admin_setting', setting_id=ID),
207 # h.form(url('admin_setting', setting_id=ID),
193 # method='delete')
208 # method='delete')
194 # url('admin_setting', setting_id=ID)
209 # url('admin_setting', setting_id=ID)
195
210
196 @HasPermissionAllDecorator('hg.admin')
211 @HasPermissionAllDecorator('hg.admin')
197 def show(self, setting_id, format='html'):
212 def show(self, setting_id, format='html'):
198 """GET /admin/settings/setting_id: Show a specific item"""
213 """GET /admin/settings/setting_id: Show a specific item"""
199 # url('admin_setting', setting_id=ID)
214 # url('admin_setting', setting_id=ID)
200
215
201 @HasPermissionAllDecorator('hg.admin')
216 @HasPermissionAllDecorator('hg.admin')
202 def edit(self, setting_id, format='html'):
217 def edit(self, setting_id, format='html'):
203 """GET /admin/settings/setting_id/edit: Form to edit an existing item"""
218 """GET /admin/settings/setting_id/edit: Form to edit an existing item"""
204 # url('admin_edit_setting', setting_id=ID)
219 # url('admin_edit_setting', setting_id=ID)
205
220
206
221
207 def my_account(self):
222 def my_account(self):
208 """
223 """
209 GET /_admin/my_account Displays info about my account
224 GET /_admin/my_account Displays info about my account
210 """
225 """
211 # url('admin_settings_my_account')
226 # url('admin_settings_my_account')
212 c.user = self.sa.query(User).get(c.hg_app_user.user_id)
227 c.user = self.sa.query(User).get(c.hg_app_user.user_id)
213 if c.user.username == 'default':
228 if c.user.username == 'default':
214 h.flash(_("You can't edit this user since it's"
229 h.flash(_("You can't edit this user since it's"
215 " crucial for entire application"), category='warning')
230 " crucial for entire application"), category='warning')
216 return redirect(url('users'))
231 return redirect(url('users'))
217
232
218 defaults = c.user.__dict__
233 defaults = c.user.__dict__
219 return htmlfill.render(
234 return htmlfill.render(
220 render('admin/users/user_edit_my_account.html'),
235 render('admin/users/user_edit_my_account.html'),
221 defaults=defaults,
236 defaults=defaults,
222 encoding="UTF-8",
237 encoding="UTF-8",
223 force_defaults=False
238 force_defaults=False
224 )
239 )
225
240
226 def my_account_update(self):
241 def my_account_update(self):
227 """PUT /_admin/my_account_update: Update an existing item"""
242 """PUT /_admin/my_account_update: Update an existing item"""
228 # Forms posted to this method should contain a hidden field:
243 # Forms posted to this method should contain a hidden field:
229 # <input type="hidden" name="_method" value="PUT" />
244 # <input type="hidden" name="_method" value="PUT" />
230 # Or using helpers:
245 # Or using helpers:
231 # h.form(url('admin_settings_my_account_update'),
246 # h.form(url('admin_settings_my_account_update'),
232 # method='put')
247 # method='put')
233 # url('admin_settings_my_account_update', id=ID)
248 # url('admin_settings_my_account_update', id=ID)
234 user_model = UserModel()
249 user_model = UserModel()
235 uid = c.hg_app_user.user_id
250 uid = c.hg_app_user.user_id
236 _form = UserForm(edit=True, old_data={'user_id':uid})()
251 _form = UserForm(edit=True, old_data={'user_id':uid})()
237 form_result = {}
252 form_result = {}
238 try:
253 try:
239 form_result = _form.to_python(dict(request.POST))
254 form_result = _form.to_python(dict(request.POST))
240 user_model.update_my_account(uid, form_result)
255 user_model.update_my_account(uid, form_result)
241 h.flash(_('Your account was updated succesfully'), category='success')
256 h.flash(_('Your account was updated succesfully'), category='success')
242
257
243 except formencode.Invalid as errors:
258 except formencode.Invalid as errors:
244 #c.user = self.sa.query(User).get(c.hg_app_user.user_id)
259 #c.user = self.sa.query(User).get(c.hg_app_user.user_id)
245 return htmlfill.render(
260 return htmlfill.render(
246 render('admin/users/user_edit_my_account.html'),
261 render('admin/users/user_edit_my_account.html'),
247 defaults=errors.value,
262 defaults=errors.value,
248 errors=errors.error_dict or {},
263 errors=errors.error_dict or {},
249 prefix_error=False,
264 prefix_error=False,
250 encoding="UTF-8")
265 encoding="UTF-8")
251 except Exception:
266 except Exception:
252 log.error(traceback.format_exc())
267 log.error(traceback.format_exc())
253 h.flash(_('error occured during update of user %s') \
268 h.flash(_('error occured during update of user %s') \
254 % form_result.get('username'), category='error')
269 % form_result.get('username'), category='error')
255
270
256 return redirect(url('my_account'))
271 return redirect(url('my_account'))
257
272
258 @HasPermissionAnyDecorator('repository.create', 'hg.admin')
273 @HasPermissionAnyDecorator('repository.create', 'hg.admin')
259 def create_repository(self):
274 def create_repository(self):
260 """GET /_admin/create_repository: Form to create a new item"""
275 """GET /_admin/create_repository: Form to create a new item"""
261 new_repo = request.GET.get('repo', '')
276 new_repo = request.GET.get('repo', '')
262 c.new_repo = h.repo_name_slug(new_repo)
277 c.new_repo = h.repo_name_slug(new_repo)
263
278
264 return render('admin/repos/repo_add_create_repository.html')
279 return render('admin/repos/repo_add_create_repository.html')
265
280
@@ -1,354 +1,364 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # Utilities for hg app
3 # Utilities for hg app
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 # This program is free software; you can redistribute it and/or
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; version 2
7 # as published by the Free Software Foundation; version 2
8 # of the License or (at your opinion) any later version of the license.
8 # of the License or (at your opinion) any later version of the license.
9 #
9 #
10 # This program is distributed in the hope that it will be useful,
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
13 # GNU General Public License for more details.
14 #
14 #
15 # You should have received a copy of the GNU General Public License
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # MA 02110-1301, USA.
18 # MA 02110-1301, USA.
19
19
20 """
20 """
21 Created on April 18, 2010
21 Created on April 18, 2010
22 Utilities for hg app
22 Utilities for hg app
23 @author: marcink
23 @author: marcink
24 """
24 """
25 from beaker.cache import cache_region
25 from beaker.cache import cache_region
26 from mercurial import ui, config, hg
26 from mercurial import ui, config, hg
27 from mercurial.error import RepoError
27 from mercurial.error import RepoError
28 from pylons_app.model import meta
28 from pylons_app.model import meta
29 from pylons_app.model.db import Repository, User, HgAppUi, HgAppSettings
29 from pylons_app.model.db import Repository, User, HgAppUi, HgAppSettings
30 from vcs.backends.base import BaseChangeset
30 from vcs.backends.base import BaseChangeset
31 from vcs.utils.lazy import LazyProperty
31 from vcs.utils.lazy import LazyProperty
32 import logging
32 import logging
33 import os
33 import os
34 log = logging.getLogger(__name__)
34 log = logging.getLogger(__name__)
35
35
36
36
37 def get_repo_slug(request):
37 def get_repo_slug(request):
38 return request.environ['pylons.routes_dict'].get('repo_name')
38 return request.environ['pylons.routes_dict'].get('repo_name')
39
39
40 def is_mercurial(environ):
40 def is_mercurial(environ):
41 """
41 """
42 Returns True if request's target is mercurial server - header
42 Returns True if request's target is mercurial server - header
43 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
43 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
44 """
44 """
45 http_accept = environ.get('HTTP_ACCEPT')
45 http_accept = environ.get('HTTP_ACCEPT')
46 if http_accept and http_accept.startswith('application/mercurial'):
46 if http_accept and http_accept.startswith('application/mercurial'):
47 return True
47 return True
48 return False
48 return False
49
49
50 def check_repo_dir(paths):
50 def check_repo_dir(paths):
51 repos_path = paths[0][1].split('/')
51 repos_path = paths[0][1].split('/')
52 if repos_path[-1] in ['*', '**']:
52 if repos_path[-1] in ['*', '**']:
53 repos_path = repos_path[:-1]
53 repos_path = repos_path[:-1]
54 if repos_path[0] != '/':
54 if repos_path[0] != '/':
55 repos_path[0] = '/'
55 repos_path[0] = '/'
56 if not os.path.isdir(os.path.join(*repos_path)):
56 if not os.path.isdir(os.path.join(*repos_path)):
57 raise Exception('Not a valid repository in %s' % paths[0][1])
57 raise Exception('Not a valid repository in %s' % paths[0][1])
58
58
59 def check_repo_fast(repo_name, base_path):
59 def check_repo_fast(repo_name, base_path):
60 if os.path.isdir(os.path.join(base_path, repo_name)):return False
60 if os.path.isdir(os.path.join(base_path, repo_name)):return False
61 return True
61 return True
62
62
63 def check_repo(repo_name, base_path, verify=True):
63 def check_repo(repo_name, base_path, verify=True):
64
64
65 repo_path = os.path.join(base_path, repo_name)
65 repo_path = os.path.join(base_path, repo_name)
66
66
67 try:
67 try:
68 if not check_repo_fast(repo_name, base_path):
68 if not check_repo_fast(repo_name, base_path):
69 return False
69 return False
70 r = hg.repository(ui.ui(), repo_path)
70 r = hg.repository(ui.ui(), repo_path)
71 if verify:
71 if verify:
72 hg.verify(r)
72 hg.verify(r)
73 #here we hnow that repo exists it was verified
73 #here we hnow that repo exists it was verified
74 log.info('%s repo is already created', repo_name)
74 log.info('%s repo is already created', repo_name)
75 return False
75 return False
76 except RepoError:
76 except RepoError:
77 #it means that there is no valid repo there...
77 #it means that there is no valid repo there...
78 log.info('%s repo is free for creation', repo_name)
78 log.info('%s repo is free for creation', repo_name)
79 return True
79 return True
80
80
81 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
81 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
82 while True:
82 while True:
83 ok = raw_input(prompt)
83 ok = raw_input(prompt)
84 if ok in ('y', 'ye', 'yes'): return True
84 if ok in ('y', 'ye', 'yes'): return True
85 if ok in ('n', 'no', 'nop', 'nope'): return False
85 if ok in ('n', 'no', 'nop', 'nope'): return False
86 retries = retries - 1
86 retries = retries - 1
87 if retries < 0: raise IOError
87 if retries < 0: raise IOError
88 print complaint
88 print complaint
89
89
90 @cache_region('super_short_term', 'cached_hg_ui')
90 @cache_region('super_short_term', 'cached_hg_ui')
91 def get_hg_ui_cached():
91 def get_hg_ui_cached():
92 try:
92 try:
93 sa = meta.Session
93 sa = meta.Session
94 ret = sa.query(HgAppUi).all()
94 ret = sa.query(HgAppUi).all()
95 finally:
95 finally:
96 meta.Session.remove()
96 meta.Session.remove()
97 return ret
97 return ret
98
98
99
99
100 def get_hg_settings():
100 def get_hg_settings():
101 try:
101 try:
102 sa = meta.Session
102 sa = meta.Session
103 ret = sa.query(HgAppSettings).all()
103 ret = sa.query(HgAppSettings).all()
104 finally:
104 finally:
105 meta.Session.remove()
105 meta.Session.remove()
106
106
107 if not ret:
107 if not ret:
108 raise Exception('Could not get application settings !')
108 raise Exception('Could not get application settings !')
109 settings = {}
109 settings = {}
110 for each in ret:
110 for each in ret:
111 settings['hg_app_' + each.app_settings_name] = each.app_settings_value
111 settings['hg_app_' + each.app_settings_name] = each.app_settings_value
112
112
113 return settings
113 return settings
114
114
115 def get_hg_ui_settings():
115 def get_hg_ui_settings():
116 try:
116 try:
117 sa = meta.Session
117 sa = meta.Session
118 ret = sa.query(HgAppUi).all()
118 ret = sa.query(HgAppUi).all()
119 finally:
119 finally:
120 meta.Session.remove()
120 meta.Session.remove()
121
121
122 if not ret:
122 if not ret:
123 raise Exception('Could not get application ui settings !')
123 raise Exception('Could not get application ui settings !')
124 settings = {}
124 settings = {}
125 for each in ret:
125 for each in ret:
126 k = each.ui_key if each.ui_key != '/' else 'root_path'
126 k = each.ui_key
127 settings[each.ui_section + '_' + k] = each.ui_value
127 v = each.ui_value
128 if k == '/':
129 k = 'root_path'
130
131 if k.find('.') != -1:
132 k = k.replace('.', '_')
133
134 if each.ui_section == 'hooks':
135 v = each.ui_active
136
137 settings[each.ui_section + '_' + k] = v
128
138
129 return settings
139 return settings
130
140
131 #propagated from mercurial documentation
141 #propagated from mercurial documentation
132 ui_sections = ['alias', 'auth',
142 ui_sections = ['alias', 'auth',
133 'decode/encode', 'defaults',
143 'decode/encode', 'defaults',
134 'diff', 'email',
144 'diff', 'email',
135 'extensions', 'format',
145 'extensions', 'format',
136 'merge-patterns', 'merge-tools',
146 'merge-patterns', 'merge-tools',
137 'hooks', 'http_proxy',
147 'hooks', 'http_proxy',
138 'smtp', 'patch',
148 'smtp', 'patch',
139 'paths', 'profiling',
149 'paths', 'profiling',
140 'server', 'trusted',
150 'server', 'trusted',
141 'ui', 'web', ]
151 'ui', 'web', ]
142
152
143 def make_ui(read_from='file', path=None, checkpaths=True):
153 def make_ui(read_from='file', path=None, checkpaths=True):
144 """
154 """
145 A function that will read python rc files or database
155 A function that will read python rc files or database
146 and make an mercurial ui object from read options
156 and make an mercurial ui object from read options
147
157
148 @param path: path to mercurial config file
158 @param path: path to mercurial config file
149 @param checkpaths: check the path
159 @param checkpaths: check the path
150 @param read_from: read from 'file' or 'db'
160 @param read_from: read from 'file' or 'db'
151 """
161 """
152
162
153 baseui = ui.ui()
163 baseui = ui.ui()
154
164
155 if read_from == 'file':
165 if read_from == 'file':
156 if not os.path.isfile(path):
166 if not os.path.isfile(path):
157 log.warning('Unable to read config file %s' % path)
167 log.warning('Unable to read config file %s' % path)
158 return False
168 return False
159 log.debug('reading hgrc from %s', path)
169 log.debug('reading hgrc from %s', path)
160 cfg = config.config()
170 cfg = config.config()
161 cfg.read(path)
171 cfg.read(path)
162 for section in ui_sections:
172 for section in ui_sections:
163 for k, v in cfg.items(section):
173 for k, v in cfg.items(section):
164 baseui.setconfig(section, k, v)
174 baseui.setconfig(section, k, v)
165 log.debug('settings ui from file[%s]%s:%s', section, k, v)
175 log.debug('settings ui from file[%s]%s:%s', section, k, v)
166 if checkpaths:check_repo_dir(cfg.items('paths'))
176 if checkpaths:check_repo_dir(cfg.items('paths'))
167
177
168
178
169 elif read_from == 'db':
179 elif read_from == 'db':
170 hg_ui = get_hg_ui_cached()
180 hg_ui = get_hg_ui_cached()
171 for ui_ in hg_ui:
181 for ui_ in hg_ui:
172 if ui_.ui_active:
182 if ui_.ui_active:
173 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section, ui_.ui_key, ui_.ui_value)
183 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section, ui_.ui_key, ui_.ui_value)
174 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
184 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
175
185
176
186
177 return baseui
187 return baseui
178
188
179
189
180 def set_hg_app_config(config):
190 def set_hg_app_config(config):
181 hgsettings = get_hg_settings()
191 hgsettings = get_hg_settings()
182
192
183 for k, v in hgsettings.items():
193 for k, v in hgsettings.items():
184 config[k] = v
194 config[k] = v
185
195
186 def invalidate_cache(name, *args):
196 def invalidate_cache(name, *args):
187 """Invalidates given name cache"""
197 """Invalidates given name cache"""
188
198
189 from beaker.cache import region_invalidate
199 from beaker.cache import region_invalidate
190 log.info('INVALIDATING CACHE FOR %s', name)
200 log.info('INVALIDATING CACHE FOR %s', name)
191
201
192 """propagate our arguments to make sure invalidation works. First
202 """propagate our arguments to make sure invalidation works. First
193 argument has to be the name of cached func name give to cache decorator
203 argument has to be the name of cached func name give to cache decorator
194 without that the invalidation would not work"""
204 without that the invalidation would not work"""
195 tmp = [name]
205 tmp = [name]
196 tmp.extend(args)
206 tmp.extend(args)
197 args = tuple(tmp)
207 args = tuple(tmp)
198
208
199 if name == 'cached_repo_list':
209 if name == 'cached_repo_list':
200 from pylons_app.model.hg_model import _get_repos_cached
210 from pylons_app.model.hg_model import _get_repos_cached
201 region_invalidate(_get_repos_cached, None, *args)
211 region_invalidate(_get_repos_cached, None, *args)
202
212
203 if name == 'full_changelog':
213 if name == 'full_changelog':
204 from pylons_app.model.hg_model import _full_changelog_cached
214 from pylons_app.model.hg_model import _full_changelog_cached
205 region_invalidate(_full_changelog_cached, None, *args)
215 region_invalidate(_full_changelog_cached, None, *args)
206
216
207 class EmptyChangeset(BaseChangeset):
217 class EmptyChangeset(BaseChangeset):
208
218
209 revision = -1
219 revision = -1
210 message = ''
220 message = ''
211
221
212 @LazyProperty
222 @LazyProperty
213 def raw_id(self):
223 def raw_id(self):
214 """
224 """
215 Returns raw string identifing this changeset, useful for web
225 Returns raw string identifing this changeset, useful for web
216 representation.
226 representation.
217 """
227 """
218 return '0' * 12
228 return '0' * 12
219
229
220
230
221 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
231 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
222 """
232 """
223 maps all found repositories into db
233 maps all found repositories into db
224 """
234 """
225 from pylons_app.model.repo_model import RepoModel
235 from pylons_app.model.repo_model import RepoModel
226
236
227 sa = meta.Session
237 sa = meta.Session
228 user = sa.query(User).filter(User.admin == True).first()
238 user = sa.query(User).filter(User.admin == True).first()
229
239
230 rm = RepoModel()
240 rm = RepoModel()
231
241
232 for name, repo in initial_repo_list.items():
242 for name, repo in initial_repo_list.items():
233 if not sa.query(Repository).filter(Repository.repo_name == name).scalar():
243 if not sa.query(Repository).filter(Repository.repo_name == name).scalar():
234 log.info('repository %s not found creating default', name)
244 log.info('repository %s not found creating default', name)
235
245
236 form_data = {
246 form_data = {
237 'repo_name':name,
247 'repo_name':name,
238 'description':repo.description if repo.description != 'unknown' else \
248 'description':repo.description if repo.description != 'unknown' else \
239 'auto description for %s' % name,
249 'auto description for %s' % name,
240 'private':False
250 'private':False
241 }
251 }
242 rm.create(form_data, user, just_db=True)
252 rm.create(form_data, user, just_db=True)
243
253
244
254
245 if remove_obsolete:
255 if remove_obsolete:
246 #remove from database those repositories that are not in the filesystem
256 #remove from database those repositories that are not in the filesystem
247 for repo in sa.query(Repository).all():
257 for repo in sa.query(Repository).all():
248 if repo.repo_name not in initial_repo_list.keys():
258 if repo.repo_name not in initial_repo_list.keys():
249 sa.delete(repo)
259 sa.delete(repo)
250 sa.commit()
260 sa.commit()
251
261
252
262
253 meta.Session.remove()
263 meta.Session.remove()
254
264
255 from UserDict import DictMixin
265 from UserDict import DictMixin
256
266
257 class OrderedDict(dict, DictMixin):
267 class OrderedDict(dict, DictMixin):
258
268
259 def __init__(self, *args, **kwds):
269 def __init__(self, *args, **kwds):
260 if len(args) > 1:
270 if len(args) > 1:
261 raise TypeError('expected at most 1 arguments, got %d' % len(args))
271 raise TypeError('expected at most 1 arguments, got %d' % len(args))
262 try:
272 try:
263 self.__end
273 self.__end
264 except AttributeError:
274 except AttributeError:
265 self.clear()
275 self.clear()
266 self.update(*args, **kwds)
276 self.update(*args, **kwds)
267
277
268 def clear(self):
278 def clear(self):
269 self.__end = end = []
279 self.__end = end = []
270 end += [None, end, end] # sentinel node for doubly linked list
280 end += [None, end, end] # sentinel node for doubly linked list
271 self.__map = {} # key --> [key, prev, next]
281 self.__map = {} # key --> [key, prev, next]
272 dict.clear(self)
282 dict.clear(self)
273
283
274 def __setitem__(self, key, value):
284 def __setitem__(self, key, value):
275 if key not in self:
285 if key not in self:
276 end = self.__end
286 end = self.__end
277 curr = end[1]
287 curr = end[1]
278 curr[2] = end[1] = self.__map[key] = [key, curr, end]
288 curr[2] = end[1] = self.__map[key] = [key, curr, end]
279 dict.__setitem__(self, key, value)
289 dict.__setitem__(self, key, value)
280
290
281 def __delitem__(self, key):
291 def __delitem__(self, key):
282 dict.__delitem__(self, key)
292 dict.__delitem__(self, key)
283 key, prev, next = self.__map.pop(key)
293 key, prev, next = self.__map.pop(key)
284 prev[2] = next
294 prev[2] = next
285 next[1] = prev
295 next[1] = prev
286
296
287 def __iter__(self):
297 def __iter__(self):
288 end = self.__end
298 end = self.__end
289 curr = end[2]
299 curr = end[2]
290 while curr is not end:
300 while curr is not end:
291 yield curr[0]
301 yield curr[0]
292 curr = curr[2]
302 curr = curr[2]
293
303
294 def __reversed__(self):
304 def __reversed__(self):
295 end = self.__end
305 end = self.__end
296 curr = end[1]
306 curr = end[1]
297 while curr is not end:
307 while curr is not end:
298 yield curr[0]
308 yield curr[0]
299 curr = curr[1]
309 curr = curr[1]
300
310
301 def popitem(self, last=True):
311 def popitem(self, last=True):
302 if not self:
312 if not self:
303 raise KeyError('dictionary is empty')
313 raise KeyError('dictionary is empty')
304 if last:
314 if last:
305 key = reversed(self).next()
315 key = reversed(self).next()
306 else:
316 else:
307 key = iter(self).next()
317 key = iter(self).next()
308 value = self.pop(key)
318 value = self.pop(key)
309 return key, value
319 return key, value
310
320
311 def __reduce__(self):
321 def __reduce__(self):
312 items = [[k, self[k]] for k in self]
322 items = [[k, self[k]] for k in self]
313 tmp = self.__map, self.__end
323 tmp = self.__map, self.__end
314 del self.__map, self.__end
324 del self.__map, self.__end
315 inst_dict = vars(self).copy()
325 inst_dict = vars(self).copy()
316 self.__map, self.__end = tmp
326 self.__map, self.__end = tmp
317 if inst_dict:
327 if inst_dict:
318 return (self.__class__, (items,), inst_dict)
328 return (self.__class__, (items,), inst_dict)
319 return self.__class__, (items,)
329 return self.__class__, (items,)
320
330
321 def keys(self):
331 def keys(self):
322 return list(self)
332 return list(self)
323
333
324 setdefault = DictMixin.setdefault
334 setdefault = DictMixin.setdefault
325 update = DictMixin.update
335 update = DictMixin.update
326 pop = DictMixin.pop
336 pop = DictMixin.pop
327 values = DictMixin.values
337 values = DictMixin.values
328 items = DictMixin.items
338 items = DictMixin.items
329 iterkeys = DictMixin.iterkeys
339 iterkeys = DictMixin.iterkeys
330 itervalues = DictMixin.itervalues
340 itervalues = DictMixin.itervalues
331 iteritems = DictMixin.iteritems
341 iteritems = DictMixin.iteritems
332
342
333 def __repr__(self):
343 def __repr__(self):
334 if not self:
344 if not self:
335 return '%s()' % (self.__class__.__name__,)
345 return '%s()' % (self.__class__.__name__,)
336 return '%s(%r)' % (self.__class__.__name__, self.items())
346 return '%s(%r)' % (self.__class__.__name__, self.items())
337
347
338 def copy(self):
348 def copy(self):
339 return self.__class__(self)
349 return self.__class__(self)
340
350
341 @classmethod
351 @classmethod
342 def fromkeys(cls, iterable, value=None):
352 def fromkeys(cls, iterable, value=None):
343 d = cls()
353 d = cls()
344 for key in iterable:
354 for key in iterable:
345 d[key] = value
355 d[key] = value
346 return d
356 return d
347
357
348 def __eq__(self, other):
358 def __eq__(self, other):
349 if isinstance(other, OrderedDict):
359 if isinstance(other, OrderedDict):
350 return len(self) == len(other) and self.items() == other.items()
360 return len(self) == len(other) and self.items() == other.items()
351 return dict.__eq__(self, other)
361 return dict.__eq__(self, other)
352
362
353 def __ne__(self, other):
363 def __ne__(self, other):
354 return not self == other
364 return not self == other
@@ -1,328 +1,330 b''
1 """ this is forms validation classes
1 """ this is forms validation classes
2 http://formencode.org/module-formencode.validators.html
2 http://formencode.org/module-formencode.validators.html
3 for list off all availible validators
3 for list off all availible validators
4
4
5 we can create our own validators
5 we can create our own validators
6
6
7 The table below outlines the options which can be used in a schema in addition to the validators themselves
7 The table below outlines the options which can be used in a schema in addition to the validators themselves
8 pre_validators [] These validators will be applied before the schema
8 pre_validators [] These validators will be applied before the schema
9 chained_validators [] These validators will be applied after the schema
9 chained_validators [] These validators will be applied after the schema
10 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
10 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
12 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
12 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
13 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
13 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
14
14
15
15
16 <name> = formencode.validators.<name of validator>
16 <name> = formencode.validators.<name of validator>
17 <name> must equal form name
17 <name> must equal form name
18 list=[1,2,3,4,5]
18 list=[1,2,3,4,5]
19 for SELECT use formencode.All(OneOf(list), Int())
19 for SELECT use formencode.All(OneOf(list), Int())
20
20
21 """
21 """
22 from formencode import All
22 from formencode import All
23 from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \
23 from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \
24 Email, Bool, StringBoolean
24 Email, Bool, StringBoolean
25 from pylons import session
25 from pylons import session
26 from pylons.i18n.translation import _
26 from pylons.i18n.translation import _
27 from pylons_app.lib.auth import get_crypt_password
27 from pylons_app.lib.auth import get_crypt_password
28 from pylons_app.model import meta
28 from pylons_app.model import meta
29 from pylons_app.model.db import User, Repository
29 from pylons_app.model.db import User, Repository
30 from sqlalchemy.exc import OperationalError
30 from sqlalchemy.exc import OperationalError
31 from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
31 from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
32 from webhelpers.pylonslib.secure_form import authentication_token
32 from webhelpers.pylonslib.secure_form import authentication_token
33 import datetime
33 import datetime
34 import formencode
34 import formencode
35 import logging
35 import logging
36 import os
36 import os
37 import pylons_app.lib.helpers as h
37 import pylons_app.lib.helpers as h
38 log = logging.getLogger(__name__)
38 log = logging.getLogger(__name__)
39
39
40
40
41 #this is needed to translate the messages using _() in validators
41 #this is needed to translate the messages using _() in validators
42 class State_obj(object):
42 class State_obj(object):
43 _ = staticmethod(_)
43 _ = staticmethod(_)
44
44
45 #===============================================================================
45 #===============================================================================
46 # VALIDATORS
46 # VALIDATORS
47 #===============================================================================
47 #===============================================================================
48 class ValidAuthToken(formencode.validators.FancyValidator):
48 class ValidAuthToken(formencode.validators.FancyValidator):
49 messages = {'invalid_token':_('Token mismatch')}
49 messages = {'invalid_token':_('Token mismatch')}
50
50
51 def validate_python(self, value, state):
51 def validate_python(self, value, state):
52
52
53 if value != authentication_token():
53 if value != authentication_token():
54 raise formencode.Invalid(self.message('invalid_token', state,
54 raise formencode.Invalid(self.message('invalid_token', state,
55 search_number=value), value, state)
55 search_number=value), value, state)
56
56
57 def ValidUsername(edit, old_data):
57 def ValidUsername(edit, old_data):
58 class _ValidUsername(formencode.validators.FancyValidator):
58 class _ValidUsername(formencode.validators.FancyValidator):
59
59
60 def validate_python(self, value, state):
60 def validate_python(self, value, state):
61 if value in ['default', 'new_user']:
61 if value in ['default', 'new_user']:
62 raise formencode.Invalid(_('Invalid username'), value, state)
62 raise formencode.Invalid(_('Invalid username'), value, state)
63 #check if user is uniq
63 #check if user is uniq
64 sa = meta.Session
64 sa = meta.Session
65 old_un = None
65 old_un = None
66 if edit:
66 if edit:
67 old_un = sa.query(User).get(old_data.get('user_id')).username
67 old_un = sa.query(User).get(old_data.get('user_id')).username
68
68
69 if old_un != value or not edit:
69 if old_un != value or not edit:
70 if sa.query(User).filter(User.username == value).scalar():
70 if sa.query(User).filter(User.username == value).scalar():
71 raise formencode.Invalid(_('This username already exists') ,
71 raise formencode.Invalid(_('This username already exists') ,
72 value, state)
72 value, state)
73 meta.Session.remove()
73 meta.Session.remove()
74
74
75 return _ValidUsername
75 return _ValidUsername
76
76
77 class ValidPassword(formencode.validators.FancyValidator):
77 class ValidPassword(formencode.validators.FancyValidator):
78
78
79 def to_python(self, value, state):
79 def to_python(self, value, state):
80 if value:
80 if value:
81 return get_crypt_password(value)
81 return get_crypt_password(value)
82
82
83 class ValidAuth(formencode.validators.FancyValidator):
83 class ValidAuth(formencode.validators.FancyValidator):
84 messages = {
84 messages = {
85 'invalid_password':_('invalid password'),
85 'invalid_password':_('invalid password'),
86 'invalid_login':_('invalid user name'),
86 'invalid_login':_('invalid user name'),
87 'disabled_account':_('Your acccount is disabled')
87 'disabled_account':_('Your acccount is disabled')
88
88
89 }
89 }
90 #error mapping
90 #error mapping
91 e_dict = {'username':messages['invalid_login'],
91 e_dict = {'username':messages['invalid_login'],
92 'password':messages['invalid_password']}
92 'password':messages['invalid_password']}
93 e_dict_disable = {'username':messages['disabled_account']}
93 e_dict_disable = {'username':messages['disabled_account']}
94
94
95 def validate_python(self, value, state):
95 def validate_python(self, value, state):
96 sa = meta.Session
96 sa = meta.Session
97 crypted_passwd = get_crypt_password(value['password'])
97 crypted_passwd = get_crypt_password(value['password'])
98 username = value['username']
98 username = value['username']
99 try:
99 try:
100 user = sa.query(User).filter(User.username == username).one()
100 user = sa.query(User).filter(User.username == username).one()
101 except (NoResultFound, MultipleResultsFound, OperationalError) as e:
101 except (NoResultFound, MultipleResultsFound, OperationalError) as e:
102 log.error(e)
102 log.error(e)
103 user = None
103 user = None
104 raise formencode.Invalid(self.message('invalid_password',
104 raise formencode.Invalid(self.message('invalid_password',
105 state=State_obj), value, state,
105 state=State_obj), value, state,
106 error_dict=self.e_dict)
106 error_dict=self.e_dict)
107 if user:
107 if user:
108 if user.active:
108 if user.active:
109 if user.username == username and user.password == crypted_passwd:
109 if user.username == username and user.password == crypted_passwd:
110 from pylons_app.lib.auth import AuthUser
110 from pylons_app.lib.auth import AuthUser
111 auth_user = AuthUser()
111 auth_user = AuthUser()
112 auth_user.username = username
112 auth_user.username = username
113 auth_user.is_authenticated = True
113 auth_user.is_authenticated = True
114 auth_user.is_admin = user.admin
114 auth_user.is_admin = user.admin
115 auth_user.user_id = user.user_id
115 auth_user.user_id = user.user_id
116 auth_user.name = user.name
116 auth_user.name = user.name
117 auth_user.lastname = user.lastname
117 auth_user.lastname = user.lastname
118 session['hg_app_user'] = auth_user
118 session['hg_app_user'] = auth_user
119 session.save()
119 session.save()
120 log.info('user %s is now authenticated', username)
120 log.info('user %s is now authenticated', username)
121
121
122 try:
122 try:
123 user.last_login = datetime.datetime.now()
123 user.last_login = datetime.datetime.now()
124 sa.add(user)
124 sa.add(user)
125 sa.commit()
125 sa.commit()
126 except (OperationalError) as e:
126 except (OperationalError) as e:
127 log.error(e)
127 log.error(e)
128 sa.rollback()
128 sa.rollback()
129
129
130 return value
130 return value
131 else:
131 else:
132 log.warning('user %s not authenticated', username)
132 log.warning('user %s not authenticated', username)
133 raise formencode.Invalid(self.message('invalid_password',
133 raise formencode.Invalid(self.message('invalid_password',
134 state=State_obj), value, state,
134 state=State_obj), value, state,
135 error_dict=self.e_dict)
135 error_dict=self.e_dict)
136 else:
136 else:
137 log.warning('user %s is disabled', username)
137 log.warning('user %s is disabled', username)
138 raise formencode.Invalid(self.message('disabled_account',
138 raise formencode.Invalid(self.message('disabled_account',
139 state=State_obj),
139 state=State_obj),
140 value, state,
140 value, state,
141 error_dict=self.e_dict_disable)
141 error_dict=self.e_dict_disable)
142
142
143 meta.Session.remove()
143 meta.Session.remove()
144
144
145
145
146 class ValidRepoUser(formencode.validators.FancyValidator):
146 class ValidRepoUser(formencode.validators.FancyValidator):
147
147
148 def to_python(self, value, state):
148 def to_python(self, value, state):
149 sa = meta.Session
149 sa = meta.Session
150 try:
150 try:
151 self.user_db = sa.query(User)\
151 self.user_db = sa.query(User)\
152 .filter(User.active == True)\
152 .filter(User.active == True)\
153 .filter(User.username == value).one()
153 .filter(User.username == value).one()
154 except Exception:
154 except Exception:
155 raise formencode.Invalid(_('This username is not valid'),
155 raise formencode.Invalid(_('This username is not valid'),
156 value, state)
156 value, state)
157 meta.Session.remove()
157 meta.Session.remove()
158 return self.user_db.user_id
158 return self.user_db.user_id
159
159
160 def ValidRepoName(edit, old_data):
160 def ValidRepoName(edit, old_data):
161 class _ValidRepoName(formencode.validators.FancyValidator):
161 class _ValidRepoName(formencode.validators.FancyValidator):
162
162
163 def to_python(self, value, state):
163 def to_python(self, value, state):
164 slug = h.repo_name_slug(value)
164 slug = h.repo_name_slug(value)
165 if slug in ['_admin']:
165 if slug in ['_admin']:
166 raise formencode.Invalid(_('This repository name is disallowed'),
166 raise formencode.Invalid(_('This repository name is disallowed'),
167 value, state)
167 value, state)
168 if old_data.get('repo_name') != value or not edit:
168 if old_data.get('repo_name') != value or not edit:
169 sa = meta.Session
169 sa = meta.Session
170 if sa.query(Repository).filter(Repository.repo_name == slug).scalar():
170 if sa.query(Repository).filter(Repository.repo_name == slug).scalar():
171 raise formencode.Invalid(_('This repository already exists') ,
171 raise formencode.Invalid(_('This repository already exists') ,
172 value, state)
172 value, state)
173 meta.Session.remove()
173 meta.Session.remove()
174 return slug
174 return slug
175
175
176
176
177 return _ValidRepoName
177 return _ValidRepoName
178
178
179 class ValidPerms(formencode.validators.FancyValidator):
179 class ValidPerms(formencode.validators.FancyValidator):
180 messages = {'perm_new_user_name':_('This username is not valid')}
180 messages = {'perm_new_user_name':_('This username is not valid')}
181
181
182 def to_python(self, value, state):
182 def to_python(self, value, state):
183 perms_update = []
183 perms_update = []
184 perms_new = []
184 perms_new = []
185 #build a list of permission to update and new permission to create
185 #build a list of permission to update and new permission to create
186 for k, v in value.items():
186 for k, v in value.items():
187 if k.startswith('perm_'):
187 if k.startswith('perm_'):
188 if k.startswith('perm_new_user'):
188 if k.startswith('perm_new_user'):
189 new_perm = value.get('perm_new_user', False)
189 new_perm = value.get('perm_new_user', False)
190 new_user = value.get('perm_new_user_name', False)
190 new_user = value.get('perm_new_user_name', False)
191 if new_user and new_perm:
191 if new_user and new_perm:
192 if (new_user, new_perm) not in perms_new:
192 if (new_user, new_perm) not in perms_new:
193 perms_new.append((new_user, new_perm))
193 perms_new.append((new_user, new_perm))
194 else:
194 else:
195 usr = k[5:]
195 usr = k[5:]
196 if usr == 'default':
196 if usr == 'default':
197 if value['private']:
197 if value['private']:
198 #set none for default when updating to private repo
198 #set none for default when updating to private repo
199 v = 'repository.none'
199 v = 'repository.none'
200 perms_update.append((usr, v))
200 perms_update.append((usr, v))
201 value['perms_updates'] = perms_update
201 value['perms_updates'] = perms_update
202 value['perms_new'] = perms_new
202 value['perms_new'] = perms_new
203 sa = meta.Session
203 sa = meta.Session
204 for k, v in perms_new:
204 for k, v in perms_new:
205 try:
205 try:
206 self.user_db = sa.query(User)\
206 self.user_db = sa.query(User)\
207 .filter(User.active == True)\
207 .filter(User.active == True)\
208 .filter(User.username == k).one()
208 .filter(User.username == k).one()
209 except Exception:
209 except Exception:
210 msg = self.message('perm_new_user_name',
210 msg = self.message('perm_new_user_name',
211 state=State_obj)
211 state=State_obj)
212 raise formencode.Invalid(msg, value, state, error_dict={'perm_new_user_name':msg})
212 raise formencode.Invalid(msg, value, state, error_dict={'perm_new_user_name':msg})
213 return value
213 return value
214
214
215 class ValidSettings(formencode.validators.FancyValidator):
215 class ValidSettings(formencode.validators.FancyValidator):
216
216
217 def to_python(self, value, state):
217 def to_python(self, value, state):
218 #settings form can't edit user
218 #settings form can't edit user
219 if value.has_key('user'):
219 if value.has_key('user'):
220 del['value']['user']
220 del['value']['user']
221
221
222 return value
222 return value
223
223
224 class ValidPath(formencode.validators.FancyValidator):
224 class ValidPath(formencode.validators.FancyValidator):
225 def to_python(self, value, state):
225 def to_python(self, value, state):
226 isdir = os.path.isdir(value.replace('*', ''))
226 isdir = os.path.isdir(value.replace('*', ''))
227 if (value.endswith('/*') or value.endswith('/**')) and isdir:
227 if (value.endswith('/*') or value.endswith('/**')) and isdir:
228 return value
228 return value
229 elif not isdir:
229 elif not isdir:
230 msg = _('This is not a valid path')
230 msg = _('This is not a valid path')
231 else:
231 else:
232 msg = _('You need to specify * or ** at the end of path (ie. /tmp/*)')
232 msg = _('You need to specify * or ** at the end of path (ie. /tmp/*)')
233
233
234 raise formencode.Invalid(msg, value, state,
234 raise formencode.Invalid(msg, value, state,
235 error_dict={'paths_root_path':msg})
235 error_dict={'paths_root_path':msg})
236
236
237 #===============================================================================
237 #===============================================================================
238 # FORMS
238 # FORMS
239 #===============================================================================
239 #===============================================================================
240 class LoginForm(formencode.Schema):
240 class LoginForm(formencode.Schema):
241 allow_extra_fields = True
241 allow_extra_fields = True
242 filter_extra_fields = True
242 filter_extra_fields = True
243 username = UnicodeString(
243 username = UnicodeString(
244 strip=True,
244 strip=True,
245 min=3,
245 min=3,
246 not_empty=True,
246 not_empty=True,
247 messages={
247 messages={
248 'empty':_('Please enter a login'),
248 'empty':_('Please enter a login'),
249 'tooShort':_('Enter a value %(min)i characters long or more')}
249 'tooShort':_('Enter a value %(min)i characters long or more')}
250 )
250 )
251
251
252 password = UnicodeString(
252 password = UnicodeString(
253 strip=True,
253 strip=True,
254 min=3,
254 min=3,
255 not_empty=True,
255 not_empty=True,
256 messages={
256 messages={
257 'empty':_('Please enter a password'),
257 'empty':_('Please enter a password'),
258 'tooShort':_('Enter a value %(min)i characters long or more')}
258 'tooShort':_('Enter a value %(min)i characters long or more')}
259 )
259 )
260
260
261
261
262 #chained validators have access to all data
262 #chained validators have access to all data
263 chained_validators = [ValidAuth]
263 chained_validators = [ValidAuth]
264
264
265 def UserForm(edit=False, old_data={}):
265 def UserForm(edit=False, old_data={}):
266 class _UserForm(formencode.Schema):
266 class _UserForm(formencode.Schema):
267 allow_extra_fields = True
267 allow_extra_fields = True
268 filter_extra_fields = True
268 filter_extra_fields = True
269 username = All(UnicodeString(strip=True, min=3, not_empty=True), ValidUsername(edit, old_data))
269 username = All(UnicodeString(strip=True, min=3, not_empty=True), ValidUsername(edit, old_data))
270 if edit:
270 if edit:
271 new_password = All(UnicodeString(strip=True, min=3, not_empty=False), ValidPassword)
271 new_password = All(UnicodeString(strip=True, min=3, not_empty=False), ValidPassword)
272 admin = StringBoolean(if_missing=False)
272 admin = StringBoolean(if_missing=False)
273 else:
273 else:
274 password = All(UnicodeString(strip=True, min=8, not_empty=True), ValidPassword)
274 password = All(UnicodeString(strip=True, min=8, not_empty=True), ValidPassword)
275 active = StringBoolean(if_missing=False)
275 active = StringBoolean(if_missing=False)
276 name = UnicodeString(strip=True, min=3, not_empty=True)
276 name = UnicodeString(strip=True, min=3, not_empty=True)
277 lastname = UnicodeString(strip=True, min=3, not_empty=True)
277 lastname = UnicodeString(strip=True, min=3, not_empty=True)
278 email = Email(not_empty=True)
278 email = Email(not_empty=True)
279
279
280 return _UserForm
280 return _UserForm
281
281
282 RegisterForm = UserForm
282 RegisterForm = UserForm
283
283
284
284
285 def RepoForm(edit=False, old_data={}):
285 def RepoForm(edit=False, old_data={}):
286 class _RepoForm(formencode.Schema):
286 class _RepoForm(formencode.Schema):
287 allow_extra_fields = True
287 allow_extra_fields = True
288 filter_extra_fields = False
288 filter_extra_fields = False
289 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
289 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
290 description = UnicodeString(strip=True, min=3, not_empty=True)
290 description = UnicodeString(strip=True, min=3, not_empty=True)
291 private = StringBoolean(if_missing=False)
291 private = StringBoolean(if_missing=False)
292
292
293 if edit:
293 if edit:
294 user = All(Int(not_empty=True), ValidRepoUser)
294 user = All(Int(not_empty=True), ValidRepoUser)
295
295
296 chained_validators = [ValidPerms]
296 chained_validators = [ValidPerms]
297 return _RepoForm
297 return _RepoForm
298
298
299 def RepoSettingsForm(edit=False, old_data={}):
299 def RepoSettingsForm(edit=False, old_data={}):
300 class _RepoForm(formencode.Schema):
300 class _RepoForm(formencode.Schema):
301 allow_extra_fields = True
301 allow_extra_fields = True
302 filter_extra_fields = False
302 filter_extra_fields = False
303 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
303 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
304 description = UnicodeString(strip=True, min=3, not_empty=True)
304 description = UnicodeString(strip=True, min=3, not_empty=True)
305 private = StringBoolean(if_missing=False)
305 private = StringBoolean(if_missing=False)
306
306
307 chained_validators = [ValidPerms, ValidSettings]
307 chained_validators = [ValidPerms, ValidSettings]
308 return _RepoForm
308 return _RepoForm
309
309
310
310
311 def ApplicationSettingsForm():
311 def ApplicationSettingsForm():
312 class _ApplicationSettingsForm(formencode.Schema):
312 class _ApplicationSettingsForm(formencode.Schema):
313 allow_extra_fields = True
313 allow_extra_fields = True
314 filter_extra_fields = False
314 filter_extra_fields = False
315 hg_app_title = UnicodeString(strip=True, min=3, not_empty=True)
315 hg_app_title = UnicodeString(strip=True, min=3, not_empty=True)
316 hg_app_realm = UnicodeString(strip=True, min=3, not_empty=True)
316 hg_app_realm = UnicodeString(strip=True, min=3, not_empty=True)
317
317
318 return _ApplicationSettingsForm
318 return _ApplicationSettingsForm
319
319
320 def ApplicationUiSettingsForm():
320 def ApplicationUiSettingsForm():
321 class _ApplicationUiSettingsForm(formencode.Schema):
321 class _ApplicationUiSettingsForm(formencode.Schema):
322 allow_extra_fields = True
322 allow_extra_fields = True
323 filter_extra_fields = False
323 filter_extra_fields = False
324 web_push_ssl = OneOf(['true', 'false'], if_missing='false')
324 web_push_ssl = OneOf(['true', 'false'], if_missing='false')
325 paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=3, not_empty=True))
325 paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=3, not_empty=True))
326 hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
327 hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
326
328
327 return _ApplicationUiSettingsForm
329 return _ApplicationUiSettingsForm
328
330
@@ -1,129 +1,145 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Settings administration')}
5 ${_('Settings administration')}
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" 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" 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 ui-widget ui-state-default ui-corner-all")}
45 ${h.submit('rescan','rescan repositories',class_="ui-button ui-widget ui-state-default ui-corner-all")}
46 </div>
46 </div>
47 </div>
47 </div>
48 </div>
48 </div>
49 ${h.end_form()}
49 ${h.end_form()}
50
50
51 <h3>${_('Global application settings')}</h3>
51 <h3>${_('Global application settings')}</h3>
52 ${h.form(url('admin_setting', setting_id='global'),method='put')}
52 ${h.form(url('admin_setting', setting_id='global'),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
57
58 <div class="field">
58 <div class="field">
59 <div class="label">
59 <div class="label">
60 <label for="hg_app_title">${_('Application name')}:</label>
60 <label for="hg_app_title">${_('Application name')}:</label>
61 </div>
61 </div>
62 <div class="input">
62 <div class="input">
63 ${h.text('hg_app_title',size=30)}
63 ${h.text('hg_app_title',size=30)}
64 </div>
64 </div>
65 </div>
65 </div>
66
66
67 <div class="field">
67 <div class="field">
68 <div class="label">
68 <div class="label">
69 <label for="hg_app_realm">${_('Realm text')}:</label>
69 <label for="hg_app_realm">${_('Realm text')}:</label>
70 </div>
70 </div>
71 <div class="input">
71 <div class="input">
72 ${h.text('hg_app_realm',size=30)}
72 ${h.text('hg_app_realm',size=30)}
73 </div>
73 </div>
74 </div>
74 </div>
75
75
76 <div class="buttons">
76 <div class="buttons">
77 ${h.submit('save','save settings',class_="ui-button ui-widget ui-state-default ui-corner-all")}
77 ${h.submit('save','save settings',class_="ui-button ui-widget ui-state-default ui-corner-all")}
78 </div>
78 </div>
79 </div>
79 </div>
80 </div>
80 </div>
81 ${h.end_form()}
81 ${h.end_form()}
82
82
83 <h3>${_('Mercurial settings')}</h3>
83 <h3>${_('Mercurial settings')}</h3>
84 ${h.form(url('admin_setting', setting_id='mercurial'),method='put')}
84 ${h.form(url('admin_setting', setting_id='mercurial'),method='put')}
85 <div class="form">
85 <div class="form">
86 <!-- fields -->
86 <!-- fields -->
87
87
88 <div class="fields">
88 <div class="fields">
89
89
90 <div class="field">
90 <div class="field">
91 <div class="label label-checkbox">
91 <div class="label label-checkbox">
92 <label for="web_push_ssl">${_('Push ssl')}:</label>
92 <label for="web_push_ssl">${_('Web')}:</label>
93 </div>
93 </div>
94 <div class="checkboxes">
94 <div class="checkboxes">
95 <div class="checkbox">
95 <div class="checkbox">
96 ${h.checkbox('web_push_ssl','true')}
96 ${h.checkbox('web_push_ssl','true')}
97 <label for="web_push_ssl">${_('require ssl for pushing')}</label>
97 <label for="web_push_ssl">${_('require ssl for pushing')}</label>
98 </div>
98 </div>
99 </div>
99 </div>
100 </div>
100 </div>
101
101
102 <div class="field">
102 <div class="field">
103 <div class="label label-checkbox">
104 <label for="web_push_ssl">${_('Hooks')}:</label>
105 </div>
106 <div class="checkboxes">
107 <div class="checkbox">
108 ${h.checkbox('hooks_changegroup_update','True')}
109 <label for="hooks_changegroup_update">${_('Update repository after push (hg update)')}</label>
110 </div>
111 <div class="checkbox">
112 ${h.checkbox('hooks_changegroup_repo_size','True')}
113 <label for="hooks_changegroup_repo_size">${_('Show repository size after push')}</label>
114 </div>
115 </div>
116 </div>
117
118 <div class="field">
103 <div class="label">
119 <div class="label">
104 <label for="paths_root_path">${_('Repositories location')}:</label>
120 <label for="paths_root_path">${_('Repositories location')}:</label>
105 </div>
121 </div>
106 <div class="input">
122 <div class="input">
107 ${h.text('paths_root_path',size=30,disabled="disabled")}
123 ${h.text('paths_root_path',size=30,readonly="readonly")}
108 <span id="path_unlock" class="tooltip" tooltip_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.'))}">
124 <span id="path_unlock" class="tooltip" tooltip_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.'))}">
109 ${_('unlock')}</span>
125 ${_('unlock')}</span>
110 </div>
126 </div>
111 </div>
127 </div>
112
128
113 <div class="buttons">
129 <div class="buttons">
114 ${h.submit('save','save settings',class_="ui-button ui-widget ui-state-default ui-corner-all")}
130 ${h.submit('save','save settings',class_="ui-button ui-widget ui-state-default ui-corner-all")}
115 </div>
131 </div>
116 </div>
132 </div>
117 </div>
133 </div>
118 ${h.end_form()}
134 ${h.end_form()}
119
135
120 <script type="text/javascript">
136 <script type="text/javascript">
121 YAHOO.util.Event.onDOMReady(function(){
137 YAHOO.util.Event.onDOMReady(function(){
122 YAHOO.util.Event.addListener('path_unlock','click',function(){
138 YAHOO.util.Event.addListener('path_unlock','click',function(){
123 YAHOO.util.Dom.get('paths_root_path').disabled=false;
139 YAHOO.util.Dom.get('paths_root_path').readonly=false;
124 });
140 });
125 });
141 });
126 </script>
142 </script>
127
143
128 </div>
144 </div>
129 </%def>
145 </%def>
General Comments 0
You need to be logged in to leave comments. Login now