##// END OF EJS Templates
merge
marcink -
r830:e46d25e5 merge beta
parent child Browse files
Show More
@@ -1,312 +1,313 b''
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 c.repo_info = repo_model.get_by_repo_name(repo_name)
268 c.repo_info = repo_model.get_by_repo_name(repo_name)
269 r = ScmModel().get(repo_name)
270
269 if c.repo_info.stats:
271 if c.repo_info.stats:
270 last_rev = c.repo_info.stats.stat_on_revision
272 last_rev = c.repo_info.stats.stat_on_revision
271 else:
273 else:
272 last_rev = 0
274 last_rev = 0
273 c.stats_revision = last_rev
275 c.stats_revision = last_rev
274 r = ScmModel().get(repo_name)
276
275 c.repo_last_rev = r.revisions[-1] if r.revisions else 0
277 c.repo_last_rev = r.revisions[-1] if r.revisions else 0
276
278
277 if last_rev == 0:
279 if last_rev == 0:
278 c.stats_percentage = 0
280 c.stats_percentage = 0
279 else:
281 else:
280 c.stats_percentage = '%.2f' % ((float((last_rev)) /
282 c.stats_percentage = '%.2f' % ((float((last_rev)) /
281 c.repo_last_rev) * 100)
283 c.repo_last_rev) * 100)
282
284
283
284 if not c.repo_info:
285 if not c.repo_info:
285 h.flash(_('%s repository is not mapped to db perhaps'
286 h.flash(_('%s repository is not mapped to db perhaps'
286 ' it was created or renamed from the filesystem'
287 ' it was created or renamed from the filesystem'
287 ' please run the application again'
288 ' please run the application again'
288 ' in order to rescan repositories') % repo_name,
289 ' in order to rescan repositories') % repo_name,
289 category='error')
290 category='error')
290
291
291 return redirect(url('repos'))
292 return redirect(url('repos'))
292
293
293 defaults = c.repo_info.__dict__
294 defaults = c.repo_info.__dict__.copy()
294 if c.repo_info.user:
295 if c.repo_info.user:
295 defaults.update({'user':c.repo_info.user.username})
296 defaults.update({'user':c.repo_info.user.username})
296 else:
297 else:
297 replacement_user = self.sa.query(User)\
298 replacement_user = self.sa.query(User)\
298 .filter(User.admin == True).first().username
299 .filter(User.admin == True).first().username
299 defaults.update({'user':replacement_user})
300 defaults.update({'user':replacement_user})
300
301
301 c.users_array = repo_model.get_users_js()
302 c.users_array = repo_model.get_users_js()
302
303
303 for p in c.repo_info.repo_to_perm:
304 for p in c.repo_info.repo_to_perm:
304 defaults.update({'perm_%s' % p.user.username:
305 defaults.update({'perm_%s' % p.user.username:
305 p.permission.permission_name})
306 p.permission.permission_name})
306
307
307 return htmlfill.render(
308 return htmlfill.render(
308 render('admin/repos/repo_edit.html'),
309 render('admin/repos/repo_edit.html'),
309 defaults=defaults,
310 defaults=defaults,
310 encoding="UTF-8",
311 encoding="UTF-8",
311 force_defaults=False
312 force_defaults=False
312 )
313 )
@@ -1,339 +1,339 b''
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__
261 defaults = c.user.__dict__.copy()
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 b''
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__
161 defaults = c.user.__dict__.copy()
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 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # settings controller for pylons
3 # settings controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on 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__
59 defaults = c.repo_info.__dict__.copy()
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,383 +1,377 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.scm
3 rhodecode.model.scm
4 ~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~
5
5
6 Scm model for RhodeCode
6 Scm model for RhodeCode
7
7
8 :created_on: Apr 9, 2010
8 :created_on: Apr 9, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 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 import os
27 import os
28 import time
28 import time
29 import traceback
29 import traceback
30 import logging
30 import logging
31
31
32 from vcs import get_backend
32 from vcs import get_backend
33 from vcs.utils.helpers import get_scm
33 from vcs.utils.helpers import get_scm
34 from vcs.exceptions import RepositoryError, VCSError
34 from vcs.exceptions import RepositoryError, VCSError
35 from vcs.utils.lazy import LazyProperty
35 from vcs.utils.lazy import LazyProperty
36
36
37 from mercurial import ui
37 from mercurial import ui
38
38
39 from beaker.cache import cache_region, region_invalidate
39 from beaker.cache import cache_region, region_invalidate
40
40
41 from rhodecode import BACKENDS
41 from rhodecode import BACKENDS
42 from rhodecode.lib import helpers as h
42 from rhodecode.lib import helpers as h
43 from rhodecode.lib.auth import HasRepoPermissionAny
43 from rhodecode.lib.auth import HasRepoPermissionAny
44 from rhodecode.lib.utils import get_repos, make_ui, action_logger
44 from rhodecode.lib.utils import get_repos, make_ui, action_logger
45 from rhodecode.model import BaseModel
45 from rhodecode.model import BaseModel
46 from rhodecode.model.user import UserModel
46 from rhodecode.model.user import UserModel
47
47
48 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
48 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
49 UserFollowing, UserLog
49 UserFollowing, UserLog
50 from rhodecode.model.caching_query import FromCache
50 from rhodecode.model.caching_query import FromCache
51
51
52 from sqlalchemy.orm import joinedload
52 from sqlalchemy.orm import joinedload
53 from sqlalchemy.orm.session import make_transient
53 from sqlalchemy.orm.session import make_transient
54 from sqlalchemy.exc import DatabaseError
54 from sqlalchemy.exc import DatabaseError
55
55
56 log = logging.getLogger(__name__)
56 log = logging.getLogger(__name__)
57
57
58
58
59 class UserTemp(object):
59 class UserTemp(object):
60 def __init__(self, user_id):
60 def __init__(self, user_id):
61 self.user_id = user_id
61 self.user_id = user_id
62 class RepoTemp(object):
62 class RepoTemp(object):
63 def __init__(self, repo_id):
63 def __init__(self, repo_id):
64 self.repo_id = repo_id
64 self.repo_id = repo_id
65
65
66 class ScmModel(BaseModel):
66 class ScmModel(BaseModel):
67 """Generic Scm Model
67 """Generic Scm Model
68 """
68 """
69
69
70 @LazyProperty
70 @LazyProperty
71 def repos_path(self):
71 def repos_path(self):
72 """Get's the repositories root path from database
72 """Get's the repositories root path from database
73 """
73 """
74
74
75 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
75 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
76
76
77 return q.ui_value
77 return q.ui_value
78
78
79 def repo_scan(self, repos_path, baseui):
79 def repo_scan(self, repos_path, baseui):
80 """Listing of repositories in given path. This path should not be a
80 """Listing of repositories in given path. This path should not be a
81 repository itself. Return a dictionary of repository objects
81 repository itself. Return a dictionary of repository objects
82
82
83 :param repos_path: path to directory containing repositories
83 :param repos_path: path to directory containing repositories
84 :param baseui
84 :param baseui
85 """
85 """
86
86
87 log.info('scanning for repositories in %s', repos_path)
87 log.info('scanning for repositories in %s', repos_path)
88
88
89 if not isinstance(baseui, ui.ui):
89 if not isinstance(baseui, ui.ui):
90 baseui = make_ui('db')
90 baseui = make_ui('db')
91 repos_list = {}
91 repos_list = {}
92
92
93 for name, path in get_repos(repos_path):
93 for name, path in get_repos(repos_path):
94 try:
94 try:
95 if repos_list.has_key(name):
95 if repos_list.has_key(name):
96 raise RepositoryError('Duplicate repository name %s '
96 raise RepositoryError('Duplicate repository name %s '
97 'found in %s' % (name, path))
97 'found in %s' % (name, path))
98 else:
98 else:
99
99
100 klass = get_backend(path[0])
100 klass = get_backend(path[0])
101
101
102 if path[0] == 'hg' and path[0] in BACKENDS.keys():
102 if path[0] == 'hg' and path[0] in BACKENDS.keys():
103 repos_list[name] = klass(path[1], baseui=baseui)
103 repos_list[name] = klass(path[1], baseui=baseui)
104
104
105 if path[0] == 'git' and path[0] in BACKENDS.keys():
105 if path[0] == 'git' and path[0] in BACKENDS.keys():
106 repos_list[name] = klass(path[1])
106 repos_list[name] = klass(path[1])
107 except OSError:
107 except OSError:
108 continue
108 continue
109
109
110 return repos_list
110 return repos_list
111
111
112 def get_repos(self, all_repos=None):
112 def get_repos(self, all_repos=None):
113 """Get all repos from db and for each repo create it's backend instance.
113 """Get all repos from db and for each repo create it's backend instance.
114 and fill that backed with information from database
114 and fill that backed with information from database
115
115
116 :param all_repos: give specific repositories list, good for filtering
116 :param all_repos: give specific repositories list, good for filtering
117 """
117 """
118
118
119 if all_repos is None:
119 if all_repos is None:
120 all_repos = self.sa.query(Repository)\
120 all_repos = self.sa.query(Repository)\
121 .order_by(Repository.repo_name).all()
121 .order_by(Repository.repo_name).all()
122
122
123 #get the repositories that should be invalidated
123 #get the repositories that should be invalidated
124 invalidation_list = [str(x.cache_key) for x in \
124 invalidation_list = [str(x.cache_key) for x in \
125 self.sa.query(CacheInvalidation.cache_key)\
125 self.sa.query(CacheInvalidation.cache_key)\
126 .filter(CacheInvalidation.cache_active == False)\
126 .filter(CacheInvalidation.cache_active == False)\
127 .all()]
127 .all()]
128
128
129 for r in all_repos:
129 for r in all_repos:
130
130
131 repo = self.get(r.repo_name, invalidation_list)
131 repo = self.get(r.repo_name, invalidation_list)
132
132
133 if repo is not None:
133 if repo is not None:
134 last_change = repo.last_change
134 last_change = repo.last_change
135 tip = h.get_changeset_safe(repo, 'tip')
135 tip = h.get_changeset_safe(repo, 'tip')
136
136
137 tmp_d = {}
137 tmp_d = {}
138 tmp_d['name'] = repo.name
138 tmp_d['name'] = repo.name
139 tmp_d['name_sort'] = tmp_d['name'].lower()
139 tmp_d['name_sort'] = tmp_d['name'].lower()
140 tmp_d['description'] = repo.dbrepo.description
140 tmp_d['description'] = repo.dbrepo.description
141 tmp_d['description_sort'] = tmp_d['description']
141 tmp_d['description_sort'] = tmp_d['description']
142 tmp_d['last_change'] = last_change
142 tmp_d['last_change'] = last_change
143 tmp_d['last_change_sort'] = time.mktime(last_change.timetuple())
143 tmp_d['last_change_sort'] = time.mktime(last_change.timetuple())
144 tmp_d['tip'] = tip.raw_id
144 tmp_d['tip'] = tip.raw_id
145 tmp_d['tip_sort'] = tip.revision
145 tmp_d['tip_sort'] = tip.revision
146 tmp_d['rev'] = tip.revision
146 tmp_d['rev'] = tip.revision
147
147 tmp_d['contact'] = repo.dbrepo.user.full_contact
148 #dirty hack for some problems
149 usr = repo.dbrepo.user
150 if isinstance(usr, basestring):
151 usr = UserModel(self.sa).get_by_username(repo.dbrepo.user)
152
153 tmp_d['contact'] = usr.full_contact
154 tmp_d['contact_sort'] = tmp_d['contact']
148 tmp_d['contact_sort'] = tmp_d['contact']
155 tmp_d['repo_archives'] = list(repo._get_archives())
149 tmp_d['repo_archives'] = list(repo._get_archives())
156 tmp_d['last_msg'] = tip.message
150 tmp_d['last_msg'] = tip.message
157 tmp_d['repo'] = repo
151 tmp_d['repo'] = repo
158 yield tmp_d
152 yield tmp_d
159
153
160 def get_repo(self, repo_name):
154 def get_repo(self, repo_name):
161 return self.get(repo_name)
155 return self.get(repo_name)
162
156
163 def get(self, repo_name, invalidation_list=None):
157 def get(self, repo_name, invalidation_list=None):
164 """Get's repository from given name, creates BackendInstance and
158 """Get's repository from given name, creates BackendInstance and
165 propagates it's data from database with all additional information
159 propagates it's data from database with all additional information
166
160
167 :param repo_name:
161 :param repo_name:
168 :param invalidation_list: if a invalidation list is given the get
162 :param invalidation_list: if a invalidation list is given the get
169 method should not manually check if this repository needs
163 method should not manually check if this repository needs
170 invalidation and just invalidate the repositories in list
164 invalidation and just invalidate the repositories in list
171
165
172 """
166 """
173 if not HasRepoPermissionAny('repository.read', 'repository.write',
167 if not HasRepoPermissionAny('repository.read', 'repository.write',
174 'repository.admin')(repo_name, 'get repo check'):
168 'repository.admin')(repo_name, 'get repo check'):
175 return
169 return
176
170
177 #======================================================================
171 #======================================================================
178 # CACHE FUNCTION
172 # CACHE FUNCTION
179 #======================================================================
173 #======================================================================
180 @cache_region('long_term')
174 @cache_region('long_term')
181 def _get_repo(repo_name):
175 def _get_repo(repo_name):
182
176
183 repo_path = os.path.join(self.repos_path, repo_name)
177 repo_path = os.path.join(self.repos_path, repo_name)
184
178
185 try:
179 try:
186 alias = get_scm(repo_path)[0]
180 alias = get_scm(repo_path)[0]
187
181
188 log.debug('Creating instance of %s repository', alias)
182 log.debug('Creating instance of %s repository', alias)
189 backend = get_backend(alias)
183 backend = get_backend(alias)
190 except VCSError:
184 except VCSError:
191 log.error(traceback.format_exc())
185 log.error(traceback.format_exc())
192 return
186 return
193
187
194 if alias == 'hg':
188 if alias == 'hg':
195 from pylons import app_globals as g
189 from pylons import app_globals as g
196 repo = backend(repo_path, create=False, baseui=g.baseui)
190 repo = backend(repo_path, create=False, baseui=g.baseui)
197 #skip hidden web repository
191 #skip hidden web repository
198 if repo._get_hidden():
192 if repo._get_hidden():
199 return
193 return
200 else:
194 else:
201 repo = backend(repo_path, create=False)
195 repo = backend(repo_path, create=False)
202
196
203 dbrepo = self.sa.query(Repository)\
197 dbrepo = self.sa.query(Repository)\
204 .options(joinedload(Repository.fork))\
198 .options(joinedload(Repository.fork))\
205 .options(joinedload(Repository.user))\
199 .options(joinedload(Repository.user))\
206 .filter(Repository.repo_name == repo_name)\
200 .filter(Repository.repo_name == repo_name)\
207 .scalar()
201 .scalar()
208
202
209 make_transient(dbrepo)
203 make_transient(dbrepo)
210 if dbrepo.user:
204 if dbrepo.user:
211 make_transient(dbrepo.user)
205 make_transient(dbrepo.user)
212 if dbrepo.fork:
206 if dbrepo.fork:
213 make_transient(dbrepo.fork)
207 make_transient(dbrepo.fork)
214
208
215 repo.dbrepo = dbrepo
209 repo.dbrepo = dbrepo
216 return repo
210 return repo
217
211
218 pre_invalidate = True
212 pre_invalidate = True
219 if invalidation_list is not None:
213 if invalidation_list is not None:
220 pre_invalidate = repo_name in invalidation_list
214 pre_invalidate = repo_name in invalidation_list
221
215
222 if pre_invalidate:
216 if pre_invalidate:
223 invalidate = self._should_invalidate(repo_name)
217 invalidate = self._should_invalidate(repo_name)
224
218
225 if invalidate:
219 if invalidate:
226 log.info('invalidating cache for repository %s', repo_name)
220 log.info('invalidating cache for repository %s', repo_name)
227 region_invalidate(_get_repo, None, repo_name)
221 region_invalidate(_get_repo, None, repo_name)
228 self._mark_invalidated(invalidate)
222 self._mark_invalidated(invalidate)
229
223
230 return _get_repo(repo_name)
224 return _get_repo(repo_name)
231
225
232
226
233
227
234 def mark_for_invalidation(self, repo_name):
228 def mark_for_invalidation(self, repo_name):
235 """Puts cache invalidation task into db for
229 """Puts cache invalidation task into db for
236 further global cache invalidation
230 further global cache invalidation
237
231
238 :param repo_name: this repo that should invalidation take place
232 :param repo_name: this repo that should invalidation take place
239 """
233 """
240
234
241 log.debug('marking %s for invalidation', repo_name)
235 log.debug('marking %s for invalidation', repo_name)
242 cache = self.sa.query(CacheInvalidation)\
236 cache = self.sa.query(CacheInvalidation)\
243 .filter(CacheInvalidation.cache_key == repo_name).scalar()
237 .filter(CacheInvalidation.cache_key == repo_name).scalar()
244
238
245 if cache:
239 if cache:
246 #mark this cache as inactive
240 #mark this cache as inactive
247 cache.cache_active = False
241 cache.cache_active = False
248 else:
242 else:
249 log.debug('cache key not found in invalidation db -> creating one')
243 log.debug('cache key not found in invalidation db -> creating one')
250 cache = CacheInvalidation(repo_name)
244 cache = CacheInvalidation(repo_name)
251
245
252 try:
246 try:
253 self.sa.add(cache)
247 self.sa.add(cache)
254 self.sa.commit()
248 self.sa.commit()
255 except (DatabaseError,):
249 except (DatabaseError,):
256 log.error(traceback.format_exc())
250 log.error(traceback.format_exc())
257 self.sa.rollback()
251 self.sa.rollback()
258
252
259
253
260 def toggle_following_repo(self, follow_repo_id, user_id):
254 def toggle_following_repo(self, follow_repo_id, user_id):
261
255
262 f = self.sa.query(UserFollowing)\
256 f = self.sa.query(UserFollowing)\
263 .filter(UserFollowing.follows_repo_id == follow_repo_id)\
257 .filter(UserFollowing.follows_repo_id == follow_repo_id)\
264 .filter(UserFollowing.user_id == user_id).scalar()
258 .filter(UserFollowing.user_id == user_id).scalar()
265
259
266 if f is not None:
260 if f is not None:
267
261
268 try:
262 try:
269 self.sa.delete(f)
263 self.sa.delete(f)
270 self.sa.commit()
264 self.sa.commit()
271 action_logger(UserTemp(user_id),
265 action_logger(UserTemp(user_id),
272 'stopped_following_repo',
266 'stopped_following_repo',
273 RepoTemp(follow_repo_id))
267 RepoTemp(follow_repo_id))
274 return
268 return
275 except:
269 except:
276 log.error(traceback.format_exc())
270 log.error(traceback.format_exc())
277 self.sa.rollback()
271 self.sa.rollback()
278 raise
272 raise
279
273
280
274
281 try:
275 try:
282 f = UserFollowing()
276 f = UserFollowing()
283 f.user_id = user_id
277 f.user_id = user_id
284 f.follows_repo_id = follow_repo_id
278 f.follows_repo_id = follow_repo_id
285 self.sa.add(f)
279 self.sa.add(f)
286 self.sa.commit()
280 self.sa.commit()
287 action_logger(UserTemp(user_id),
281 action_logger(UserTemp(user_id),
288 'started_following_repo',
282 'started_following_repo',
289 RepoTemp(follow_repo_id))
283 RepoTemp(follow_repo_id))
290 except:
284 except:
291 log.error(traceback.format_exc())
285 log.error(traceback.format_exc())
292 self.sa.rollback()
286 self.sa.rollback()
293 raise
287 raise
294
288
295 def toggle_following_user(self, follow_user_id , user_id):
289 def toggle_following_user(self, follow_user_id , user_id):
296 f = self.sa.query(UserFollowing)\
290 f = self.sa.query(UserFollowing)\
297 .filter(UserFollowing.follows_user_id == follow_user_id)\
291 .filter(UserFollowing.follows_user_id == follow_user_id)\
298 .filter(UserFollowing.user_id == user_id).scalar()
292 .filter(UserFollowing.user_id == user_id).scalar()
299
293
300 if f is not None:
294 if f is not None:
301 try:
295 try:
302 self.sa.delete(f)
296 self.sa.delete(f)
303 self.sa.commit()
297 self.sa.commit()
304 return
298 return
305 except:
299 except:
306 log.error(traceback.format_exc())
300 log.error(traceback.format_exc())
307 self.sa.rollback()
301 self.sa.rollback()
308 raise
302 raise
309
303
310 try:
304 try:
311 f = UserFollowing()
305 f = UserFollowing()
312 f.user_id = user_id
306 f.user_id = user_id
313 f.follows_user_id = follow_user_id
307 f.follows_user_id = follow_user_id
314 self.sa.add(f)
308 self.sa.add(f)
315 self.sa.commit()
309 self.sa.commit()
316 except:
310 except:
317 log.error(traceback.format_exc())
311 log.error(traceback.format_exc())
318 self.sa.rollback()
312 self.sa.rollback()
319 raise
313 raise
320
314
321 def is_following_repo(self, repo_name, user_id):
315 def is_following_repo(self, repo_name, user_id):
322 r = self.sa.query(Repository)\
316 r = self.sa.query(Repository)\
323 .filter(Repository.repo_name == repo_name).scalar()
317 .filter(Repository.repo_name == repo_name).scalar()
324
318
325 f = self.sa.query(UserFollowing)\
319 f = self.sa.query(UserFollowing)\
326 .filter(UserFollowing.follows_repository == r)\
320 .filter(UserFollowing.follows_repository == r)\
327 .filter(UserFollowing.user_id == user_id).scalar()
321 .filter(UserFollowing.user_id == user_id).scalar()
328
322
329 return f is not None
323 return f is not None
330
324
331 def is_following_user(self, username, user_id):
325 def is_following_user(self, username, user_id):
332 u = UserModel(self.sa).get_by_username(username)
326 u = UserModel(self.sa).get_by_username(username)
333
327
334 f = self.sa.query(UserFollowing)\
328 f = self.sa.query(UserFollowing)\
335 .filter(UserFollowing.follows_user == u)\
329 .filter(UserFollowing.follows_user == u)\
336 .filter(UserFollowing.user_id == user_id).scalar()
330 .filter(UserFollowing.user_id == user_id).scalar()
337
331
338 return f is not None
332 return f is not None
339
333
340 def get_followers(self, repo_id):
334 def get_followers(self, repo_id):
341 return self.sa.query(UserFollowing)\
335 return self.sa.query(UserFollowing)\
342 .filter(UserFollowing.follows_repo_id == repo_id).count()
336 .filter(UserFollowing.follows_repo_id == repo_id).count()
343
337
344 def get_forks(self, repo_id):
338 def get_forks(self, repo_id):
345 return self.sa.query(Repository)\
339 return self.sa.query(Repository)\
346 .filter(Repository.fork_id == repo_id).count()
340 .filter(Repository.fork_id == repo_id).count()
347
341
348
342
349 def get_unread_journal(self):
343 def get_unread_journal(self):
350 return self.sa.query(UserLog).count()
344 return self.sa.query(UserLog).count()
351
345
352
346
353 def _should_invalidate(self, repo_name):
347 def _should_invalidate(self, repo_name):
354 """Looks up database for invalidation signals for this repo_name
348 """Looks up database for invalidation signals for this repo_name
355
349
356 :param repo_name:
350 :param repo_name:
357 """
351 """
358
352
359 ret = self.sa.query(CacheInvalidation)\
353 ret = self.sa.query(CacheInvalidation)\
360 .options(FromCache('sql_cache_short',
354 .options(FromCache('sql_cache_short',
361 'get_invalidation_%s' % repo_name))\
355 'get_invalidation_%s' % repo_name))\
362 .filter(CacheInvalidation.cache_key == repo_name)\
356 .filter(CacheInvalidation.cache_key == repo_name)\
363 .filter(CacheInvalidation.cache_active == False)\
357 .filter(CacheInvalidation.cache_active == False)\
364 .scalar()
358 .scalar()
365
359
366 return ret
360 return ret
367
361
368 def _mark_invalidated(self, cache_key):
362 def _mark_invalidated(self, cache_key):
369 """ Marks all occurences of cache to invaldation as already invalidated
363 """ Marks all occurences of cache to invaldation as already invalidated
370
364
371 :param cache_key:
365 :param cache_key:
372 """
366 """
373
367
374 if cache_key:
368 if cache_key:
375 log.debug('marking %s as already invalidated', cache_key)
369 log.debug('marking %s as already invalidated', cache_key)
376 try:
370 try:
377 cache_key.cache_active = True
371 cache_key.cache_active = True
378 self.sa.add(cache_key)
372 self.sa.add(cache_key)
379 self.sa.commit()
373 self.sa.commit()
380 except (DatabaseError,):
374 except (DatabaseError,):
381 log.error(traceback.format_exc())
375 log.error(traceback.format_exc())
382 self.sa.rollback()
376 self.sa.rollback()
383
377
General Comments 0
You need to be logged in to leave comments. Login now