##// END OF EJS Templates
new improved models with helper functions for easier data fetching
marcink -
r832:634596f8 beta
parent child Browse files
Show More
@@ -1,313 +1,313
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.repos
3 rhodecode.controllers.admin.repos
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Admin controller for RhodeCode
6 Admin controller for RhodeCode
7
7
8 :created_on: Apr 7, 2010
8 :created_on: Apr 7, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2010 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
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
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, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27
27
28 import logging
28 import logging
29 import traceback
29 import traceback
30 import formencode
30 import formencode
31 from operator import itemgetter
31 from operator import itemgetter
32 from formencode import htmlfill
32 from formencode import htmlfill
33
33
34 from paste.httpexceptions import HTTPInternalServerError
34 from paste.httpexceptions import HTTPInternalServerError
35 from pylons import request, response, session, tmpl_context as c, url
35 from pylons import request, response, session, tmpl_context as c, url
36 from pylons.controllers.util import abort, redirect
36 from pylons.controllers.util import abort, redirect
37 from pylons.i18n.translation import _
37 from pylons.i18n.translation import _
38
38
39 from rhodecode.lib import helpers as h
39 from rhodecode.lib import helpers as h
40 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
40 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
41 HasPermissionAnyDecorator
41 HasPermissionAnyDecorator
42 from rhodecode.lib.base import BaseController, render
42 from rhodecode.lib.base import BaseController, render
43 from rhodecode.lib.utils import invalidate_cache, action_logger
43 from rhodecode.lib.utils import invalidate_cache, action_logger
44 from rhodecode.model.db import User
44 from rhodecode.model.db import User
45 from rhodecode.model.forms import RepoForm
45 from rhodecode.model.forms import RepoForm
46 from rhodecode.model.scm import ScmModel
46 from rhodecode.model.scm import ScmModel
47 from rhodecode.model.repo import RepoModel
47 from rhodecode.model.repo import RepoModel
48
48
49
49
50 log = logging.getLogger(__name__)
50 log = logging.getLogger(__name__)
51
51
52 class ReposController(BaseController):
52 class ReposController(BaseController):
53 """REST Controller styled on the Atom Publishing Protocol"""
53 """REST Controller styled on the Atom Publishing Protocol"""
54 # To properly map this controller, ensure your config/routing.py
54 # To properly map this controller, ensure your config/routing.py
55 # file has a resource setup:
55 # file has a resource setup:
56 # map.resource('repo', 'repos')
56 # map.resource('repo', 'repos')
57
57
58 @LoginRequired()
58 @LoginRequired()
59 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
59 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
60 def __before__(self):
60 def __before__(self):
61 c.admin_user = session.get('admin_user')
61 c.admin_user = session.get('admin_user')
62 c.admin_username = session.get('admin_username')
62 c.admin_username = session.get('admin_username')
63 super(ReposController, self).__before__()
63 super(ReposController, self).__before__()
64
64
65 @HasPermissionAllDecorator('hg.admin')
65 @HasPermissionAllDecorator('hg.admin')
66 def index(self, format='html'):
66 def index(self, format='html'):
67 """GET /repos: All items in the collection"""
67 """GET /repos: All items in the collection"""
68 # url('repos')
68 # url('repos')
69 cached_repo_list = ScmModel().get_repos()
69 cached_repo_list = ScmModel().get_repos()
70 c.repos_list = sorted(cached_repo_list, key=itemgetter('name_sort'))
70 c.repos_list = sorted(cached_repo_list, key=itemgetter('name_sort'))
71 return render('admin/repos/repos.html')
71 return render('admin/repos/repos.html')
72
72
73 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
73 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
74 def create(self):
74 def create(self):
75 """POST /repos: Create a new item"""
75 """POST /repos: Create a new item"""
76 # url('repos')
76 # url('repos')
77 repo_model = RepoModel()
77 repo_model = RepoModel()
78 _form = RepoForm()()
78 _form = RepoForm()()
79 form_result = {}
79 form_result = {}
80 try:
80 try:
81 form_result = _form.to_python(dict(request.POST))
81 form_result = _form.to_python(dict(request.POST))
82 repo_model.create(form_result, c.rhodecode_user)
82 repo_model.create(form_result, c.rhodecode_user)
83 h.flash(_('created repository %s') % form_result['repo_name'],
83 h.flash(_('created repository %s') % form_result['repo_name'],
84 category='success')
84 category='success')
85
85
86 if request.POST.get('user_created'):
86 if request.POST.get('user_created'):
87 action_logger(self.rhodecode_user, 'user_created_repo',
87 action_logger(self.rhodecode_user, 'user_created_repo',
88 form_result['repo_name'], '', self.sa)
88 form_result['repo_name'], '', self.sa)
89 else:
89 else:
90 action_logger(self.rhodecode_user, 'admin_created_repo',
90 action_logger(self.rhodecode_user, 'admin_created_repo',
91 form_result['repo_name'], '', self.sa)
91 form_result['repo_name'], '', self.sa)
92
92
93 except formencode.Invalid, errors:
93 except formencode.Invalid, errors:
94 c.new_repo = errors.value['repo_name']
94 c.new_repo = errors.value['repo_name']
95
95
96 if request.POST.get('user_created'):
96 if request.POST.get('user_created'):
97 r = render('admin/repos/repo_add_create_repository.html')
97 r = render('admin/repos/repo_add_create_repository.html')
98 else:
98 else:
99 r = render('admin/repos/repo_add.html')
99 r = render('admin/repos/repo_add.html')
100
100
101 return htmlfill.render(
101 return htmlfill.render(
102 r,
102 r,
103 defaults=errors.value,
103 defaults=errors.value,
104 errors=errors.error_dict or {},
104 errors=errors.error_dict or {},
105 prefix_error=False,
105 prefix_error=False,
106 encoding="UTF-8")
106 encoding="UTF-8")
107
107
108 except Exception:
108 except Exception:
109 log.error(traceback.format_exc())
109 log.error(traceback.format_exc())
110 msg = _('error occured during creation of repository %s') \
110 msg = _('error occured during creation of repository %s') \
111 % form_result.get('repo_name')
111 % form_result.get('repo_name')
112 h.flash(msg, category='error')
112 h.flash(msg, category='error')
113 if request.POST.get('user_created'):
113 if request.POST.get('user_created'):
114 return redirect(url('home'))
114 return redirect(url('home'))
115 return redirect(url('repos'))
115 return redirect(url('repos'))
116
116
117 @HasPermissionAllDecorator('hg.admin')
117 @HasPermissionAllDecorator('hg.admin')
118 def new(self, format='html'):
118 def new(self, format='html'):
119 """GET /repos/new: Form to create a new item"""
119 """GET /repos/new: Form to create a new item"""
120 new_repo = request.GET.get('repo', '')
120 new_repo = request.GET.get('repo', '')
121 c.new_repo = h.repo_name_slug(new_repo)
121 c.new_repo = h.repo_name_slug(new_repo)
122
122
123 return render('admin/repos/repo_add.html')
123 return render('admin/repos/repo_add.html')
124
124
125 @HasPermissionAllDecorator('hg.admin')
125 @HasPermissionAllDecorator('hg.admin')
126 def update(self, repo_name):
126 def update(self, repo_name):
127 """PUT /repos/repo_name: Update an existing item"""
127 """PUT /repos/repo_name: Update an existing item"""
128 # Forms posted to this method should contain a hidden field:
128 # Forms posted to this method should contain a hidden field:
129 # <input type="hidden" name="_method" value="PUT" />
129 # <input type="hidden" name="_method" value="PUT" />
130 # Or using helpers:
130 # Or using helpers:
131 # h.form(url('repo', repo_name=ID),
131 # h.form(url('repo', repo_name=ID),
132 # method='put')
132 # method='put')
133 # url('repo', repo_name=ID)
133 # url('repo', repo_name=ID)
134 repo_model = RepoModel()
134 repo_model = RepoModel()
135 changed_name = repo_name
135 changed_name = repo_name
136 _form = RepoForm(edit=True, old_data={'repo_name':repo_name})()
136 _form = RepoForm(edit=True, old_data={'repo_name':repo_name})()
137
137
138 try:
138 try:
139 form_result = _form.to_python(dict(request.POST))
139 form_result = _form.to_python(dict(request.POST))
140 repo_model.update(repo_name, form_result)
140 repo_model.update(repo_name, form_result)
141 invalidate_cache('get_repo_cached_%s' % repo_name)
141 invalidate_cache('get_repo_cached_%s' % repo_name)
142 h.flash(_('Repository %s updated successfully' % repo_name),
142 h.flash(_('Repository %s updated successfully' % repo_name),
143 category='success')
143 category='success')
144 changed_name = form_result['repo_name']
144 changed_name = form_result['repo_name']
145 action_logger(self.rhodecode_user, 'admin_updated_repo',
145 action_logger(self.rhodecode_user, 'admin_updated_repo',
146 changed_name, '', self.sa)
146 changed_name, '', self.sa)
147
147
148 except formencode.Invalid, errors:
148 except formencode.Invalid, errors:
149 c.repo_info = repo_model.get_by_repo_name(repo_name)
149 c.repo_info = repo_model.get_by_repo_name(repo_name)
150 if c.repo_info.stats:
150 if c.repo_info.stats:
151 last_rev = c.repo_info.stats.stat_on_revision
151 last_rev = c.repo_info.stats.stat_on_revision
152 else:
152 else:
153 last_rev = 0
153 last_rev = 0
154 c.stats_revision = last_rev
154 c.stats_revision = last_rev
155 r = ScmModel().get(repo_name)
155 r = ScmModel().get(repo_name)
156 c.repo_last_rev = r.revisions[-1] if r.revisions else 0
156 c.repo_last_rev = r.revisions[-1] if r.revisions else 0
157
157
158 if last_rev == 0:
158 if last_rev == 0:
159 c.stats_percentage = 0
159 c.stats_percentage = 0
160 else:
160 else:
161 c.stats_percentage = '%.2f' % ((float((last_rev)) /
161 c.stats_percentage = '%.2f' % ((float((last_rev)) /
162 c.repo_last_rev) * 100)
162 c.repo_last_rev) * 100)
163
163
164 c.users_array = repo_model.get_users_js()
164 c.users_array = repo_model.get_users_js()
165 errors.value.update({'user':c.repo_info.user.username})
165 errors.value.update({'user':c.repo_info.user.username})
166 return htmlfill.render(
166 return htmlfill.render(
167 render('admin/repos/repo_edit.html'),
167 render('admin/repos/repo_edit.html'),
168 defaults=errors.value,
168 defaults=errors.value,
169 errors=errors.error_dict or {},
169 errors=errors.error_dict or {},
170 prefix_error=False,
170 prefix_error=False,
171 encoding="UTF-8")
171 encoding="UTF-8")
172
172
173 except Exception:
173 except Exception:
174 log.error(traceback.format_exc())
174 log.error(traceback.format_exc())
175 h.flash(_('error occurred during update of repository %s') \
175 h.flash(_('error occurred during update of repository %s') \
176 % repo_name, category='error')
176 % repo_name, category='error')
177
177
178 return redirect(url('edit_repo', repo_name=changed_name))
178 return redirect(url('edit_repo', repo_name=changed_name))
179
179
180 @HasPermissionAllDecorator('hg.admin')
180 @HasPermissionAllDecorator('hg.admin')
181 def delete(self, repo_name):
181 def delete(self, repo_name):
182 """DELETE /repos/repo_name: Delete an existing item"""
182 """DELETE /repos/repo_name: Delete an existing item"""
183 # Forms posted to this method should contain a hidden field:
183 # Forms posted to this method should contain a hidden field:
184 # <input type="hidden" name="_method" value="DELETE" />
184 # <input type="hidden" name="_method" value="DELETE" />
185 # Or using helpers:
185 # Or using helpers:
186 # h.form(url('repo', repo_name=ID),
186 # h.form(url('repo', repo_name=ID),
187 # method='delete')
187 # method='delete')
188 # url('repo', repo_name=ID)
188 # url('repo', repo_name=ID)
189
189
190 repo_model = RepoModel()
190 repo_model = RepoModel()
191 repo = repo_model.get_by_repo_name(repo_name)
191 repo = repo_model.get_by_repo_name(repo_name)
192 if not repo:
192 if not repo:
193 h.flash(_('%s repository is not mapped to db perhaps'
193 h.flash(_('%s repository is not mapped to db perhaps'
194 ' it was moved or renamed from the filesystem'
194 ' it was moved or renamed from the filesystem'
195 ' please run the application again'
195 ' please run the application again'
196 ' in order to rescan repositories') % repo_name,
196 ' in order to rescan repositories') % repo_name,
197 category='error')
197 category='error')
198
198
199 return redirect(url('repos'))
199 return redirect(url('repos'))
200 try:
200 try:
201 action_logger(self.rhodecode_user, 'admin_deleted_repo',
201 action_logger(self.rhodecode_user, 'admin_deleted_repo',
202 repo_name, '', self.sa)
202 repo_name, '', self.sa)
203 repo_model.delete(repo)
203 repo_model.delete(repo)
204 invalidate_cache('get_repo_cached_%s' % repo_name)
204 invalidate_cache('get_repo_cached_%s' % repo_name)
205 h.flash(_('deleted repository %s') % repo_name, category='success')
205 h.flash(_('deleted repository %s') % repo_name, category='success')
206
206
207 except Exception, e:
207 except Exception, e:
208 log.error(traceback.format_exc())
208 log.error(traceback.format_exc())
209 h.flash(_('An error occured during deletion of %s') % repo_name,
209 h.flash(_('An error occured during deletion of %s') % repo_name,
210 category='error')
210 category='error')
211
211
212 return redirect(url('repos'))
212 return redirect(url('repos'))
213
213
214 @HasPermissionAllDecorator('hg.admin')
214 @HasPermissionAllDecorator('hg.admin')
215 def delete_perm_user(self, repo_name):
215 def delete_perm_user(self, repo_name):
216 """
216 """
217 DELETE an existing repository permission user
217 DELETE an existing repository permission user
218 :param repo_name:
218 :param repo_name:
219 """
219 """
220
220
221 try:
221 try:
222 repo_model = RepoModel()
222 repo_model = RepoModel()
223 repo_model.delete_perm_user(request.POST, repo_name)
223 repo_model.delete_perm_user(request.POST, repo_name)
224 except Exception, e:
224 except Exception, e:
225 h.flash(_('An error occured during deletion of repository user'),
225 h.flash(_('An error occured during deletion of repository user'),
226 category='error')
226 category='error')
227 raise HTTPInternalServerError()
227 raise HTTPInternalServerError()
228
228
229 @HasPermissionAllDecorator('hg.admin')
229 @HasPermissionAllDecorator('hg.admin')
230 def repo_stats(self, repo_name):
230 def repo_stats(self, repo_name):
231 """
231 """
232 DELETE an existing repository statistics
232 DELETE an existing repository statistics
233 :param repo_name:
233 :param repo_name:
234 """
234 """
235
235
236 try:
236 try:
237 repo_model = RepoModel()
237 repo_model = RepoModel()
238 repo_model.delete_stats(repo_name)
238 repo_model.delete_stats(repo_name)
239 except Exception, e:
239 except Exception, e:
240 h.flash(_('An error occured during deletion of repository stats'),
240 h.flash(_('An error occured during deletion of repository stats'),
241 category='error')
241 category='error')
242 return redirect(url('edit_repo', repo_name=repo_name))
242 return redirect(url('edit_repo', repo_name=repo_name))
243
243
244 @HasPermissionAllDecorator('hg.admin')
244 @HasPermissionAllDecorator('hg.admin')
245 def repo_cache(self, repo_name):
245 def repo_cache(self, repo_name):
246 """
246 """
247 INVALIDATE exisitings repository cache
247 INVALIDATE exisitings repository cache
248 :param repo_name:
248 :param repo_name:
249 """
249 """
250
250
251 try:
251 try:
252 ScmModel().mark_for_invalidation(repo_name)
252 ScmModel().mark_for_invalidation(repo_name)
253 except Exception, e:
253 except Exception, e:
254 h.flash(_('An error occurred during cache invalidation'),
254 h.flash(_('An error occurred during cache invalidation'),
255 category='error')
255 category='error')
256 return redirect(url('edit_repo', repo_name=repo_name))
256 return redirect(url('edit_repo', repo_name=repo_name))
257
257
258 @HasPermissionAllDecorator('hg.admin')
258 @HasPermissionAllDecorator('hg.admin')
259 def show(self, repo_name, format='html'):
259 def show(self, repo_name, format='html'):
260 """GET /repos/repo_name: Show a specific item"""
260 """GET /repos/repo_name: Show a specific item"""
261 # url('repo', repo_name=ID)
261 # url('repo', repo_name=ID)
262
262
263 @HasPermissionAllDecorator('hg.admin')
263 @HasPermissionAllDecorator('hg.admin')
264 def edit(self, repo_name, format='html'):
264 def edit(self, repo_name, format='html'):
265 """GET /repos/repo_name/edit: Form to edit an existing item"""
265 """GET /repos/repo_name/edit: Form to edit an existing item"""
266 # url('edit_repo', repo_name=ID)
266 # url('edit_repo', repo_name=ID)
267 repo_model = RepoModel()
267 repo_model = RepoModel()
268 r = ScmModel().get(repo_name)
268 r = ScmModel().get(repo_name)
269 c.repo_info = repo_model.get_by_repo_name(repo_name)
269 c.repo_info = repo_model.get_by_repo_name(repo_name)
270
270
271 if c.repo_info is None:
271 if c.repo_info is None:
272 h.flash(_('%s repository is not mapped to db perhaps'
272 h.flash(_('%s repository is not mapped to db perhaps'
273 ' it was created or renamed from the filesystem'
273 ' it was created or renamed from the filesystem'
274 ' please run the application again'
274 ' please run the application again'
275 ' in order to rescan repositories') % repo_name,
275 ' in order to rescan repositories') % repo_name,
276 category='error')
276 category='error')
277
277
278 return redirect(url('repos'))
278 return redirect(url('repos'))
279
279
280 if c.repo_info.stats:
280 if c.repo_info.stats:
281 last_rev = c.repo_info.stats.stat_on_revision
281 last_rev = c.repo_info.stats.stat_on_revision
282 else:
282 else:
283 last_rev = 0
283 last_rev = 0
284 c.stats_revision = last_rev
284 c.stats_revision = last_rev
285
285
286 c.repo_last_rev = r.revisions[-1] if r.revisions else 0
286 c.repo_last_rev = r.revisions[-1] if r.revisions else 0
287
287
288 if last_rev == 0:
288 if last_rev == 0:
289 c.stats_percentage = 0
289 c.stats_percentage = 0
290 else:
290 else:
291 c.stats_percentage = '%.2f' % ((float((last_rev)) /
291 c.stats_percentage = '%.2f' % ((float((last_rev)) /
292 c.repo_last_rev) * 100)
292 c.repo_last_rev) * 100)
293
293
294 defaults = c.repo_info.__dict__.copy()
294 defaults = c.repo_info.get_dict()
295 if c.repo_info.user:
295 if c.repo_info.user:
296 defaults.update({'user':c.repo_info.user.username})
296 defaults.update({'user':c.repo_info.user.username})
297 else:
297 else:
298 replacement_user = self.sa.query(User)\
298 replacement_user = self.sa.query(User)\
299 .filter(User.admin == True).first().username
299 .filter(User.admin == True).first().username
300 defaults.update({'user':replacement_user})
300 defaults.update({'user':replacement_user})
301
301
302 c.users_array = repo_model.get_users_js()
302 c.users_array = repo_model.get_users_js()
303
303
304 for p in c.repo_info.repo_to_perm:
304 for p in c.repo_info.repo_to_perm:
305 defaults.update({'perm_%s' % p.user.username:
305 defaults.update({'perm_%s' % p.user.username:
306 p.permission.permission_name})
306 p.permission.permission_name})
307
307
308 return htmlfill.render(
308 return htmlfill.render(
309 render('admin/repos/repo_edit.html'),
309 render('admin/repos/repo_edit.html'),
310 defaults=defaults,
310 defaults=defaults,
311 encoding="UTF-8",
311 encoding="UTF-8",
312 force_defaults=False
312 force_defaults=False
313 )
313 )
@@ -1,339 +1,339
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 package.rhodecode.controllers.admin.settings
3 package.rhodecode.controllers.admin.settings
4 ~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~
5 settings controller for rhodecode admin
5 settings controller for rhodecode admin
6
6
7 :created_on: Jul 14, 2010
7 :created_on: Jul 14, 2010
8 :author: marcink
8 :author: marcink
9 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
9 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :license: GPLv3, see COPYING for more details.
10 :license: GPLv3, see COPYING for more details.
11 """
11 """
12 # This program is free software; you can redistribute it and/or
12 # This program is free software; you can redistribute it and/or
13 # modify it under the terms of the GNU General Public License
13 # modify it under the terms of the GNU General Public License
14 # as published by the Free Software Foundation; version 2
14 # as published by the Free Software Foundation; version 2
15 # of the License or (at your opinion) any later version of the license.
15 # of the License or (at your opinion) any later version of the license.
16 #
16 #
17 # This program is distributed in the hope that it will be useful,
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
20 # GNU General Public License for more details.
21 #
21 #
22 # You should have received a copy of the GNU General Public License
22 # You should have received a copy of the GNU General Public License
23 # along with this program; if not, write to the Free Software
23 # along with this program; if not, write to the Free Software
24 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
24 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # MA 02110-1301, USA.
25 # MA 02110-1301, USA.
26
26
27 from formencode import htmlfill
27 from formencode import htmlfill
28 from pylons import request, session, tmpl_context as c, url, app_globals as g, \
28 from pylons import request, session, tmpl_context as c, url, app_globals as g, \
29 config
29 config
30 from pylons.controllers.util import abort, redirect
30 from pylons.controllers.util import abort, redirect
31 from pylons.i18n.translation import _
31 from pylons.i18n.translation import _
32 from rhodecode.lib import helpers as h
32 from rhodecode.lib import helpers as h
33 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
33 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
34 HasPermissionAnyDecorator, NotAnonymous
34 HasPermissionAnyDecorator, NotAnonymous
35 from rhodecode.lib.base import BaseController, render
35 from rhodecode.lib.base import BaseController, render
36 from rhodecode.lib.celerylib import tasks, run_task
36 from rhodecode.lib.celerylib import tasks, run_task
37 from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
37 from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
38 set_rhodecode_config
38 set_rhodecode_config
39 from rhodecode.model.db import RhodeCodeUi, Repository
39 from rhodecode.model.db import RhodeCodeUi, Repository
40 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
40 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
41 ApplicationUiSettingsForm
41 ApplicationUiSettingsForm
42 from rhodecode.model.scm import ScmModel
42 from rhodecode.model.scm import ScmModel
43 from rhodecode.model.settings import SettingsModel
43 from rhodecode.model.settings import SettingsModel
44 from rhodecode.model.user import UserModel
44 from rhodecode.model.user import UserModel
45 from sqlalchemy import func
45 from sqlalchemy import func
46 import formencode
46 import formencode
47 import logging
47 import logging
48 import traceback
48 import traceback
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
60
61 @LoginRequired()
61 @LoginRequired()
62 def __before__(self):
62 def __before__(self):
63 c.admin_user = session.get('admin_user')
63 c.admin_user = session.get('admin_user')
64 c.admin_username = session.get('admin_username')
64 c.admin_username = session.get('admin_username')
65 super(SettingsController, self).__before__()
65 super(SettingsController, self).__before__()
66
66
67
67
68 @HasPermissionAllDecorator('hg.admin')
68 @HasPermissionAllDecorator('hg.admin')
69 def index(self, format='html'):
69 def index(self, format='html'):
70 """GET /admin/settings: All items in the collection"""
70 """GET /admin/settings: All items in the collection"""
71 # url('admin_settings')
71 # url('admin_settings')
72
72
73 defaults = SettingsModel().get_app_settings()
73 defaults = SettingsModel().get_app_settings()
74 defaults.update(self.get_hg_ui_settings())
74 defaults.update(self.get_hg_ui_settings())
75 return htmlfill.render(
75 return htmlfill.render(
76 render('admin/settings/settings.html'),
76 render('admin/settings/settings.html'),
77 defaults=defaults,
77 defaults=defaults,
78 encoding="UTF-8",
78 encoding="UTF-8",
79 force_defaults=False
79 force_defaults=False
80 )
80 )
81
81
82 @HasPermissionAllDecorator('hg.admin')
82 @HasPermissionAllDecorator('hg.admin')
83 def create(self):
83 def create(self):
84 """POST /admin/settings: Create a new item"""
84 """POST /admin/settings: Create a new item"""
85 # url('admin_settings')
85 # url('admin_settings')
86
86
87 @HasPermissionAllDecorator('hg.admin')
87 @HasPermissionAllDecorator('hg.admin')
88 def new(self, format='html'):
88 def new(self, format='html'):
89 """GET /admin/settings/new: Form to create a new item"""
89 """GET /admin/settings/new: Form to create a new item"""
90 # url('admin_new_setting')
90 # url('admin_new_setting')
91
91
92 @HasPermissionAllDecorator('hg.admin')
92 @HasPermissionAllDecorator('hg.admin')
93 def update(self, setting_id):
93 def update(self, setting_id):
94 """PUT /admin/settings/setting_id: Update an existing item"""
94 """PUT /admin/settings/setting_id: Update an existing item"""
95 # Forms posted to this method should contain a hidden field:
95 # Forms posted to this method should contain a hidden field:
96 # <input type="hidden" name="_method" value="PUT" />
96 # <input type="hidden" name="_method" value="PUT" />
97 # Or using helpers:
97 # Or using helpers:
98 # h.form(url('admin_setting', setting_id=ID),
98 # h.form(url('admin_setting', setting_id=ID),
99 # method='put')
99 # method='put')
100 # url('admin_setting', setting_id=ID)
100 # url('admin_setting', setting_id=ID)
101 if setting_id == 'mapping':
101 if setting_id == 'mapping':
102 rm_obsolete = request.POST.get('destroy', False)
102 rm_obsolete = request.POST.get('destroy', False)
103 log.debug('Rescanning directories with destroy=%s', rm_obsolete)
103 log.debug('Rescanning directories with destroy=%s', rm_obsolete)
104
104
105 initial = ScmModel().repo_scan(g.paths[0][1], g.baseui)
105 initial = ScmModel().repo_scan(g.paths[0][1], g.baseui)
106 for repo_name in initial.keys():
106 for repo_name in initial.keys():
107 invalidate_cache('get_repo_cached_%s' % repo_name)
107 invalidate_cache('get_repo_cached_%s' % repo_name)
108
108
109 repo2db_mapper(initial, rm_obsolete)
109 repo2db_mapper(initial, rm_obsolete)
110
110
111 h.flash(_('Repositories successfully rescanned'), category='success')
111 h.flash(_('Repositories successfully rescanned'), 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 task = run_task(tasks.whoosh_index, repo_location, full_index)
116 task = 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 settings_model = SettingsModel()
124 settings_model = SettingsModel()
125 try:
125 try:
126 hgsettings1 = settings_model.get('title')
126 hgsettings1 = settings_model.get('title')
127 hgsettings1.app_settings_value = form_result['rhodecode_title']
127 hgsettings1.app_settings_value = form_result['rhodecode_title']
128
128
129 hgsettings2 = settings_model.get('realm')
129 hgsettings2 = settings_model.get('realm')
130 hgsettings2.app_settings_value = form_result['rhodecode_realm']
130 hgsettings2.app_settings_value = form_result['rhodecode_realm']
131
131
132
132
133 self.sa.add(hgsettings1)
133 self.sa.add(hgsettings1)
134 self.sa.add(hgsettings2)
134 self.sa.add(hgsettings2)
135 self.sa.commit()
135 self.sa.commit()
136 set_rhodecode_config(config)
136 set_rhodecode_config(config)
137 h.flash(_('Updated application settings'),
137 h.flash(_('Updated application settings'),
138 category='success')
138 category='success')
139
139
140 except:
140 except:
141 log.error(traceback.format_exc())
141 log.error(traceback.format_exc())
142 h.flash(_('error occurred during updating application settings'),
142 h.flash(_('error occurred during updating application settings'),
143 category='error')
143 category='error')
144
144
145 self.sa.rollback()
145 self.sa.rollback()
146
146
147
147
148 except formencode.Invalid, errors:
148 except formencode.Invalid, errors:
149 return htmlfill.render(
149 return htmlfill.render(
150 render('admin/settings/settings.html'),
150 render('admin/settings/settings.html'),
151 defaults=errors.value,
151 defaults=errors.value,
152 errors=errors.error_dict or {},
152 errors=errors.error_dict or {},
153 prefix_error=False,
153 prefix_error=False,
154 encoding="UTF-8")
154 encoding="UTF-8")
155
155
156 if setting_id == 'mercurial':
156 if setting_id == 'mercurial':
157 application_form = ApplicationUiSettingsForm()()
157 application_form = ApplicationUiSettingsForm()()
158 try:
158 try:
159 form_result = application_form.to_python(dict(request.POST))
159 form_result = application_form.to_python(dict(request.POST))
160
160
161 try:
161 try:
162
162
163 hgsettings1 = self.sa.query(RhodeCodeUi)\
163 hgsettings1 = self.sa.query(RhodeCodeUi)\
164 .filter(RhodeCodeUi.ui_key == 'push_ssl').one()
164 .filter(RhodeCodeUi.ui_key == 'push_ssl').one()
165 hgsettings1.ui_value = form_result['web_push_ssl']
165 hgsettings1.ui_value = form_result['web_push_ssl']
166
166
167 hgsettings2 = self.sa.query(RhodeCodeUi)\
167 hgsettings2 = self.sa.query(RhodeCodeUi)\
168 .filter(RhodeCodeUi.ui_key == '/').one()
168 .filter(RhodeCodeUi.ui_key == '/').one()
169 hgsettings2.ui_value = form_result['paths_root_path']
169 hgsettings2.ui_value = form_result['paths_root_path']
170
170
171
171
172 #HOOKS
172 #HOOKS
173 hgsettings3 = self.sa.query(RhodeCodeUi)\
173 hgsettings3 = self.sa.query(RhodeCodeUi)\
174 .filter(RhodeCodeUi.ui_key == 'changegroup.update').one()
174 .filter(RhodeCodeUi.ui_key == 'changegroup.update').one()
175 hgsettings3.ui_active = bool(form_result['hooks_changegroup_update'])
175 hgsettings3.ui_active = bool(form_result['hooks_changegroup_update'])
176
176
177 hgsettings4 = self.sa.query(RhodeCodeUi)\
177 hgsettings4 = self.sa.query(RhodeCodeUi)\
178 .filter(RhodeCodeUi.ui_key == 'changegroup.repo_size').one()
178 .filter(RhodeCodeUi.ui_key == 'changegroup.repo_size').one()
179 hgsettings4.ui_active = bool(form_result['hooks_changegroup_repo_size'])
179 hgsettings4.ui_active = bool(form_result['hooks_changegroup_repo_size'])
180
180
181 hgsettings5 = self.sa.query(RhodeCodeUi)\
181 hgsettings5 = self.sa.query(RhodeCodeUi)\
182 .filter(RhodeCodeUi.ui_key == 'pretxnchangegroup.push_logger').one()
182 .filter(RhodeCodeUi.ui_key == 'pretxnchangegroup.push_logger').one()
183 hgsettings5.ui_active = bool(form_result['hooks_pretxnchangegroup_push_logger'])
183 hgsettings5.ui_active = bool(form_result['hooks_pretxnchangegroup_push_logger'])
184
184
185 hgsettings6 = self.sa.query(RhodeCodeUi)\
185 hgsettings6 = self.sa.query(RhodeCodeUi)\
186 .filter(RhodeCodeUi.ui_key == 'preoutgoing.pull_logger').one()
186 .filter(RhodeCodeUi.ui_key == 'preoutgoing.pull_logger').one()
187 hgsettings6.ui_active = bool(form_result['hooks_preoutgoing_pull_logger'])
187 hgsettings6.ui_active = bool(form_result['hooks_preoutgoing_pull_logger'])
188
188
189
189
190 self.sa.add(hgsettings1)
190 self.sa.add(hgsettings1)
191 self.sa.add(hgsettings2)
191 self.sa.add(hgsettings2)
192 self.sa.add(hgsettings3)
192 self.sa.add(hgsettings3)
193 self.sa.add(hgsettings4)
193 self.sa.add(hgsettings4)
194 self.sa.add(hgsettings5)
194 self.sa.add(hgsettings5)
195 self.sa.add(hgsettings6)
195 self.sa.add(hgsettings6)
196 self.sa.commit()
196 self.sa.commit()
197
197
198 h.flash(_('Updated mercurial settings'),
198 h.flash(_('Updated mercurial settings'),
199 category='success')
199 category='success')
200
200
201 except:
201 except:
202 log.error(traceback.format_exc())
202 log.error(traceback.format_exc())
203 h.flash(_('error occurred during updating application settings'),
203 h.flash(_('error occurred during updating application settings'),
204 category='error')
204 category='error')
205
205
206 self.sa.rollback()
206 self.sa.rollback()
207
207
208
208
209 except formencode.Invalid, errors:
209 except formencode.Invalid, errors:
210 return htmlfill.render(
210 return htmlfill.render(
211 render('admin/settings/settings.html'),
211 render('admin/settings/settings.html'),
212 defaults=errors.value,
212 defaults=errors.value,
213 errors=errors.error_dict or {},
213 errors=errors.error_dict or {},
214 prefix_error=False,
214 prefix_error=False,
215 encoding="UTF-8")
215 encoding="UTF-8")
216
216
217
217
218
218
219 return redirect(url('admin_settings'))
219 return redirect(url('admin_settings'))
220
220
221 @HasPermissionAllDecorator('hg.admin')
221 @HasPermissionAllDecorator('hg.admin')
222 def delete(self, setting_id):
222 def delete(self, setting_id):
223 """DELETE /admin/settings/setting_id: Delete an existing item"""
223 """DELETE /admin/settings/setting_id: Delete an existing item"""
224 # Forms posted to this method should contain a hidden field:
224 # Forms posted to this method should contain a hidden field:
225 # <input type="hidden" name="_method" value="DELETE" />
225 # <input type="hidden" name="_method" value="DELETE" />
226 # Or using helpers:
226 # Or using helpers:
227 # h.form(url('admin_setting', setting_id=ID),
227 # h.form(url('admin_setting', setting_id=ID),
228 # method='delete')
228 # method='delete')
229 # url('admin_setting', setting_id=ID)
229 # url('admin_setting', setting_id=ID)
230
230
231 @HasPermissionAllDecorator('hg.admin')
231 @HasPermissionAllDecorator('hg.admin')
232 def show(self, setting_id, format='html'):
232 def show(self, setting_id, format='html'):
233 """GET /admin/settings/setting_id: Show a specific item"""
233 """GET /admin/settings/setting_id: Show a specific item"""
234 # url('admin_setting', setting_id=ID)
234 # url('admin_setting', setting_id=ID)
235
235
236 @HasPermissionAllDecorator('hg.admin')
236 @HasPermissionAllDecorator('hg.admin')
237 def edit(self, setting_id, format='html'):
237 def edit(self, setting_id, format='html'):
238 """GET /admin/settings/setting_id/edit: Form to edit an existing item"""
238 """GET /admin/settings/setting_id/edit: Form to edit an existing item"""
239 # url('admin_edit_setting', setting_id=ID)
239 # url('admin_edit_setting', setting_id=ID)
240
240
241 @NotAnonymous()
241 @NotAnonymous()
242 def my_account(self):
242 def my_account(self):
243 """
243 """
244 GET /_admin/my_account Displays info about my account
244 GET /_admin/my_account Displays info about my account
245 """
245 """
246 # url('admin_settings_my_account')
246 # url('admin_settings_my_account')
247
247
248 c.user = UserModel().get(c.rhodecode_user.user_id, cache=False)
248 c.user = UserModel().get(c.rhodecode_user.user_id, cache=False)
249 all_repos = self.sa.query(Repository)\
249 all_repos = self.sa.query(Repository)\
250 .filter(Repository.user_id == c.user.user_id)\
250 .filter(Repository.user_id == c.user.user_id)\
251 .order_by(func.lower(Repository.repo_name))\
251 .order_by(func.lower(Repository.repo_name))\
252 .all()
252 .all()
253
253
254 c.user_repos = ScmModel().get_repos(all_repos)
254 c.user_repos = ScmModel().get_repos(all_repos)
255
255
256 if c.user.username == 'default':
256 if c.user.username == 'default':
257 h.flash(_("You can't edit this user since it's"
257 h.flash(_("You can't edit this user since it's"
258 " crucial for entire application"), category='warning')
258 " crucial for entire application"), category='warning')
259 return redirect(url('users'))
259 return redirect(url('users'))
260
260
261 defaults = c.user.__dict__.copy()
261 defaults = c.user.get_dict()
262 return htmlfill.render(
262 return htmlfill.render(
263 render('admin/users/user_edit_my_account.html'),
263 render('admin/users/user_edit_my_account.html'),
264 defaults=defaults,
264 defaults=defaults,
265 encoding="UTF-8",
265 encoding="UTF-8",
266 force_defaults=False
266 force_defaults=False
267 )
267 )
268
268
269 def my_account_update(self):
269 def my_account_update(self):
270 """PUT /_admin/my_account_update: Update an existing item"""
270 """PUT /_admin/my_account_update: Update an existing item"""
271 # Forms posted to this method should contain a hidden field:
271 # Forms posted to this method should contain a hidden field:
272 # <input type="hidden" name="_method" value="PUT" />
272 # <input type="hidden" name="_method" value="PUT" />
273 # Or using helpers:
273 # Or using helpers:
274 # h.form(url('admin_settings_my_account_update'),
274 # h.form(url('admin_settings_my_account_update'),
275 # method='put')
275 # method='put')
276 # url('admin_settings_my_account_update', id=ID)
276 # url('admin_settings_my_account_update', id=ID)
277 user_model = UserModel()
277 user_model = UserModel()
278 uid = c.rhodecode_user.user_id
278 uid = c.rhodecode_user.user_id
279 _form = UserForm(edit=True, old_data={'user_id':uid,
279 _form = UserForm(edit=True, old_data={'user_id':uid,
280 'email':c.rhodecode_user.email})()
280 'email':c.rhodecode_user.email})()
281 form_result = {}
281 form_result = {}
282 try:
282 try:
283 form_result = _form.to_python(dict(request.POST))
283 form_result = _form.to_python(dict(request.POST))
284 user_model.update_my_account(uid, form_result)
284 user_model.update_my_account(uid, form_result)
285 h.flash(_('Your account was updated successfully'),
285 h.flash(_('Your account was updated successfully'),
286 category='success')
286 category='success')
287
287
288 except formencode.Invalid, errors:
288 except formencode.Invalid, errors:
289 c.user = user_model.get(c.rhodecode_user.user_id, cache=False)
289 c.user = user_model.get(c.rhodecode_user.user_id, cache=False)
290 c.user = UserModel().get(c.rhodecode_user.user_id, cache=False)
290 c.user = UserModel().get(c.rhodecode_user.user_id, cache=False)
291 all_repos = self.sa.query(Repository)\
291 all_repos = self.sa.query(Repository)\
292 .filter(Repository.user_id == c.user.user_id)\
292 .filter(Repository.user_id == c.user.user_id)\
293 .order_by(func.lower(Repository.repo_name))\
293 .order_by(func.lower(Repository.repo_name))\
294 .all()
294 .all()
295 c.user_repos = ScmModel().get_repos(all_repos)
295 c.user_repos = ScmModel().get_repos(all_repos)
296
296
297 return htmlfill.render(
297 return htmlfill.render(
298 render('admin/users/user_edit_my_account.html'),
298 render('admin/users/user_edit_my_account.html'),
299 defaults=errors.value,
299 defaults=errors.value,
300 errors=errors.error_dict or {},
300 errors=errors.error_dict or {},
301 prefix_error=False,
301 prefix_error=False,
302 encoding="UTF-8")
302 encoding="UTF-8")
303 except Exception:
303 except Exception:
304 log.error(traceback.format_exc())
304 log.error(traceback.format_exc())
305 h.flash(_('error occurred during update of user %s') \
305 h.flash(_('error occurred during update of user %s') \
306 % form_result.get('username'), category='error')
306 % form_result.get('username'), category='error')
307
307
308 return redirect(url('my_account'))
308 return redirect(url('my_account'))
309
309
310 @NotAnonymous()
310 @NotAnonymous()
311 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
311 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
312 def create_repository(self):
312 def create_repository(self):
313 """GET /_admin/create_repository: Form to create a new item"""
313 """GET /_admin/create_repository: Form to create a new item"""
314 new_repo = request.GET.get('repo', '')
314 new_repo = request.GET.get('repo', '')
315 c.new_repo = h.repo_name_slug(new_repo)
315 c.new_repo = h.repo_name_slug(new_repo)
316
316
317 return render('admin/repos/repo_add_create_repository.html')
317 return render('admin/repos/repo_add_create_repository.html')
318
318
319 def get_hg_ui_settings(self):
319 def get_hg_ui_settings(self):
320 ret = self.sa.query(RhodeCodeUi).all()
320 ret = self.sa.query(RhodeCodeUi).all()
321
321
322 if not ret:
322 if not ret:
323 raise Exception('Could not get application ui settings !')
323 raise Exception('Could not get application ui settings !')
324 settings = {}
324 settings = {}
325 for each in ret:
325 for each in ret:
326 k = each.ui_key
326 k = each.ui_key
327 v = each.ui_value
327 v = each.ui_value
328 if k == '/':
328 if k == '/':
329 k = 'root_path'
329 k = 'root_path'
330
330
331 if k.find('.') != -1:
331 if k.find('.') != -1:
332 k = k.replace('.', '_')
332 k = k.replace('.', '_')
333
333
334 if each.ui_section == 'hooks':
334 if each.ui_section == 'hooks':
335 v = each.ui_active
335 v = each.ui_active
336
336
337 settings[each.ui_section + '_' + k] = v
337 settings[each.ui_section + '_' + k] = v
338
338
339 return settings
339 return settings
@@ -1,167 +1,167
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # users controller for pylons
3 # users 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 April 4, 2010
21 Created on April 4, 2010
22 users controller for pylons
22 users controller for pylons
23 @author: marcink
23 @author: marcink
24 """
24 """
25
25
26 from formencode import htmlfill
26 from formencode import htmlfill
27 from pylons import request, session, tmpl_context as c, url
27 from pylons import request, session, tmpl_context as c, url
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 rhodecode.lib.exceptions import *
30 from rhodecode.lib.exceptions import *
31 from rhodecode.lib import helpers as h
31 from rhodecode.lib import helpers as h
32 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
32 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
33 from rhodecode.lib.base import BaseController, render
33 from rhodecode.lib.base import BaseController, render
34 from rhodecode.model.db import User
34 from rhodecode.model.db import User
35 from rhodecode.model.forms import UserForm
35 from rhodecode.model.forms import UserForm
36 from rhodecode.model.user import UserModel
36 from rhodecode.model.user import UserModel
37 import formencode
37 import formencode
38 import logging
38 import logging
39 import traceback
39 import traceback
40
40
41 log = logging.getLogger(__name__)
41 log = logging.getLogger(__name__)
42
42
43 class UsersController(BaseController):
43 class UsersController(BaseController):
44 """REST Controller styled on the Atom Publishing Protocol"""
44 """REST Controller styled on the Atom Publishing Protocol"""
45 # To properly map this controller, ensure your config/routing.py
45 # To properly map this controller, ensure your config/routing.py
46 # file has a resource setup:
46 # file has a resource setup:
47 # map.resource('user', 'users')
47 # map.resource('user', 'users')
48
48
49 @LoginRequired()
49 @LoginRequired()
50 @HasPermissionAllDecorator('hg.admin')
50 @HasPermissionAllDecorator('hg.admin')
51 def __before__(self):
51 def __before__(self):
52 c.admin_user = session.get('admin_user')
52 c.admin_user = session.get('admin_user')
53 c.admin_username = session.get('admin_username')
53 c.admin_username = session.get('admin_username')
54 super(UsersController, self).__before__()
54 super(UsersController, self).__before__()
55
55
56
56
57 def index(self, format='html'):
57 def index(self, format='html'):
58 """GET /users: All items in the collection"""
58 """GET /users: All items in the collection"""
59 # url('users')
59 # url('users')
60
60
61 c.users_list = self.sa.query(User).all()
61 c.users_list = self.sa.query(User).all()
62 return render('admin/users/users.html')
62 return render('admin/users/users.html')
63
63
64 def create(self):
64 def create(self):
65 """POST /users: Create a new item"""
65 """POST /users: Create a new item"""
66 # url('users')
66 # url('users')
67
67
68 user_model = UserModel()
68 user_model = UserModel()
69 login_form = UserForm()()
69 login_form = UserForm()()
70 try:
70 try:
71 form_result = login_form.to_python(dict(request.POST))
71 form_result = login_form.to_python(dict(request.POST))
72 user_model.create(form_result)
72 user_model.create(form_result)
73 h.flash(_('created user %s') % form_result['username'],
73 h.flash(_('created user %s') % form_result['username'],
74 category='success')
74 category='success')
75 #action_logger(self.rhodecode_user, 'new_user', '', '', self.sa)
75 #action_logger(self.rhodecode_user, 'new_user', '', '', self.sa)
76 except formencode.Invalid, errors:
76 except formencode.Invalid, errors:
77 return htmlfill.render(
77 return htmlfill.render(
78 render('admin/users/user_add.html'),
78 render('admin/users/user_add.html'),
79 defaults=errors.value,
79 defaults=errors.value,
80 errors=errors.error_dict or {},
80 errors=errors.error_dict or {},
81 prefix_error=False,
81 prefix_error=False,
82 encoding="UTF-8")
82 encoding="UTF-8")
83 except Exception:
83 except Exception:
84 log.error(traceback.format_exc())
84 log.error(traceback.format_exc())
85 h.flash(_('error occured during creation of user %s') \
85 h.flash(_('error occured during creation of user %s') \
86 % request.POST.get('username'), category='error')
86 % request.POST.get('username'), category='error')
87 return redirect(url('users'))
87 return redirect(url('users'))
88
88
89 def new(self, format='html'):
89 def new(self, format='html'):
90 """GET /users/new: Form to create a new item"""
90 """GET /users/new: Form to create a new item"""
91 # url('new_user')
91 # url('new_user')
92 return render('admin/users/user_add.html')
92 return render('admin/users/user_add.html')
93
93
94 def update(self, id):
94 def update(self, id):
95 """PUT /users/id: Update an existing item"""
95 """PUT /users/id: Update an existing item"""
96 # Forms posted to this method should contain a hidden field:
96 # Forms posted to this method should contain a hidden field:
97 # <input type="hidden" name="_method" value="PUT" />
97 # <input type="hidden" name="_method" value="PUT" />
98 # Or using helpers:
98 # Or using helpers:
99 # h.form(url('user', id=ID),
99 # h.form(url('user', id=ID),
100 # method='put')
100 # method='put')
101 # url('user', id=ID)
101 # url('user', id=ID)
102 user_model = UserModel()
102 user_model = UserModel()
103 c.user = user_model.get(id)
103 c.user = user_model.get(id)
104
104
105 _form = UserForm(edit=True, old_data={'user_id':id,
105 _form = UserForm(edit=True, old_data={'user_id':id,
106 'email':c.user.email})()
106 'email':c.user.email})()
107 form_result = {}
107 form_result = {}
108 try:
108 try:
109 form_result = _form.to_python(dict(request.POST))
109 form_result = _form.to_python(dict(request.POST))
110 user_model.update(id, form_result)
110 user_model.update(id, form_result)
111 h.flash(_('User updated succesfully'), category='success')
111 h.flash(_('User updated succesfully'), category='success')
112
112
113 except formencode.Invalid, errors:
113 except formencode.Invalid, errors:
114 return htmlfill.render(
114 return htmlfill.render(
115 render('admin/users/user_edit.html'),
115 render('admin/users/user_edit.html'),
116 defaults=errors.value,
116 defaults=errors.value,
117 errors=errors.error_dict or {},
117 errors=errors.error_dict or {},
118 prefix_error=False,
118 prefix_error=False,
119 encoding="UTF-8")
119 encoding="UTF-8")
120 except Exception:
120 except Exception:
121 log.error(traceback.format_exc())
121 log.error(traceback.format_exc())
122 h.flash(_('error occured during update of user %s') \
122 h.flash(_('error occured during update of user %s') \
123 % form_result.get('username'), category='error')
123 % form_result.get('username'), category='error')
124
124
125 return redirect(url('users'))
125 return redirect(url('users'))
126
126
127 def delete(self, id):
127 def delete(self, id):
128 """DELETE /users/id: Delete an existing item"""
128 """DELETE /users/id: Delete an existing item"""
129 # Forms posted to this method should contain a hidden field:
129 # Forms posted to this method should contain a hidden field:
130 # <input type="hidden" name="_method" value="DELETE" />
130 # <input type="hidden" name="_method" value="DELETE" />
131 # Or using helpers:
131 # Or using helpers:
132 # h.form(url('user', id=ID),
132 # h.form(url('user', id=ID),
133 # method='delete')
133 # method='delete')
134 # url('user', id=ID)
134 # url('user', id=ID)
135 user_model = UserModel()
135 user_model = UserModel()
136 try:
136 try:
137 user_model.delete(id)
137 user_model.delete(id)
138 h.flash(_('sucessfully deleted user'), category='success')
138 h.flash(_('sucessfully deleted user'), category='success')
139 except (UserOwnsReposException, DefaultUserException), e:
139 except (UserOwnsReposException, DefaultUserException), e:
140 h.flash(str(e), category='warning')
140 h.flash(str(e), category='warning')
141 except Exception:
141 except Exception:
142 h.flash(_('An error occured during deletion of user'),
142 h.flash(_('An error occured during deletion of user'),
143 category='error')
143 category='error')
144 return redirect(url('users'))
144 return redirect(url('users'))
145
145
146 def show(self, id, format='html'):
146 def show(self, id, format='html'):
147 """GET /users/id: Show a specific item"""
147 """GET /users/id: Show a specific item"""
148 # url('user', id=ID)
148 # url('user', id=ID)
149
149
150
150
151 def edit(self, id, format='html'):
151 def edit(self, id, format='html'):
152 """GET /users/id/edit: Form to edit an existing item"""
152 """GET /users/id/edit: Form to edit an existing item"""
153 # url('edit_user', id=ID)
153 # url('edit_user', id=ID)
154 c.user = self.sa.query(User).get(id)
154 c.user = self.sa.query(User).get(id)
155 if not c.user:
155 if not c.user:
156 return redirect(url('users'))
156 return redirect(url('users'))
157 if c.user.username == 'default':
157 if c.user.username == 'default':
158 h.flash(_("You can't edit this user"), category='warning')
158 h.flash(_("You can't edit this user"), category='warning')
159 return redirect(url('users'))
159 return redirect(url('users'))
160
160
161 defaults = c.user.__dict__.copy()
161 defaults = c.user.get_dict()
162 return htmlfill.render(
162 return htmlfill.render(
163 render('admin/users/user_edit.html'),
163 render('admin/users/user_edit.html'),
164 defaults=defaults,
164 defaults=defaults,
165 encoding="UTF-8",
165 encoding="UTF-8",
166 force_defaults=False
166 force_defaults=False
167 )
167 )
@@ -1,178 +1,178
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 June 30, 2010
21 Created on June 30, 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 tmpl_context as c, request, url
26 from pylons import tmpl_context as c, request, url
27 from pylons.controllers.util import redirect
27 from pylons.controllers.util import redirect
28 from pylons.i18n.translation import _
28 from pylons.i18n.translation import _
29 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAllDecorator
29 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAllDecorator
30 from rhodecode.lib.base import BaseController, render
30 from rhodecode.lib.base import BaseController, render
31 from rhodecode.lib.utils import invalidate_cache, action_logger
31 from rhodecode.lib.utils import invalidate_cache, action_logger
32 from rhodecode.model.forms import RepoSettingsForm, RepoForkForm
32 from rhodecode.model.forms import RepoSettingsForm, RepoForkForm
33 from rhodecode.model.repo import RepoModel
33 from rhodecode.model.repo import RepoModel
34 import formencode
34 import formencode
35 import logging
35 import logging
36 import rhodecode.lib.helpers as h
36 import rhodecode.lib.helpers as h
37 import traceback
37 import traceback
38
38
39 log = logging.getLogger(__name__)
39 log = logging.getLogger(__name__)
40
40
41 class SettingsController(BaseController):
41 class SettingsController(BaseController):
42
42
43 @LoginRequired()
43 @LoginRequired()
44 @HasRepoPermissionAllDecorator('repository.admin')
44 @HasRepoPermissionAllDecorator('repository.admin')
45 def __before__(self):
45 def __before__(self):
46 super(SettingsController, self).__before__()
46 super(SettingsController, self).__before__()
47
47
48 def index(self, repo_name):
48 def index(self, repo_name):
49 repo_model = RepoModel()
49 repo_model = RepoModel()
50 c.repo_info = repo = repo_model.get_by_repo_name(repo_name)
50 c.repo_info = repo = repo_model.get_by_repo_name(repo_name)
51 if not repo:
51 if not repo:
52 h.flash(_('%s repository is not mapped to db perhaps'
52 h.flash(_('%s repository is not mapped to db perhaps'
53 ' it was created or renamed from the filesystem'
53 ' it was created or renamed from the filesystem'
54 ' please run the application again'
54 ' please run the application again'
55 ' in order to rescan repositories') % repo_name,
55 ' in order to rescan repositories') % repo_name,
56 category='error')
56 category='error')
57
57
58 return redirect(url('home'))
58 return redirect(url('home'))
59 defaults = c.repo_info.__dict__.copy()
59 defaults = c.repo_info.get_dict()
60 defaults.update({'user':c.repo_info.user.username})
60 defaults.update({'user':c.repo_info.user.username})
61 c.users_array = repo_model.get_users_js()
61 c.users_array = repo_model.get_users_js()
62
62
63 for p in c.repo_info.repo_to_perm:
63 for p in c.repo_info.repo_to_perm:
64 defaults.update({'perm_%s' % p.user.username:
64 defaults.update({'perm_%s' % p.user.username:
65 p.permission.permission_name})
65 p.permission.permission_name})
66
66
67 return htmlfill.render(
67 return htmlfill.render(
68 render('settings/repo_settings.html'),
68 render('settings/repo_settings.html'),
69 defaults=defaults,
69 defaults=defaults,
70 encoding="UTF-8",
70 encoding="UTF-8",
71 force_defaults=False
71 force_defaults=False
72 )
72 )
73
73
74 def update(self, repo_name):
74 def update(self, repo_name):
75 repo_model = RepoModel()
75 repo_model = RepoModel()
76 changed_name = repo_name
76 changed_name = repo_name
77 _form = RepoSettingsForm(edit=True, old_data={'repo_name':repo_name})()
77 _form = RepoSettingsForm(edit=True, old_data={'repo_name':repo_name})()
78 try:
78 try:
79 form_result = _form.to_python(dict(request.POST))
79 form_result = _form.to_python(dict(request.POST))
80 repo_model.update(repo_name, form_result)
80 repo_model.update(repo_name, form_result)
81 invalidate_cache('get_repo_cached_%s' % repo_name)
81 invalidate_cache('get_repo_cached_%s' % repo_name)
82 h.flash(_('Repository %s updated successfully' % repo_name),
82 h.flash(_('Repository %s updated successfully' % repo_name),
83 category='success')
83 category='success')
84 changed_name = form_result['repo_name']
84 changed_name = form_result['repo_name']
85 action_logger(self.rhodecode_user, 'user_updated_repo',
85 action_logger(self.rhodecode_user, 'user_updated_repo',
86 changed_name, '', self.sa)
86 changed_name, '', self.sa)
87 except formencode.Invalid, errors:
87 except formencode.Invalid, errors:
88 c.repo_info = repo_model.get_by_repo_name(repo_name)
88 c.repo_info = repo_model.get_by_repo_name(repo_name)
89 c.users_array = repo_model.get_users_js()
89 c.users_array = repo_model.get_users_js()
90 errors.value.update({'user':c.repo_info.user.username})
90 errors.value.update({'user':c.repo_info.user.username})
91 return htmlfill.render(
91 return htmlfill.render(
92 render('settings/repo_settings.html'),
92 render('settings/repo_settings.html'),
93 defaults=errors.value,
93 defaults=errors.value,
94 errors=errors.error_dict or {},
94 errors=errors.error_dict or {},
95 prefix_error=False,
95 prefix_error=False,
96 encoding="UTF-8")
96 encoding="UTF-8")
97 except Exception:
97 except Exception:
98 log.error(traceback.format_exc())
98 log.error(traceback.format_exc())
99 h.flash(_('error occurred during update of repository %s') \
99 h.flash(_('error occurred during update of repository %s') \
100 % repo_name, category='error')
100 % repo_name, category='error')
101
101
102 return redirect(url('repo_settings_home', repo_name=changed_name))
102 return redirect(url('repo_settings_home', repo_name=changed_name))
103
103
104
104
105
105
106 def delete(self, repo_name):
106 def delete(self, repo_name):
107 """DELETE /repos/repo_name: Delete an existing item"""
107 """DELETE /repos/repo_name: Delete an existing item"""
108 # Forms posted to this method should contain a hidden field:
108 # Forms posted to this method should contain a hidden field:
109 # <input type="hidden" name="_method" value="DELETE" />
109 # <input type="hidden" name="_method" value="DELETE" />
110 # Or using helpers:
110 # Or using helpers:
111 # h.form(url('repo_settings_delete', repo_name=ID),
111 # h.form(url('repo_settings_delete', repo_name=ID),
112 # method='delete')
112 # method='delete')
113 # url('repo_settings_delete', repo_name=ID)
113 # url('repo_settings_delete', repo_name=ID)
114
114
115 repo_model = RepoModel()
115 repo_model = RepoModel()
116 repo = repo_model.get_by_repo_name(repo_name)
116 repo = repo_model.get_by_repo_name(repo_name)
117 if not repo:
117 if not repo:
118 h.flash(_('%s repository is not mapped to db perhaps'
118 h.flash(_('%s repository is not mapped to db perhaps'
119 ' it was moved or renamed from the filesystem'
119 ' it was moved or renamed from the filesystem'
120 ' please run the application again'
120 ' please run the application again'
121 ' in order to rescan repositories') % repo_name,
121 ' in order to rescan repositories') % repo_name,
122 category='error')
122 category='error')
123
123
124 return redirect(url('home'))
124 return redirect(url('home'))
125 try:
125 try:
126 action_logger(self.rhodecode_user, 'user_deleted_repo',
126 action_logger(self.rhodecode_user, 'user_deleted_repo',
127 repo_name, '', self.sa)
127 repo_name, '', self.sa)
128 repo_model.delete(repo)
128 repo_model.delete(repo)
129 invalidate_cache('get_repo_cached_%s' % repo_name)
129 invalidate_cache('get_repo_cached_%s' % repo_name)
130 h.flash(_('deleted repository %s') % repo_name, category='success')
130 h.flash(_('deleted repository %s') % repo_name, category='success')
131 except Exception:
131 except Exception:
132 h.flash(_('An error occurred during deletion of %s') % repo_name,
132 h.flash(_('An error occurred during deletion of %s') % repo_name,
133 category='error')
133 category='error')
134
134
135 return redirect(url('home'))
135 return redirect(url('home'))
136
136
137 def fork(self, repo_name):
137 def fork(self, repo_name):
138 repo_model = RepoModel()
138 repo_model = RepoModel()
139 c.repo_info = repo = repo_model.get_by_repo_name(repo_name)
139 c.repo_info = repo = repo_model.get_by_repo_name(repo_name)
140 if not repo:
140 if not repo:
141 h.flash(_('%s repository is not mapped to db perhaps'
141 h.flash(_('%s repository is not mapped to db perhaps'
142 ' it was created or renamed from the filesystem'
142 ' it was created or renamed from the filesystem'
143 ' please run the application again'
143 ' please run the application again'
144 ' in order to rescan repositories') % repo_name,
144 ' in order to rescan repositories') % repo_name,
145 category='error')
145 category='error')
146
146
147 return redirect(url('home'))
147 return redirect(url('home'))
148
148
149 return render('settings/repo_fork.html')
149 return render('settings/repo_fork.html')
150
150
151
151
152
152
153 def fork_create(self, repo_name):
153 def fork_create(self, repo_name):
154 repo_model = RepoModel()
154 repo_model = RepoModel()
155 c.repo_info = repo_model.get_by_repo_name(repo_name)
155 c.repo_info = repo_model.get_by_repo_name(repo_name)
156 _form = RepoForkForm(old_data={'repo_type':c.repo_info.repo_type})()
156 _form = RepoForkForm(old_data={'repo_type':c.repo_info.repo_type})()
157 form_result = {}
157 form_result = {}
158 try:
158 try:
159 form_result = _form.to_python(dict(request.POST))
159 form_result = _form.to_python(dict(request.POST))
160 form_result.update({'repo_name':repo_name})
160 form_result.update({'repo_name':repo_name})
161 repo_model.create_fork(form_result, c.rhodecode_user)
161 repo_model.create_fork(form_result, c.rhodecode_user)
162 h.flash(_('forked %s repository as %s') \
162 h.flash(_('forked %s repository as %s') \
163 % (repo_name, form_result['fork_name']),
163 % (repo_name, form_result['fork_name']),
164 category='success')
164 category='success')
165 action_logger(self.rhodecode_user,
165 action_logger(self.rhodecode_user,
166 'user_forked_repo:%s' % form_result['fork_name'],
166 'user_forked_repo:%s' % form_result['fork_name'],
167 repo_name, '', self.sa)
167 repo_name, '', self.sa)
168 except formencode.Invalid, errors:
168 except formencode.Invalid, errors:
169 c.new_repo = errors.value['fork_name']
169 c.new_repo = errors.value['fork_name']
170 r = render('settings/repo_fork.html')
170 r = render('settings/repo_fork.html')
171
171
172 return htmlfill.render(
172 return htmlfill.render(
173 r,
173 r,
174 defaults=errors.value,
174 defaults=errors.value,
175 errors=errors.error_dict or {},
175 errors=errors.error_dict or {},
176 prefix_error=False,
176 prefix_error=False,
177 encoding="UTF-8")
177 encoding="UTF-8")
178 return redirect(url('home'))
178 return redirect(url('home'))
@@ -1,216 +1,248
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 package.rhodecode.model.db
3 rhodecode.model.db
4 ~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~
5
5
6 Database Models for RhodeCode
6 Database Models for RhodeCode
7 :created_on: Apr 08, 2010
7 :created_on: Apr 08, 2010
8 :author: marcink
8 :author: marcink
9 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
9 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :license: GPLv3, see COPYING for more details.
10 :license: GPLv3, see COPYING for more details.
11 """
11 """
12 # This program is free software; you can redistribute it and/or
12 # This program is free software; you can redistribute it and/or
13 # modify it under the terms of the GNU General Public License
13 # modify it under the terms of the GNU General Public License
14 # as published by the Free Software Foundation; version 2
14 # as published by the Free Software Foundation; version 2
15 # of the License or (at your opinion) any later version of the license.
15 # of the License or (at your opinion) any later version of the license.
16 #
16 #
17 # This program is distributed in the hope that it will be useful,
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
20 # GNU General Public License for more details.
21 #
21 #
22 # You should have received a copy of the GNU General Public License
22 # You should have received a copy of the GNU General Public License
23 # along with this program; if not, write to the Free Software
23 # along with this program; if not, write to the Free Software
24 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
24 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # MA 02110-1301, USA.
25 # MA 02110-1301, USA.
26 import logging
26 import logging
27 import datetime
27 import datetime
28
28
29 from sqlalchemy import *
29 from sqlalchemy import *
30 from sqlalchemy.exc import DatabaseError
30 from sqlalchemy.exc import DatabaseError
31 from sqlalchemy.orm import relation, backref
31 from sqlalchemy.orm import relation, backref, class_mapper
32 from sqlalchemy.orm.session import Session
32 from sqlalchemy.orm.session import Session
33
33
34 from rhodecode.model.meta import Base
34 from rhodecode.model.meta import Base
35
35
36 log = logging.getLogger(__name__)
36 log = logging.getLogger(__name__)
37
37
38 class RhodeCodeSettings(Base):
38 class BaseModel(object):
39
40 @classmethod
41 def _get_keys(cls):
42 """return column names for this model """
43 return class_mapper(cls).c.keys()
44
45 def get_dict(self):
46 """return dict with keys and values corresponding
47 to this model data """
48
49 d = {}
50 for k in self._get_keys():
51 d[k] = getattr(self, k)
52 return d
53
54 def get_appstruct(self):
55 """return list with keys and values tupples corresponding
56 to this model data """
57
58 l = []
59 for k in self._get_keys():
60 l.append((k, getattr(self, k),))
61 return l
62
63 def populate_obj(self, populate_dict):
64 """populate model with data from given populate_dict"""
65
66 for k in self._get_keys():
67 if k in populate_dict:
68 setattr(self, k, populate_dict[k])
69
70 class RhodeCodeSettings(Base, BaseModel):
39 __tablename__ = 'rhodecode_settings'
71 __tablename__ = 'rhodecode_settings'
40 __table_args__ = (UniqueConstraint('app_settings_name'), {'useexisting':True})
72 __table_args__ = (UniqueConstraint('app_settings_name'), {'useexisting':True})
41 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
73 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
42 app_settings_name = Column("app_settings_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
74 app_settings_name = Column("app_settings_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
43 app_settings_value = Column("app_settings_value", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
75 app_settings_value = Column("app_settings_value", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
44
76
45 def __init__(self, k, v):
77 def __init__(self, k, v):
46 self.app_settings_name = k
78 self.app_settings_name = k
47 self.app_settings_value = v
79 self.app_settings_value = v
48
80
49 def __repr__(self):
81 def __repr__(self):
50 return "<RhodeCodeSetting('%s:%s')>" % (self.app_settings_name,
82 return "<RhodeCodeSetting('%s:%s')>" % (self.app_settings_name,
51 self.app_settings_value)
83 self.app_settings_value)
52
84
53 class RhodeCodeUi(Base):
85 class RhodeCodeUi(Base, BaseModel):
54 __tablename__ = 'rhodecode_ui'
86 __tablename__ = 'rhodecode_ui'
55 __table_args__ = {'useexisting':True}
87 __table_args__ = {'useexisting':True}
56 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
88 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
57 ui_section = Column("ui_section", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
89 ui_section = Column("ui_section", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
58 ui_key = Column("ui_key", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
90 ui_key = Column("ui_key", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
59 ui_value = Column("ui_value", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
91 ui_value = Column("ui_value", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
60 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
92 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
61
93
62
94
63 class User(Base):
95 class User(Base, BaseModel):
64 __tablename__ = 'users'
96 __tablename__ = 'users'
65 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'useexisting':True})
97 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'useexisting':True})
66 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
98 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
67 username = Column("username", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
99 username = Column("username", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
68 password = Column("password", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
100 password = Column("password", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
69 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
101 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
70 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
102 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
71 name = Column("name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
103 name = Column("name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
72 lastname = Column("lastname", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
104 lastname = Column("lastname", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
73 email = Column("email", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
105 email = Column("email", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
74 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
106 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
75 is_ldap = Column("is_ldap", Boolean(), nullable=False, unique=None, default=False)
107 is_ldap = Column("is_ldap", Boolean(), nullable=False, unique=None, default=False)
76
108
77 user_log = relation('UserLog', cascade='all')
109 user_log = relation('UserLog', cascade='all')
78 user_perms = relation('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
110 user_perms = relation('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
79
111
80 repositories = relation('Repository')
112 repositories = relation('Repository')
81 user_followers = relation('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
113 user_followers = relation('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
82
114
83 @property
115 @property
84 def full_contact(self):
116 def full_contact(self):
85 return '%s %s <%s>' % (self.name, self.lastname, self.email)
117 return '%s %s <%s>' % (self.name, self.lastname, self.email)
86
118
87 def __repr__(self):
119 def __repr__(self):
88 return "<User('id:%s:%s')>" % (self.user_id, self.username)
120 return "<User('id:%s:%s')>" % (self.user_id, self.username)
89
121
90 def update_lastlogin(self):
122 def update_lastlogin(self):
91 """Update user lastlogin"""
123 """Update user lastlogin"""
92
124
93 try:
125 try:
94 session = Session.object_session(self)
126 session = Session.object_session(self)
95 self.last_login = datetime.datetime.now()
127 self.last_login = datetime.datetime.now()
96 session.add(self)
128 session.add(self)
97 session.commit()
129 session.commit()
98 log.debug('updated user %s lastlogin', self.username)
130 log.debug('updated user %s lastlogin', self.username)
99 except (DatabaseError,):
131 except (DatabaseError,):
100 session.rollback()
132 session.rollback()
101
133
102
134
103 class UserLog(Base):
135 class UserLog(Base, BaseModel):
104 __tablename__ = 'user_logs'
136 __tablename__ = 'user_logs'
105 __table_args__ = {'useexisting':True}
137 __table_args__ = {'useexisting':True}
106 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
138 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
107 user_id = Column("user_id", Integer(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
139 user_id = Column("user_id", Integer(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
108 repository_id = Column("repository_id", Integer(length=None, convert_unicode=False, assert_unicode=None), ForeignKey(u'repositories.repo_id'), nullable=False, unique=None, default=None)
140 repository_id = Column("repository_id", Integer(length=None, convert_unicode=False, assert_unicode=None), ForeignKey(u'repositories.repo_id'), nullable=False, unique=None, default=None)
109 repository_name = Column("repository_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
141 repository_name = Column("repository_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
110 user_ip = Column("user_ip", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
142 user_ip = Column("user_ip", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
111 action = Column("action", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
143 action = Column("action", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
112 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
144 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
113
145
114 user = relation('User')
146 user = relation('User')
115 repository = relation('Repository')
147 repository = relation('Repository')
116
148
117 class Repository(Base):
149 class Repository(Base, BaseModel):
118 __tablename__ = 'repositories'
150 __tablename__ = 'repositories'
119 __table_args__ = (UniqueConstraint('repo_name'), {'useexisting':True},)
151 __table_args__ = (UniqueConstraint('repo_name'), {'useexisting':True},)
120 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
152 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
121 repo_name = Column("repo_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
153 repo_name = Column("repo_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
122 repo_type = Column("repo_type", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
154 repo_type = Column("repo_type", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
123 user_id = Column("user_id", Integer(), ForeignKey(u'users.user_id'), nullable=False, unique=False, default=None)
155 user_id = Column("user_id", Integer(), ForeignKey(u'users.user_id'), nullable=False, unique=False, default=None)
124 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
156 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
125 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
157 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
126 description = Column("description", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
158 description = Column("description", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
127 fork_id = Column("fork_id", Integer(), ForeignKey(u'repositories.repo_id'), nullable=True, unique=False, default=None)
159 fork_id = Column("fork_id", Integer(), ForeignKey(u'repositories.repo_id'), nullable=True, unique=False, default=None)
128
160
129 user = relation('User')
161 user = relation('User')
130 fork = relation('Repository', remote_side=repo_id)
162 fork = relation('Repository', remote_side=repo_id)
131 repo_to_perm = relation('RepoToPerm', cascade='all')
163 repo_to_perm = relation('RepoToPerm', cascade='all')
132 stats = relation('Statistics', cascade='all', uselist=False)
164 stats = relation('Statistics', cascade='all', uselist=False)
133
165
134 repo_followers = relation('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
166 repo_followers = relation('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
135
167
136
168
137 def __repr__(self):
169 def __repr__(self):
138 return "<Repository('%s:%s')>" % (self.repo_id, self.repo_name)
170 return "<Repository('%s:%s')>" % (self.repo_id, self.repo_name)
139
171
140 class Permission(Base):
172 class Permission(Base, BaseModel):
141 __tablename__ = 'permissions'
173 __tablename__ = 'permissions'
142 __table_args__ = {'useexisting':True}
174 __table_args__ = {'useexisting':True}
143 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
175 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
144 permission_name = Column("permission_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
176 permission_name = Column("permission_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
145 permission_longname = Column("permission_longname", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
177 permission_longname = Column("permission_longname", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
146
178
147 def __repr__(self):
179 def __repr__(self):
148 return "<Permission('%s:%s')>" % (self.permission_id, self.permission_name)
180 return "<Permission('%s:%s')>" % (self.permission_id, self.permission_name)
149
181
150 class RepoToPerm(Base):
182 class RepoToPerm(Base, BaseModel):
151 __tablename__ = 'repo_to_perm'
183 __tablename__ = 'repo_to_perm'
152 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'useexisting':True})
184 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'useexisting':True})
153 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
185 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
154 user_id = Column("user_id", Integer(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
186 user_id = Column("user_id", Integer(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
155 permission_id = Column("permission_id", Integer(), ForeignKey(u'permissions.permission_id'), nullable=False, unique=None, default=None)
187 permission_id = Column("permission_id", Integer(), ForeignKey(u'permissions.permission_id'), nullable=False, unique=None, default=None)
156 repository_id = Column("repository_id", Integer(), ForeignKey(u'repositories.repo_id'), nullable=False, unique=None, default=None)
188 repository_id = Column("repository_id", Integer(), ForeignKey(u'repositories.repo_id'), nullable=False, unique=None, default=None)
157
189
158 user = relation('User')
190 user = relation('User')
159 permission = relation('Permission')
191 permission = relation('Permission')
160 repository = relation('Repository')
192 repository = relation('Repository')
161
193
162 class UserToPerm(Base):
194 class UserToPerm(Base, BaseModel):
163 __tablename__ = 'user_to_perm'
195 __tablename__ = 'user_to_perm'
164 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'useexisting':True})
196 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'useexisting':True})
165 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
197 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
166 user_id = Column("user_id", Integer(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
198 user_id = Column("user_id", Integer(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
167 permission_id = Column("permission_id", Integer(), ForeignKey(u'permissions.permission_id'), nullable=False, unique=None, default=None)
199 permission_id = Column("permission_id", Integer(), ForeignKey(u'permissions.permission_id'), nullable=False, unique=None, default=None)
168
200
169 user = relation('User')
201 user = relation('User')
170 permission = relation('Permission')
202 permission = relation('Permission')
171
203
172 class Statistics(Base):
204 class Statistics(Base, BaseModel):
173 __tablename__ = 'statistics'
205 __tablename__ = 'statistics'
174 __table_args__ = (UniqueConstraint('repository_id'), {'useexisting':True})
206 __table_args__ = (UniqueConstraint('repository_id'), {'useexisting':True})
175 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
207 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
176 repository_id = Column("repository_id", Integer(), ForeignKey(u'repositories.repo_id'), nullable=False, unique=True, default=None)
208 repository_id = Column("repository_id", Integer(), ForeignKey(u'repositories.repo_id'), nullable=False, unique=True, default=None)
177 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
209 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
178 commit_activity = Column("commit_activity", LargeBinary(), nullable=False)#JSON data
210 commit_activity = Column("commit_activity", LargeBinary(), nullable=False)#JSON data
179 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
211 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
180 languages = Column("languages", LargeBinary(), nullable=False)#JSON data
212 languages = Column("languages", LargeBinary(), nullable=False)#JSON data
181
213
182 repository = relation('Repository', single_parent=True)
214 repository = relation('Repository', single_parent=True)
183
215
184 class UserFollowing(Base):
216 class UserFollowing(Base, BaseModel):
185 __tablename__ = 'user_followings'
217 __tablename__ = 'user_followings'
186 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
218 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
187 UniqueConstraint('user_id', 'follows_user_id')
219 UniqueConstraint('user_id', 'follows_user_id')
188 , {'useexisting':True})
220 , {'useexisting':True})
189
221
190 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
222 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
191 user_id = Column("user_id", Integer(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
223 user_id = Column("user_id", Integer(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
192 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey(u'repositories.repo_id'), nullable=True, unique=None, default=None)
224 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey(u'repositories.repo_id'), nullable=True, unique=None, default=None)
193 follows_user_id = Column("follows_user_id", Integer(), ForeignKey(u'users.user_id'), nullable=True, unique=None, default=None)
225 follows_user_id = Column("follows_user_id", Integer(), ForeignKey(u'users.user_id'), nullable=True, unique=None, default=None)
194
226
195 user = relation('User', primaryjoin='User.user_id==UserFollowing.user_id')
227 user = relation('User', primaryjoin='User.user_id==UserFollowing.user_id')
196
228
197 follows_user = relation('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
229 follows_user = relation('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
198 follows_repository = relation('Repository')
230 follows_repository = relation('Repository')
199
231
200
232
201 class CacheInvalidation(Base):
233 class CacheInvalidation(Base, BaseModel):
202 __tablename__ = 'cache_invalidation'
234 __tablename__ = 'cache_invalidation'
203 __table_args__ = (UniqueConstraint('cache_key'), {'useexisting':True})
235 __table_args__ = (UniqueConstraint('cache_key'), {'useexisting':True})
204 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
236 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
205 cache_key = Column("cache_key", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
237 cache_key = Column("cache_key", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
206 cache_args = Column("cache_args", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
238 cache_args = Column("cache_args", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
207 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
239 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
208
240
209
241
210 def __init__(self, cache_key, cache_args=''):
242 def __init__(self, cache_key, cache_args=''):
211 self.cache_key = cache_key
243 self.cache_key = cache_key
212 self.cache_args = cache_args
244 self.cache_args = cache_args
213 self.cache_active = False
245 self.cache_active = False
214
246
215 def __repr__(self):
247 def __repr__(self):
216 return "<CacheInvalidation('%s:%s')>" % (self.cache_id, self.cache_key)
248 return "<CacheInvalidation('%s:%s')>" % (self.cache_id, self.cache_key)
General Comments 0
You need to be logged in to leave comments. Login now