##// END OF EJS Templates
replaced all global calls to template context (rhodecode_user), into instance attributes
marcink -
r1121:f3f84771 beta
parent child Browse files
Show More
@@ -1,410 +1,410 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-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
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, repo_name_slug
43 from rhodecode.lib.utils import invalidate_cache, action_logger, repo_name_slug
44 from rhodecode.lib.helpers import get_token
44 from rhodecode.lib.helpers import get_token
45 from rhodecode.model.db import User, Repository, UserFollowing, Group
45 from rhodecode.model.db import User, Repository, UserFollowing, Group
46 from rhodecode.model.forms import RepoForm
46 from rhodecode.model.forms import RepoForm
47 from rhodecode.model.scm import ScmModel
47 from rhodecode.model.scm import ScmModel
48 from rhodecode.model.repo import RepoModel
48 from rhodecode.model.repo import RepoModel
49
49
50 log = logging.getLogger(__name__)
50 log = logging.getLogger(__name__)
51
51
52 class ReposController(BaseController):
52 class ReposController(BaseController):
53 """
53 """
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('repo', 'repos')
57 # map.resource('repo', 'repos')
58
58
59 @LoginRequired()
59 @LoginRequired()
60 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
60 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
61 def __before__(self):
61 def __before__(self):
62 c.admin_user = session.get('admin_user')
62 c.admin_user = session.get('admin_user')
63 c.admin_username = session.get('admin_username')
63 c.admin_username = session.get('admin_username')
64 super(ReposController, self).__before__()
64 super(ReposController, self).__before__()
65
65
66
66
67
67
68 def __load_data(self, repo_name):
68 def __load_data(self, repo_name):
69 """
69 """
70 Load defaults settings for edit, and update
70 Load defaults settings for edit, and update
71
71
72 :param repo_name:
72 :param repo_name:
73 """
73 """
74 repo, dbrepo = ScmModel().get(repo_name, retval='repo')
74 repo, dbrepo = ScmModel().get(repo_name, retval='repo')
75
75
76 repo_model = RepoModel()
76 repo_model = RepoModel()
77 c.repo_info = repo_model.get_by_repo_name(repo_name)
77 c.repo_info = repo_model.get_by_repo_name(repo_name)
78
78
79 if c.repo_info is None:
79 if c.repo_info is None:
80 h.flash(_('%s repository is not mapped to db perhaps'
80 h.flash(_('%s repository is not mapped to db perhaps'
81 ' it was created or renamed from the filesystem'
81 ' it was created or renamed from the filesystem'
82 ' please run the application again'
82 ' please run the application again'
83 ' in order to rescan repositories') % repo_name,
83 ' in order to rescan repositories') % repo_name,
84 category='error')
84 category='error')
85
85
86 return redirect(url('repos'))
86 return redirect(url('repos'))
87
87
88
88
89
89
90 c.repo_groups = [('', '')]
90 c.repo_groups = [('', '')]
91 c.repo_groups.extend([(x.group_id, x.group_name) for x in self.sa.query(Group).all()])
91 c.repo_groups.extend([(x.group_id, x.group_name) for x in self.sa.query(Group).all()])
92
92
93 c.default_user_id = User.by_username('default').user_id
93 c.default_user_id = User.by_username('default').user_id
94 c.in_public_journal = self.sa.query(UserFollowing)\
94 c.in_public_journal = self.sa.query(UserFollowing)\
95 .filter(UserFollowing.user_id == c.default_user_id)\
95 .filter(UserFollowing.user_id == c.default_user_id)\
96 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
96 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
97
97
98 if c.repo_info.stats:
98 if c.repo_info.stats:
99 last_rev = c.repo_info.stats.stat_on_revision
99 last_rev = c.repo_info.stats.stat_on_revision
100 else:
100 else:
101 last_rev = 0
101 last_rev = 0
102 c.stats_revision = last_rev
102 c.stats_revision = last_rev
103
103
104 c.repo_last_rev = repo.count() - 1 if repo.revisions else 0
104 c.repo_last_rev = repo.count() - 1 if repo.revisions else 0
105
105
106 if last_rev == 0 or c.repo_last_rev == 0:
106 if last_rev == 0 or c.repo_last_rev == 0:
107 c.stats_percentage = 0
107 c.stats_percentage = 0
108 else:
108 else:
109 c.stats_percentage = '%.2f' % ((float((last_rev)) /
109 c.stats_percentage = '%.2f' % ((float((last_rev)) /
110 c.repo_last_rev) * 100)
110 c.repo_last_rev) * 100)
111
111
112 c.users_array = repo_model.get_users_js()
112 c.users_array = repo_model.get_users_js()
113 c.users_groups_array = repo_model.get_users_groups_js()
113 c.users_groups_array = repo_model.get_users_groups_js()
114
114
115 defaults = c.repo_info.get_dict()
115 defaults = c.repo_info.get_dict()
116 group, repo_name = c.repo_info.groups_and_repo
116 group, repo_name = c.repo_info.groups_and_repo
117 defaults['repo_name'] = repo_name
117 defaults['repo_name'] = repo_name
118 defaults['repo_group'] = getattr(group, 'group_id', None)
118 defaults['repo_group'] = getattr(group, 'group_id', None)
119 #fill owner
119 #fill owner
120 if c.repo_info.user:
120 if c.repo_info.user:
121 defaults.update({'user':c.repo_info.user.username})
121 defaults.update({'user':c.repo_info.user.username})
122 else:
122 else:
123 replacement_user = self.sa.query(User)\
123 replacement_user = self.sa.query(User)\
124 .filter(User.admin == True).first().username
124 .filter(User.admin == True).first().username
125 defaults.update({'user':replacement_user})
125 defaults.update({'user':replacement_user})
126
126
127
127
128 #fill repository users
128 #fill repository users
129 for p in c.repo_info.repo_to_perm:
129 for p in c.repo_info.repo_to_perm:
130 defaults.update({'u_perm_%s' % p.user.username:
130 defaults.update({'u_perm_%s' % p.user.username:
131 p.permission.permission_name})
131 p.permission.permission_name})
132
132
133 #fill repository groups
133 #fill repository groups
134 for p in c.repo_info.users_group_to_perm:
134 for p in c.repo_info.users_group_to_perm:
135 defaults.update({'g_perm_%s' % p.users_group.users_group_name:
135 defaults.update({'g_perm_%s' % p.users_group.users_group_name:
136 p.permission.permission_name})
136 p.permission.permission_name})
137
137
138
138
139 return defaults
139 return defaults
140
140
141
141
142 @HasPermissionAllDecorator('hg.admin')
142 @HasPermissionAllDecorator('hg.admin')
143 def index(self, format='html'):
143 def index(self, format='html'):
144 """GET /repos: All items in the collection"""
144 """GET /repos: All items in the collection"""
145 # url('repos')
145 # url('repos')
146 cached_repo_list = ScmModel().get_repos()
146 cached_repo_list = ScmModel().get_repos()
147 c.repos_list = sorted(cached_repo_list, key=itemgetter('name_sort'))
147 c.repos_list = sorted(cached_repo_list, key=itemgetter('name_sort'))
148 return render('admin/repos/repos.html')
148 return render('admin/repos/repos.html')
149
149
150 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
150 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
151 def create(self):
151 def create(self):
152 """
152 """
153 POST /repos: Create a new item"""
153 POST /repos: Create a new item"""
154 # url('repos')
154 # url('repos')
155 repo_model = RepoModel()
155 repo_model = RepoModel()
156 c.repo_groups = [('', '')]
156 c.repo_groups = [('', '')]
157 c.repo_groups.extend([(x.group_id, x.group_name) for x in self.sa.query(Group).all()])
157 c.repo_groups.extend([(x.group_id, x.group_name) for x in self.sa.query(Group).all()])
158 form_result = {}
158 form_result = {}
159 try:
159 try:
160 form_result = RepoForm()(repo_groups=c.repo_groups).to_python(dict(request.POST))
160 form_result = RepoForm()(repo_groups=c.repo_groups).to_python(dict(request.POST))
161 repo_model.create(form_result, c.rhodecode_user)
161 repo_model.create(form_result, self.rhodecode_user)
162 if form_result['clone_uri']:
162 if form_result['clone_uri']:
163 h.flash(_('created repository %s from %s') \
163 h.flash(_('created repository %s from %s') \
164 % (form_result['repo_name'], form_result['clone_uri']),
164 % (form_result['repo_name'], form_result['clone_uri']),
165 category='success')
165 category='success')
166 else:
166 else:
167 h.flash(_('created repository %s') % form_result['repo_name'],
167 h.flash(_('created repository %s') % form_result['repo_name'],
168 category='success')
168 category='success')
169
169
170 if request.POST.get('user_created'):
170 if request.POST.get('user_created'):
171 action_logger(self.rhodecode_user, 'user_created_repo',
171 action_logger(self.rhodecode_user, 'user_created_repo',
172 form_result['repo_name'], '', self.sa)
172 form_result['repo_name'], '', self.sa)
173 else:
173 else:
174 action_logger(self.rhodecode_user, 'admin_created_repo',
174 action_logger(self.rhodecode_user, 'admin_created_repo',
175 form_result['repo_name'], '', self.sa)
175 form_result['repo_name'], '', self.sa)
176
176
177 except formencode.Invalid, errors:
177 except formencode.Invalid, errors:
178
178
179 c.new_repo = errors.value['repo_name']
179 c.new_repo = errors.value['repo_name']
180 c.repo_groups = [('', '')]
180 c.repo_groups = [('', '')]
181 c.repo_groups.extend([(x.group_id, x.group_name) for x in self.sa.query(Group).all()])
181 c.repo_groups.extend([(x.group_id, x.group_name) for x in self.sa.query(Group).all()])
182
182
183 if request.POST.get('user_created'):
183 if request.POST.get('user_created'):
184 r = render('admin/repos/repo_add_create_repository.html')
184 r = render('admin/repos/repo_add_create_repository.html')
185 else:
185 else:
186 r = render('admin/repos/repo_add.html')
186 r = render('admin/repos/repo_add.html')
187
187
188 return htmlfill.render(
188 return htmlfill.render(
189 r,
189 r,
190 defaults=errors.value,
190 defaults=errors.value,
191 errors=errors.error_dict or {},
191 errors=errors.error_dict or {},
192 prefix_error=False,
192 prefix_error=False,
193 encoding="UTF-8")
193 encoding="UTF-8")
194
194
195 except Exception:
195 except Exception:
196 log.error(traceback.format_exc())
196 log.error(traceback.format_exc())
197 msg = _('error occurred during creation of repository %s') \
197 msg = _('error occurred during creation of repository %s') \
198 % form_result.get('repo_name')
198 % form_result.get('repo_name')
199 h.flash(msg, category='error')
199 h.flash(msg, category='error')
200 if request.POST.get('user_created'):
200 if request.POST.get('user_created'):
201 return redirect(url('home'))
201 return redirect(url('home'))
202 return redirect(url('repos'))
202 return redirect(url('repos'))
203
203
204 @HasPermissionAllDecorator('hg.admin')
204 @HasPermissionAllDecorator('hg.admin')
205 def new(self, format='html'):
205 def new(self, format='html'):
206 """GET /repos/new: Form to create a new item"""
206 """GET /repos/new: Form to create a new item"""
207 new_repo = request.GET.get('repo', '')
207 new_repo = request.GET.get('repo', '')
208 c.new_repo = repo_name_slug(new_repo)
208 c.new_repo = repo_name_slug(new_repo)
209 c.repo_groups = [('', '')]
209 c.repo_groups = [('', '')]
210 c.repo_groups.extend([(x.group_id, x.group_name) for x in self.sa.query(Group).all()])
210 c.repo_groups.extend([(x.group_id, x.group_name) for x in self.sa.query(Group).all()])
211 return render('admin/repos/repo_add.html')
211 return render('admin/repos/repo_add.html')
212
212
213 @HasPermissionAllDecorator('hg.admin')
213 @HasPermissionAllDecorator('hg.admin')
214 def update(self, repo_name):
214 def update(self, repo_name):
215 """
215 """
216 PUT /repos/repo_name: Update an existing item"""
216 PUT /repos/repo_name: Update an existing item"""
217 # Forms posted to this method should contain a hidden field:
217 # Forms posted to this method should contain a hidden field:
218 # <input type="hidden" name="_method" value="PUT" />
218 # <input type="hidden" name="_method" value="PUT" />
219 # Or using helpers:
219 # Or using helpers:
220 # h.form(url('repo', repo_name=ID),
220 # h.form(url('repo', repo_name=ID),
221 # method='put')
221 # method='put')
222 # url('repo', repo_name=ID)
222 # url('repo', repo_name=ID)
223 repo_model = RepoModel()
223 repo_model = RepoModel()
224 changed_name = repo_name
224 changed_name = repo_name
225 _form = RepoForm(edit=True, old_data={'repo_name':repo_name})()
225 _form = RepoForm(edit=True, old_data={'repo_name':repo_name})()
226 try:
226 try:
227 form_result = _form.to_python(dict(request.POST))
227 form_result = _form.to_python(dict(request.POST))
228 repo_model.update(repo_name, form_result)
228 repo_model.update(repo_name, form_result)
229 invalidate_cache('get_repo_cached_%s' % repo_name)
229 invalidate_cache('get_repo_cached_%s' % repo_name)
230 h.flash(_('Repository %s updated successfully' % repo_name),
230 h.flash(_('Repository %s updated successfully' % repo_name),
231 category='success')
231 category='success')
232 changed_name = form_result['repo_name']
232 changed_name = form_result['repo_name']
233 action_logger(self.rhodecode_user, 'admin_updated_repo',
233 action_logger(self.rhodecode_user, 'admin_updated_repo',
234 changed_name, '', self.sa)
234 changed_name, '', self.sa)
235
235
236 except formencode.Invalid, errors:
236 except formencode.Invalid, errors:
237 defaults = self.__load_data(repo_name)
237 defaults = self.__load_data(repo_name)
238 defaults.update(errors.value)
238 defaults.update(errors.value)
239 return htmlfill.render(
239 return htmlfill.render(
240 render('admin/repos/repo_edit.html'),
240 render('admin/repos/repo_edit.html'),
241 defaults=defaults,
241 defaults=defaults,
242 errors=errors.error_dict or {},
242 errors=errors.error_dict or {},
243 prefix_error=False,
243 prefix_error=False,
244 encoding="UTF-8")
244 encoding="UTF-8")
245
245
246 except Exception:
246 except Exception:
247 log.error(traceback.format_exc())
247 log.error(traceback.format_exc())
248 h.flash(_('error occurred during update of repository %s') \
248 h.flash(_('error occurred during update of repository %s') \
249 % repo_name, category='error')
249 % repo_name, category='error')
250 return redirect(url('edit_repo', repo_name=changed_name))
250 return redirect(url('edit_repo', repo_name=changed_name))
251
251
252 @HasPermissionAllDecorator('hg.admin')
252 @HasPermissionAllDecorator('hg.admin')
253 def delete(self, repo_name):
253 def delete(self, repo_name):
254 """
254 """
255 DELETE /repos/repo_name: Delete an existing item"""
255 DELETE /repos/repo_name: Delete an existing item"""
256 # Forms posted to this method should contain a hidden field:
256 # Forms posted to this method should contain a hidden field:
257 # <input type="hidden" name="_method" value="DELETE" />
257 # <input type="hidden" name="_method" value="DELETE" />
258 # Or using helpers:
258 # Or using helpers:
259 # h.form(url('repo', repo_name=ID),
259 # h.form(url('repo', repo_name=ID),
260 # method='delete')
260 # method='delete')
261 # url('repo', repo_name=ID)
261 # url('repo', repo_name=ID)
262
262
263 repo_model = RepoModel()
263 repo_model = RepoModel()
264 repo = repo_model.get_by_repo_name(repo_name)
264 repo = repo_model.get_by_repo_name(repo_name)
265 if not repo:
265 if not repo:
266 h.flash(_('%s repository is not mapped to db perhaps'
266 h.flash(_('%s repository is not mapped to db perhaps'
267 ' it was moved or renamed from the filesystem'
267 ' it was moved or renamed from the filesystem'
268 ' please run the application again'
268 ' please run the application again'
269 ' in order to rescan repositories') % repo_name,
269 ' in order to rescan repositories') % repo_name,
270 category='error')
270 category='error')
271
271
272 return redirect(url('repos'))
272 return redirect(url('repos'))
273 try:
273 try:
274 action_logger(self.rhodecode_user, 'admin_deleted_repo',
274 action_logger(self.rhodecode_user, 'admin_deleted_repo',
275 repo_name, '', self.sa)
275 repo_name, '', self.sa)
276 repo_model.delete(repo)
276 repo_model.delete(repo)
277 invalidate_cache('get_repo_cached_%s' % repo_name)
277 invalidate_cache('get_repo_cached_%s' % repo_name)
278 h.flash(_('deleted repository %s') % repo_name, category='success')
278 h.flash(_('deleted repository %s') % repo_name, category='success')
279
279
280 except Exception, e:
280 except Exception, e:
281 log.error(traceback.format_exc())
281 log.error(traceback.format_exc())
282 h.flash(_('An error occurred during deletion of %s') % repo_name,
282 h.flash(_('An error occurred during deletion of %s') % repo_name,
283 category='error')
283 category='error')
284
284
285 return redirect(url('repos'))
285 return redirect(url('repos'))
286
286
287 @HasPermissionAllDecorator('hg.admin')
287 @HasPermissionAllDecorator('hg.admin')
288 def delete_perm_user(self, repo_name):
288 def delete_perm_user(self, repo_name):
289 """
289 """
290 DELETE an existing repository permission user
290 DELETE an existing repository permission user
291
291
292 :param repo_name:
292 :param repo_name:
293 """
293 """
294
294
295 try:
295 try:
296 repo_model = RepoModel()
296 repo_model = RepoModel()
297 repo_model.delete_perm_user(request.POST, repo_name)
297 repo_model.delete_perm_user(request.POST, repo_name)
298 except Exception, e:
298 except Exception, e:
299 h.flash(_('An error occurred during deletion of repository user'),
299 h.flash(_('An error occurred during deletion of repository user'),
300 category='error')
300 category='error')
301 raise HTTPInternalServerError()
301 raise HTTPInternalServerError()
302
302
303 @HasPermissionAllDecorator('hg.admin')
303 @HasPermissionAllDecorator('hg.admin')
304 def delete_perm_users_group(self, repo_name):
304 def delete_perm_users_group(self, repo_name):
305 """
305 """
306 DELETE an existing repository permission users group
306 DELETE an existing repository permission users group
307
307
308 :param repo_name:
308 :param repo_name:
309 """
309 """
310 try:
310 try:
311 repo_model = RepoModel()
311 repo_model = RepoModel()
312 repo_model.delete_perm_users_group(request.POST, repo_name)
312 repo_model.delete_perm_users_group(request.POST, repo_name)
313 except Exception, e:
313 except Exception, e:
314 h.flash(_('An error occurred during deletion of repository'
314 h.flash(_('An error occurred during deletion of repository'
315 ' users groups'),
315 ' users groups'),
316 category='error')
316 category='error')
317 raise HTTPInternalServerError()
317 raise HTTPInternalServerError()
318
318
319 @HasPermissionAllDecorator('hg.admin')
319 @HasPermissionAllDecorator('hg.admin')
320 def repo_stats(self, repo_name):
320 def repo_stats(self, repo_name):
321 """
321 """
322 DELETE an existing repository statistics
322 DELETE an existing repository statistics
323
323
324 :param repo_name:
324 :param repo_name:
325 """
325 """
326
326
327 try:
327 try:
328 repo_model = RepoModel()
328 repo_model = RepoModel()
329 repo_model.delete_stats(repo_name)
329 repo_model.delete_stats(repo_name)
330 except Exception, e:
330 except Exception, e:
331 h.flash(_('An error occurred during deletion of repository stats'),
331 h.flash(_('An error occurred during deletion of repository stats'),
332 category='error')
332 category='error')
333 return redirect(url('edit_repo', repo_name=repo_name))
333 return redirect(url('edit_repo', repo_name=repo_name))
334
334
335 @HasPermissionAllDecorator('hg.admin')
335 @HasPermissionAllDecorator('hg.admin')
336 def repo_cache(self, repo_name):
336 def repo_cache(self, repo_name):
337 """
337 """
338 INVALIDATE existing repository cache
338 INVALIDATE existing repository cache
339
339
340 :param repo_name:
340 :param repo_name:
341 """
341 """
342
342
343 try:
343 try:
344 ScmModel().mark_for_invalidation(repo_name)
344 ScmModel().mark_for_invalidation(repo_name)
345 except Exception, e:
345 except Exception, e:
346 h.flash(_('An error occurred during cache invalidation'),
346 h.flash(_('An error occurred during cache invalidation'),
347 category='error')
347 category='error')
348 return redirect(url('edit_repo', repo_name=repo_name))
348 return redirect(url('edit_repo', repo_name=repo_name))
349
349
350 @HasPermissionAllDecorator('hg.admin')
350 @HasPermissionAllDecorator('hg.admin')
351 def repo_public_journal(self, repo_name):
351 def repo_public_journal(self, repo_name):
352 """
352 """
353 Set's this repository to be visible in public journal,
353 Set's this repository to be visible in public journal,
354 in other words assing default user to follow this repo
354 in other words assing default user to follow this repo
355
355
356 :param repo_name:
356 :param repo_name:
357 """
357 """
358
358
359 cur_token = request.POST.get('auth_token')
359 cur_token = request.POST.get('auth_token')
360 token = get_token()
360 token = get_token()
361 if cur_token == token:
361 if cur_token == token:
362 try:
362 try:
363 repo_id = Repository.by_repo_name(repo_name).repo_id
363 repo_id = Repository.by_repo_name(repo_name).repo_id
364 user_id = User.by_username('default').user_id
364 user_id = User.by_username('default').user_id
365 self.scm_model.toggle_following_repo(repo_id, user_id)
365 self.scm_model.toggle_following_repo(repo_id, user_id)
366 h.flash(_('Updated repository visibility in public journal'),
366 h.flash(_('Updated repository visibility in public journal'),
367 category='success')
367 category='success')
368 except:
368 except:
369 h.flash(_('An error occurred during setting this'
369 h.flash(_('An error occurred during setting this'
370 ' repository in public journal'),
370 ' repository in public journal'),
371 category='error')
371 category='error')
372
372
373 else:
373 else:
374 h.flash(_('Token mismatch'), category='error')
374 h.flash(_('Token mismatch'), category='error')
375 return redirect(url('edit_repo', repo_name=repo_name))
375 return redirect(url('edit_repo', repo_name=repo_name))
376
376
377 @HasPermissionAllDecorator('hg.admin')
377 @HasPermissionAllDecorator('hg.admin')
378 def repo_pull(self, repo_name):
378 def repo_pull(self, repo_name):
379 """
379 """
380 Runs task to update given repository with remote changes,
380 Runs task to update given repository with remote changes,
381 ie. make pull on remote location
381 ie. make pull on remote location
382
382
383 :param repo_name:
383 :param repo_name:
384 """
384 """
385 try:
385 try:
386 ScmModel().pull_changes(repo_name, c.rhodecode_user.username)
386 ScmModel().pull_changes(repo_name, self.rhodecode_user.username)
387 h.flash(_('Pulled from remote location'), category='success')
387 h.flash(_('Pulled from remote location'), category='success')
388 except Exception, e:
388 except Exception, e:
389 h.flash(_('An error occurred during pull from remote location'),
389 h.flash(_('An error occurred during pull from remote location'),
390 category='error')
390 category='error')
391
391
392 return redirect(url('edit_repo', repo_name=repo_name))
392 return redirect(url('edit_repo', repo_name=repo_name))
393
393
394 @HasPermissionAllDecorator('hg.admin')
394 @HasPermissionAllDecorator('hg.admin')
395 def show(self, repo_name, format='html'):
395 def show(self, repo_name, format='html'):
396 """GET /repos/repo_name: Show a specific item"""
396 """GET /repos/repo_name: Show a specific item"""
397 # url('repo', repo_name=ID)
397 # url('repo', repo_name=ID)
398
398
399 @HasPermissionAllDecorator('hg.admin')
399 @HasPermissionAllDecorator('hg.admin')
400 def edit(self, repo_name, format='html'):
400 def edit(self, repo_name, format='html'):
401 """GET /repos/repo_name/edit: Form to edit an existing item"""
401 """GET /repos/repo_name/edit: Form to edit an existing item"""
402 # url('edit_repo', repo_name=ID)
402 # url('edit_repo', repo_name=ID)
403 defaults = self.__load_data(repo_name)
403 defaults = self.__load_data(repo_name)
404
404
405 return htmlfill.render(
405 return htmlfill.render(
406 render('admin/repos/repo_edit.html'),
406 render('admin/repos/repo_edit.html'),
407 defaults=defaults,
407 defaults=defaults,
408 encoding="UTF-8",
408 encoding="UTF-8",
409 force_defaults=False
409 force_defaults=False
410 )
410 )
@@ -1,348 +1,348 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.settings
3 rhodecode.controllers.admin.settings
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 settings controller for rhodecode admin
6 settings controller for rhodecode admin
7
7
8 :created_on: Jul 14, 2010
8 :created_on: Jul 14, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
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
31
32 from sqlalchemy import func
32 from sqlalchemy import func
33 from formencode import htmlfill
33 from formencode import htmlfill
34 from pylons import request, session, tmpl_context as c, url, config
34 from pylons import request, session, tmpl_context as c, url, config
35 from pylons.controllers.util import abort, redirect
35 from pylons.controllers.util import abort, redirect
36 from pylons.i18n.translation import _
36 from pylons.i18n.translation import _
37
37
38 from rhodecode.lib import helpers as h
38 from rhodecode.lib import helpers as h
39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
40 HasPermissionAnyDecorator, NotAnonymous
40 HasPermissionAnyDecorator, NotAnonymous
41 from rhodecode.lib.base import BaseController, render
41 from rhodecode.lib.base import BaseController, render
42 from rhodecode.lib.celerylib import tasks, run_task
42 from rhodecode.lib.celerylib import tasks, run_task
43 from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
43 from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
44 set_rhodecode_config, repo_name_slug
44 set_rhodecode_config, repo_name_slug
45 from rhodecode.model.db import RhodeCodeUi, Repository, Group
45 from rhodecode.model.db import RhodeCodeUi, Repository, Group
46 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
46 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
47 ApplicationUiSettingsForm
47 ApplicationUiSettingsForm
48 from rhodecode.model.scm import ScmModel
48 from rhodecode.model.scm import ScmModel
49 from rhodecode.model.settings import SettingsModel
49 from rhodecode.model.settings import SettingsModel
50 from rhodecode.model.user import UserModel
50 from rhodecode.model.user import UserModel
51
51
52 log = logging.getLogger(__name__)
52 log = logging.getLogger(__name__)
53
53
54
54
55 class SettingsController(BaseController):
55 class SettingsController(BaseController):
56 """REST Controller styled on the Atom Publishing Protocol"""
56 """REST Controller styled on the Atom Publishing Protocol"""
57 # To properly map this controller, ensure your config/routing.py
57 # To properly map this controller, ensure your config/routing.py
58 # file has a resource setup:
58 # file has a resource setup:
59 # map.resource('setting', 'settings', controller='admin/settings',
59 # map.resource('setting', 'settings', controller='admin/settings',
60 # path_prefix='/admin', name_prefix='admin_')
60 # path_prefix='/admin', name_prefix='admin_')
61
61
62
62
63 @LoginRequired()
63 @LoginRequired()
64 def __before__(self):
64 def __before__(self):
65 c.admin_user = session.get('admin_user')
65 c.admin_user = session.get('admin_user')
66 c.admin_username = session.get('admin_username')
66 c.admin_username = session.get('admin_username')
67 super(SettingsController, self).__before__()
67 super(SettingsController, self).__before__()
68
68
69
69
70 @HasPermissionAllDecorator('hg.admin')
70 @HasPermissionAllDecorator('hg.admin')
71 def index(self, format='html'):
71 def index(self, format='html'):
72 """GET /admin/settings: All items in the collection"""
72 """GET /admin/settings: All items in the collection"""
73 # url('admin_settings')
73 # url('admin_settings')
74
74
75 defaults = SettingsModel().get_app_settings()
75 defaults = SettingsModel().get_app_settings()
76 defaults.update(self.get_hg_ui_settings())
76 defaults.update(self.get_hg_ui_settings())
77 return htmlfill.render(
77 return htmlfill.render(
78 render('admin/settings/settings.html'),
78 render('admin/settings/settings.html'),
79 defaults=defaults,
79 defaults=defaults,
80 encoding="UTF-8",
80 encoding="UTF-8",
81 force_defaults=False
81 force_defaults=False
82 )
82 )
83
83
84 @HasPermissionAllDecorator('hg.admin')
84 @HasPermissionAllDecorator('hg.admin')
85 def create(self):
85 def create(self):
86 """POST /admin/settings: Create a new item"""
86 """POST /admin/settings: Create a new item"""
87 # url('admin_settings')
87 # url('admin_settings')
88
88
89 @HasPermissionAllDecorator('hg.admin')
89 @HasPermissionAllDecorator('hg.admin')
90 def new(self, format='html'):
90 def new(self, format='html'):
91 """GET /admin/settings/new: Form to create a new item"""
91 """GET /admin/settings/new: Form to create a new item"""
92 # url('admin_new_setting')
92 # url('admin_new_setting')
93
93
94 @HasPermissionAllDecorator('hg.admin')
94 @HasPermissionAllDecorator('hg.admin')
95 def update(self, setting_id):
95 def update(self, setting_id):
96 """PUT /admin/settings/setting_id: Update an existing item"""
96 """PUT /admin/settings/setting_id: Update an existing item"""
97 # Forms posted to this method should contain a hidden field:
97 # Forms posted to this method should contain a hidden field:
98 # <input type="hidden" name="_method" value="PUT" />
98 # <input type="hidden" name="_method" value="PUT" />
99 # Or using helpers:
99 # Or using helpers:
100 # h.form(url('admin_setting', setting_id=ID),
100 # h.form(url('admin_setting', setting_id=ID),
101 # method='put')
101 # method='put')
102 # url('admin_setting', setting_id=ID)
102 # url('admin_setting', setting_id=ID)
103 if setting_id == 'mapping':
103 if setting_id == 'mapping':
104 rm_obsolete = request.POST.get('destroy', False)
104 rm_obsolete = request.POST.get('destroy', False)
105 log.debug('Rescanning directories with destroy=%s', rm_obsolete)
105 log.debug('Rescanning directories with destroy=%s', rm_obsolete)
106 initial = ScmModel().repo_scan()
106 initial = ScmModel().repo_scan()
107 log.debug('invalidating all repositories')
107 log.debug('invalidating all repositories')
108 for repo_name in initial.keys():
108 for repo_name in initial.keys():
109 invalidate_cache('get_repo_cached_%s' % repo_name)
109 invalidate_cache('get_repo_cached_%s' % repo_name)
110
110
111 added, removed = repo2db_mapper(initial, rm_obsolete)
111 added, removed = repo2db_mapper(initial, rm_obsolete)
112
112
113 h.flash(_('Repositories successfully'
113 h.flash(_('Repositories successfully'
114 ' rescanned added: %s,removed: %s') % (added, removed)
114 ' rescanned added: %s,removed: %s') % (added, removed)
115 , category='success')
115 , category='success')
116
116
117 if setting_id == 'whoosh':
117 if setting_id == 'whoosh':
118 repo_location = self.get_hg_ui_settings()['paths_root_path']
118 repo_location = self.get_hg_ui_settings()['paths_root_path']
119 full_index = request.POST.get('full_index', False)
119 full_index = request.POST.get('full_index', False)
120 task = run_task(tasks.whoosh_index, repo_location, full_index)
120 task = run_task(tasks.whoosh_index, repo_location, full_index)
121
121
122 h.flash(_('Whoosh reindex task scheduled'), category='success')
122 h.flash(_('Whoosh reindex task scheduled'), category='success')
123 if setting_id == 'global':
123 if setting_id == 'global':
124
124
125 application_form = ApplicationSettingsForm()()
125 application_form = ApplicationSettingsForm()()
126 try:
126 try:
127 form_result = application_form.to_python(dict(request.POST))
127 form_result = application_form.to_python(dict(request.POST))
128 settings_model = SettingsModel()
128 settings_model = SettingsModel()
129
129
130 try:
130 try:
131 hgsettings1 = settings_model.get('title')
131 hgsettings1 = settings_model.get('title')
132 hgsettings1.app_settings_value = form_result['rhodecode_title']
132 hgsettings1.app_settings_value = form_result['rhodecode_title']
133
133
134 hgsettings2 = settings_model.get('realm')
134 hgsettings2 = settings_model.get('realm')
135 hgsettings2.app_settings_value = form_result['rhodecode_realm']
135 hgsettings2.app_settings_value = form_result['rhodecode_realm']
136
136
137 hgsettings3 = settings_model.get('ga_code')
137 hgsettings3 = settings_model.get('ga_code')
138 hgsettings3.app_settings_value = form_result['rhodecode_ga_code']
138 hgsettings3.app_settings_value = form_result['rhodecode_ga_code']
139
139
140
140
141
141
142 self.sa.add(hgsettings1)
142 self.sa.add(hgsettings1)
143 self.sa.add(hgsettings2)
143 self.sa.add(hgsettings2)
144 self.sa.add(hgsettings3)
144 self.sa.add(hgsettings3)
145 self.sa.commit()
145 self.sa.commit()
146 set_rhodecode_config(config)
146 set_rhodecode_config(config)
147 h.flash(_('Updated application settings'),
147 h.flash(_('Updated application settings'),
148 category='success')
148 category='success')
149
149
150 except:
150 except:
151 log.error(traceback.format_exc())
151 log.error(traceback.format_exc())
152 h.flash(_('error occurred during updating application settings'),
152 h.flash(_('error occurred during updating application settings'),
153 category='error')
153 category='error')
154
154
155 self.sa.rollback()
155 self.sa.rollback()
156
156
157
157
158 except formencode.Invalid, errors:
158 except formencode.Invalid, errors:
159 return htmlfill.render(
159 return htmlfill.render(
160 render('admin/settings/settings.html'),
160 render('admin/settings/settings.html'),
161 defaults=errors.value,
161 defaults=errors.value,
162 errors=errors.error_dict or {},
162 errors=errors.error_dict or {},
163 prefix_error=False,
163 prefix_error=False,
164 encoding="UTF-8")
164 encoding="UTF-8")
165
165
166 if setting_id == 'mercurial':
166 if setting_id == 'mercurial':
167 application_form = ApplicationUiSettingsForm()()
167 application_form = ApplicationUiSettingsForm()()
168 try:
168 try:
169 form_result = application_form.to_python(dict(request.POST))
169 form_result = application_form.to_python(dict(request.POST))
170
170
171 try:
171 try:
172
172
173 hgsettings1 = self.sa.query(RhodeCodeUi)\
173 hgsettings1 = self.sa.query(RhodeCodeUi)\
174 .filter(RhodeCodeUi.ui_key == 'push_ssl').one()
174 .filter(RhodeCodeUi.ui_key == 'push_ssl').one()
175 hgsettings1.ui_value = form_result['web_push_ssl']
175 hgsettings1.ui_value = form_result['web_push_ssl']
176
176
177 hgsettings2 = self.sa.query(RhodeCodeUi)\
177 hgsettings2 = self.sa.query(RhodeCodeUi)\
178 .filter(RhodeCodeUi.ui_key == '/').one()
178 .filter(RhodeCodeUi.ui_key == '/').one()
179 hgsettings2.ui_value = form_result['paths_root_path']
179 hgsettings2.ui_value = form_result['paths_root_path']
180
180
181
181
182 #HOOKS
182 #HOOKS
183 hgsettings3 = self.sa.query(RhodeCodeUi)\
183 hgsettings3 = self.sa.query(RhodeCodeUi)\
184 .filter(RhodeCodeUi.ui_key == 'changegroup.update').one()
184 .filter(RhodeCodeUi.ui_key == 'changegroup.update').one()
185 hgsettings3.ui_active = bool(form_result['hooks_changegroup_update'])
185 hgsettings3.ui_active = bool(form_result['hooks_changegroup_update'])
186
186
187 hgsettings4 = self.sa.query(RhodeCodeUi)\
187 hgsettings4 = self.sa.query(RhodeCodeUi)\
188 .filter(RhodeCodeUi.ui_key == 'changegroup.repo_size').one()
188 .filter(RhodeCodeUi.ui_key == 'changegroup.repo_size').one()
189 hgsettings4.ui_active = bool(form_result['hooks_changegroup_repo_size'])
189 hgsettings4.ui_active = bool(form_result['hooks_changegroup_repo_size'])
190
190
191 hgsettings5 = self.sa.query(RhodeCodeUi)\
191 hgsettings5 = self.sa.query(RhodeCodeUi)\
192 .filter(RhodeCodeUi.ui_key == 'pretxnchangegroup.push_logger').one()
192 .filter(RhodeCodeUi.ui_key == 'pretxnchangegroup.push_logger').one()
193 hgsettings5.ui_active = bool(form_result['hooks_pretxnchangegroup_push_logger'])
193 hgsettings5.ui_active = bool(form_result['hooks_pretxnchangegroup_push_logger'])
194
194
195 hgsettings6 = self.sa.query(RhodeCodeUi)\
195 hgsettings6 = self.sa.query(RhodeCodeUi)\
196 .filter(RhodeCodeUi.ui_key == 'preoutgoing.pull_logger').one()
196 .filter(RhodeCodeUi.ui_key == 'preoutgoing.pull_logger').one()
197 hgsettings6.ui_active = bool(form_result['hooks_preoutgoing_pull_logger'])
197 hgsettings6.ui_active = bool(form_result['hooks_preoutgoing_pull_logger'])
198
198
199
199
200 self.sa.add(hgsettings1)
200 self.sa.add(hgsettings1)
201 self.sa.add(hgsettings2)
201 self.sa.add(hgsettings2)
202 self.sa.add(hgsettings3)
202 self.sa.add(hgsettings3)
203 self.sa.add(hgsettings4)
203 self.sa.add(hgsettings4)
204 self.sa.add(hgsettings5)
204 self.sa.add(hgsettings5)
205 self.sa.add(hgsettings6)
205 self.sa.add(hgsettings6)
206 self.sa.commit()
206 self.sa.commit()
207
207
208 h.flash(_('Updated mercurial settings'),
208 h.flash(_('Updated mercurial settings'),
209 category='success')
209 category='success')
210
210
211 except:
211 except:
212 log.error(traceback.format_exc())
212 log.error(traceback.format_exc())
213 h.flash(_('error occurred during updating application settings'),
213 h.flash(_('error occurred during updating application settings'),
214 category='error')
214 category='error')
215
215
216 self.sa.rollback()
216 self.sa.rollback()
217
217
218
218
219 except formencode.Invalid, errors:
219 except formencode.Invalid, errors:
220 return htmlfill.render(
220 return htmlfill.render(
221 render('admin/settings/settings.html'),
221 render('admin/settings/settings.html'),
222 defaults=errors.value,
222 defaults=errors.value,
223 errors=errors.error_dict or {},
223 errors=errors.error_dict or {},
224 prefix_error=False,
224 prefix_error=False,
225 encoding="UTF-8")
225 encoding="UTF-8")
226
226
227
227
228
228
229 return redirect(url('admin_settings'))
229 return redirect(url('admin_settings'))
230
230
231 @HasPermissionAllDecorator('hg.admin')
231 @HasPermissionAllDecorator('hg.admin')
232 def delete(self, setting_id):
232 def delete(self, setting_id):
233 """DELETE /admin/settings/setting_id: Delete an existing item"""
233 """DELETE /admin/settings/setting_id: Delete an existing item"""
234 # Forms posted to this method should contain a hidden field:
234 # Forms posted to this method should contain a hidden field:
235 # <input type="hidden" name="_method" value="DELETE" />
235 # <input type="hidden" name="_method" value="DELETE" />
236 # Or using helpers:
236 # Or using helpers:
237 # h.form(url('admin_setting', setting_id=ID),
237 # h.form(url('admin_setting', setting_id=ID),
238 # method='delete')
238 # method='delete')
239 # url('admin_setting', setting_id=ID)
239 # url('admin_setting', setting_id=ID)
240
240
241 @HasPermissionAllDecorator('hg.admin')
241 @HasPermissionAllDecorator('hg.admin')
242 def show(self, setting_id, format='html'):
242 def show(self, setting_id, format='html'):
243 """GET /admin/settings/setting_id: Show a specific item"""
243 """GET /admin/settings/setting_id: Show a specific item"""
244 # url('admin_setting', setting_id=ID)
244 # url('admin_setting', setting_id=ID)
245
245
246 @HasPermissionAllDecorator('hg.admin')
246 @HasPermissionAllDecorator('hg.admin')
247 def edit(self, setting_id, format='html'):
247 def edit(self, setting_id, format='html'):
248 """GET /admin/settings/setting_id/edit: Form to edit an existing item"""
248 """GET /admin/settings/setting_id/edit: Form to edit an existing item"""
249 # url('admin_edit_setting', setting_id=ID)
249 # url('admin_edit_setting', setting_id=ID)
250
250
251 @NotAnonymous()
251 @NotAnonymous()
252 def my_account(self):
252 def my_account(self):
253 """
253 """
254 GET /_admin/my_account Displays info about my account
254 GET /_admin/my_account Displays info about my account
255 """
255 """
256 # url('admin_settings_my_account')
256 # url('admin_settings_my_account')
257
257
258 c.user = UserModel().get(c.rhodecode_user.user_id, cache=False)
258 c.user = UserModel().get(self.rhodecode_user.user_id, cache=False)
259 all_repos = [r.repo_name for r in self.sa.query(Repository)\
259 all_repos = [r.repo_name for r in self.sa.query(Repository)\
260 .filter(Repository.user_id == c.user.user_id)\
260 .filter(Repository.user_id == c.user.user_id)\
261 .order_by(func.lower(Repository.repo_name)).all()]
261 .order_by(func.lower(Repository.repo_name)).all()]
262 c.user_repos = ScmModel().get_repos(all_repos)
262 c.user_repos = ScmModel().get_repos(all_repos)
263
263
264 if c.user.username == 'default':
264 if c.user.username == 'default':
265 h.flash(_("You can't edit this user since it's"
265 h.flash(_("You can't edit this user since it's"
266 " crucial for entire application"), category='warning')
266 " crucial for entire application"), category='warning')
267 return redirect(url('users'))
267 return redirect(url('users'))
268
268
269 defaults = c.user.get_dict()
269 defaults = c.user.get_dict()
270 return htmlfill.render(
270 return htmlfill.render(
271 render('admin/users/user_edit_my_account.html'),
271 render('admin/users/user_edit_my_account.html'),
272 defaults=defaults,
272 defaults=defaults,
273 encoding="UTF-8",
273 encoding="UTF-8",
274 force_defaults=False
274 force_defaults=False
275 )
275 )
276
276
277 def my_account_update(self):
277 def my_account_update(self):
278 """PUT /_admin/my_account_update: Update an existing item"""
278 """PUT /_admin/my_account_update: Update an existing item"""
279 # Forms posted to this method should contain a hidden field:
279 # Forms posted to this method should contain a hidden field:
280 # <input type="hidden" name="_method" value="PUT" />
280 # <input type="hidden" name="_method" value="PUT" />
281 # Or using helpers:
281 # Or using helpers:
282 # h.form(url('admin_settings_my_account_update'),
282 # h.form(url('admin_settings_my_account_update'),
283 # method='put')
283 # method='put')
284 # url('admin_settings_my_account_update', id=ID)
284 # url('admin_settings_my_account_update', id=ID)
285 user_model = UserModel()
285 user_model = UserModel()
286 uid = c.rhodecode_user.user_id
286 uid = self.rhodecode_user.user_id
287 _form = UserForm(edit=True, old_data={'user_id':uid,
287 _form = UserForm(edit=True, old_data={'user_id':uid,
288 'email':c.rhodecode_user.email})()
288 'email':self.rhodecode_user.email})()
289 form_result = {}
289 form_result = {}
290 try:
290 try:
291 form_result = _form.to_python(dict(request.POST))
291 form_result = _form.to_python(dict(request.POST))
292 user_model.update_my_account(uid, form_result)
292 user_model.update_my_account(uid, form_result)
293 h.flash(_('Your account was updated successfully'),
293 h.flash(_('Your account was updated successfully'),
294 category='success')
294 category='success')
295
295
296 except formencode.Invalid, errors:
296 except formencode.Invalid, errors:
297 c.user = user_model.get(c.rhodecode_user.user_id, cache=False)
297 c.user = user_model.get(self.rhodecode_user.user_id, cache=False)
298 c.user = UserModel().get(c.rhodecode_user.user_id, cache=False)
298 c.user = UserModel().get(self.rhodecode_user.user_id, cache=False)
299 all_repos = self.sa.query(Repository)\
299 all_repos = self.sa.query(Repository)\
300 .filter(Repository.user_id == c.user.user_id)\
300 .filter(Repository.user_id == c.user.user_id)\
301 .order_by(func.lower(Repository.repo_name))\
301 .order_by(func.lower(Repository.repo_name))\
302 .all()
302 .all()
303 c.user_repos = ScmModel().get_repos(all_repos)
303 c.user_repos = ScmModel().get_repos(all_repos)
304
304
305 return htmlfill.render(
305 return htmlfill.render(
306 render('admin/users/user_edit_my_account.html'),
306 render('admin/users/user_edit_my_account.html'),
307 defaults=errors.value,
307 defaults=errors.value,
308 errors=errors.error_dict or {},
308 errors=errors.error_dict or {},
309 prefix_error=False,
309 prefix_error=False,
310 encoding="UTF-8")
310 encoding="UTF-8")
311 except Exception:
311 except Exception:
312 log.error(traceback.format_exc())
312 log.error(traceback.format_exc())
313 h.flash(_('error occurred during update of user %s') \
313 h.flash(_('error occurred during update of user %s') \
314 % form_result.get('username'), category='error')
314 % form_result.get('username'), category='error')
315
315
316 return redirect(url('my_account'))
316 return redirect(url('my_account'))
317
317
318 @NotAnonymous()
318 @NotAnonymous()
319 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
319 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
320 def create_repository(self):
320 def create_repository(self):
321 """GET /_admin/create_repository: Form to create a new item"""
321 """GET /_admin/create_repository: Form to create a new item"""
322 new_repo = request.GET.get('repo', '')
322 new_repo = request.GET.get('repo', '')
323 c.new_repo = repo_name_slug(new_repo)
323 c.new_repo = repo_name_slug(new_repo)
324 c.repo_groups = [('', '')]
324 c.repo_groups = [('', '')]
325 c.repo_groups.extend([(x.group_id, x.group_name) for x in self.sa.query(Group).all()])
325 c.repo_groups.extend([(x.group_id, x.group_name) for x in self.sa.query(Group).all()])
326 return render('admin/repos/repo_add_create_repository.html')
326 return render('admin/repos/repo_add_create_repository.html')
327
327
328 def get_hg_ui_settings(self):
328 def get_hg_ui_settings(self):
329 ret = self.sa.query(RhodeCodeUi).all()
329 ret = self.sa.query(RhodeCodeUi).all()
330
330
331 if not ret:
331 if not ret:
332 raise Exception('Could not get application ui settings !')
332 raise Exception('Could not get application ui settings !')
333 settings = {}
333 settings = {}
334 for each in ret:
334 for each in ret:
335 k = each.ui_key
335 k = each.ui_key
336 v = each.ui_value
336 v = each.ui_value
337 if k == '/':
337 if k == '/':
338 k = 'root_path'
338 k = 'root_path'
339
339
340 if k.find('.') != -1:
340 if k.find('.') != -1:
341 k = k.replace('.', '_')
341 k = k.replace('.', '_')
342
342
343 if each.ui_section == 'hooks':
343 if each.ui_section == 'hooks':
344 v = each.ui_active
344 v = each.ui_active
345
345
346 settings[each.ui_section + '_' + k] = v
346 settings[each.ui_section + '_' + k] = v
347
347
348 return settings
348 return settings
@@ -1,248 +1,248 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.journal
3 rhodecode.controllers.journal
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Journal controller for pylons
6 Journal controller for pylons
7
7
8 :created_on: Nov 21, 2010
8 :created_on: Nov 21, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
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 logging
27 import logging
28
28
29 from sqlalchemy import or_
29 from sqlalchemy import or_
30 from sqlalchemy.orm import joinedload, make_transient
30 from sqlalchemy.orm import joinedload, make_transient
31 from webhelpers.paginate import Page
31 from webhelpers.paginate import Page
32 from itertools import groupby
32 from itertools import groupby
33
33
34 from paste.httpexceptions import HTTPInternalServerError
34 from paste.httpexceptions import HTTPInternalServerError
35 from pylons import request, tmpl_context as c, response, url
35 from pylons import request, tmpl_context as c, response, url
36 from pylons.i18n.translation import _
36 from pylons.i18n.translation import _
37 from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed
37 from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed
38
38
39 import rhodecode.lib.helpers as h
39 import rhodecode.lib.helpers as h
40 from rhodecode.lib.auth import LoginRequired, NotAnonymous
40 from rhodecode.lib.auth import LoginRequired, NotAnonymous
41 from rhodecode.lib.base import BaseController, render
41 from rhodecode.lib.base import BaseController, render
42 from rhodecode.model.db import UserLog, UserFollowing
42 from rhodecode.model.db import UserLog, UserFollowing
43
43
44 log = logging.getLogger(__name__)
44 log = logging.getLogger(__name__)
45
45
46 class JournalController(BaseController):
46 class JournalController(BaseController):
47
47
48
48
49
49
50 def __before__(self):
50 def __before__(self):
51 super(JournalController, self).__before__()
51 super(JournalController, self).__before__()
52 c.rhodecode_user = self.rhodecode_user
52 self.rhodecode_user = self.rhodecode_user
53 self.title = _('%s public journal %s feed') % (c.rhodecode_name, '%s')
53 self.title = _('%s public journal %s feed') % (c.rhodecode_name, '%s')
54 self.language = 'en-us'
54 self.language = 'en-us'
55 self.ttl = "5"
55 self.ttl = "5"
56 self.feed_nr = 20
56 self.feed_nr = 20
57
57
58 @LoginRequired()
58 @LoginRequired()
59 @NotAnonymous()
59 @NotAnonymous()
60 def index(self):
60 def index(self):
61 # Return a rendered template
61 # Return a rendered template
62 p = int(request.params.get('page', 1))
62 p = int(request.params.get('page', 1))
63
63
64 c.following = self.sa.query(UserFollowing)\
64 c.following = self.sa.query(UserFollowing)\
65 .filter(UserFollowing.user_id == self.rhodecode_user.user_id)\
65 .filter(UserFollowing.user_id == self.rhodecode_user.user_id)\
66 .options(joinedload(UserFollowing.follows_repository))\
66 .options(joinedload(UserFollowing.follows_repository))\
67 .all()
67 .all()
68
68
69 journal = self._get_journal_data(c.following)
69 journal = self._get_journal_data(c.following)
70
70
71 c.journal_pager = Page(journal, page=p, items_per_page=20)
71 c.journal_pager = Page(journal, page=p, items_per_page=20)
72
72
73 c.journal_day_aggreagate = self._get_daily_aggregate(c.journal_pager)
73 c.journal_day_aggreagate = self._get_daily_aggregate(c.journal_pager)
74
74
75 c.journal_data = render('journal/journal_data.html')
75 c.journal_data = render('journal/journal_data.html')
76 if request.params.get('partial'):
76 if request.params.get('partial'):
77 return c.journal_data
77 return c.journal_data
78 return render('journal/journal.html')
78 return render('journal/journal.html')
79
79
80
80
81 def _get_daily_aggregate(self, journal):
81 def _get_daily_aggregate(self, journal):
82 groups = []
82 groups = []
83 for k, g in groupby(journal, lambda x:x.action_as_day):
83 for k, g in groupby(journal, lambda x:x.action_as_day):
84 user_group = []
84 user_group = []
85 for k2, g2 in groupby(list(g), lambda x:x.user.email):
85 for k2, g2 in groupby(list(g), lambda x:x.user.email):
86 l = list(g2)
86 l = list(g2)
87 user_group.append((l[0].user, l))
87 user_group.append((l[0].user, l))
88
88
89 groups.append((k, user_group,))
89 groups.append((k, user_group,))
90
90
91 return groups
91 return groups
92
92
93
93
94 def _get_journal_data(self, following_repos):
94 def _get_journal_data(self, following_repos):
95 repo_ids = [x.follows_repository.repo_id for x in following_repos
95 repo_ids = [x.follows_repository.repo_id for x in following_repos
96 if x.follows_repository is not None]
96 if x.follows_repository is not None]
97 user_ids = [x.follows_user.user_id for x in following_repos
97 user_ids = [x.follows_user.user_id for x in following_repos
98 if x.follows_user is not None]
98 if x.follows_user is not None]
99
99
100 filtering_criterion = None
100 filtering_criterion = None
101
101
102 if repo_ids and user_ids:
102 if repo_ids and user_ids:
103 filtering_criterion = or_(UserLog.repository_id.in_(repo_ids),
103 filtering_criterion = or_(UserLog.repository_id.in_(repo_ids),
104 UserLog.user_id.in_(user_ids))
104 UserLog.user_id.in_(user_ids))
105 if repo_ids and not user_ids:
105 if repo_ids and not user_ids:
106 filtering_criterion = UserLog.repository_id.in_(repo_ids)
106 filtering_criterion = UserLog.repository_id.in_(repo_ids)
107 if not repo_ids and user_ids:
107 if not repo_ids and user_ids:
108 filtering_criterion = UserLog.user_id.in_(user_ids)
108 filtering_criterion = UserLog.user_id.in_(user_ids)
109 if filtering_criterion is not None:
109 if filtering_criterion is not None:
110 journal = self.sa.query(UserLog)\
110 journal = self.sa.query(UserLog)\
111 .options(joinedload(UserLog.user))\
111 .options(joinedload(UserLog.user))\
112 .options(joinedload(UserLog.repository))\
112 .options(joinedload(UserLog.repository))\
113 .filter(filtering_criterion)\
113 .filter(filtering_criterion)\
114 .order_by(UserLog.action_date.desc())
114 .order_by(UserLog.action_date.desc())
115 else:
115 else:
116 journal = []
116 journal = []
117
117
118
118
119 return journal
119 return journal
120
120
121 @LoginRequired()
121 @LoginRequired()
122 @NotAnonymous()
122 @NotAnonymous()
123 def toggle_following(self):
123 def toggle_following(self):
124 cur_token = request.POST.get('auth_token')
124 cur_token = request.POST.get('auth_token')
125 token = h.get_token()
125 token = h.get_token()
126 if cur_token == token:
126 if cur_token == token:
127
127
128 user_id = request.POST.get('follows_user_id')
128 user_id = request.POST.get('follows_user_id')
129 if user_id:
129 if user_id:
130 try:
130 try:
131 self.scm_model.toggle_following_user(user_id,
131 self.scm_model.toggle_following_user(user_id,
132 self.rhodecode_user.user_id)
132 self.rhodecode_user.user_id)
133 return 'ok'
133 return 'ok'
134 except:
134 except:
135 raise HTTPInternalServerError()
135 raise HTTPInternalServerError()
136
136
137 repo_id = request.POST.get('follows_repo_id')
137 repo_id = request.POST.get('follows_repo_id')
138 if repo_id:
138 if repo_id:
139 try:
139 try:
140 self.scm_model.toggle_following_repo(repo_id,
140 self.scm_model.toggle_following_repo(repo_id,
141 self.rhodecode_user.user_id)
141 self.rhodecode_user.user_id)
142 return 'ok'
142 return 'ok'
143 except:
143 except:
144 raise HTTPInternalServerError()
144 raise HTTPInternalServerError()
145
145
146
146
147 log.debug('token mismatch %s vs %s', cur_token, token)
147 log.debug('token mismatch %s vs %s', cur_token, token)
148 raise HTTPInternalServerError()
148 raise HTTPInternalServerError()
149
149
150
150
151
151
152 @LoginRequired()
152 @LoginRequired()
153 def public_journal(self):
153 def public_journal(self):
154 # Return a rendered template
154 # Return a rendered template
155 p = int(request.params.get('page', 1))
155 p = int(request.params.get('page', 1))
156
156
157 c.following = self.sa.query(UserFollowing)\
157 c.following = self.sa.query(UserFollowing)\
158 .filter(UserFollowing.user_id == self.rhodecode_user.user_id)\
158 .filter(UserFollowing.user_id == self.rhodecode_user.user_id)\
159 .options(joinedload(UserFollowing.follows_repository))\
159 .options(joinedload(UserFollowing.follows_repository))\
160 .all()
160 .all()
161
161
162 journal = self._get_journal_data(c.following)
162 journal = self._get_journal_data(c.following)
163
163
164 c.journal_pager = Page(journal, page=p, items_per_page=20)
164 c.journal_pager = Page(journal, page=p, items_per_page=20)
165
165
166 c.journal_day_aggreagate = self._get_daily_aggregate(c.journal_pager)
166 c.journal_day_aggreagate = self._get_daily_aggregate(c.journal_pager)
167
167
168 c.journal_data = render('journal/journal_data.html')
168 c.journal_data = render('journal/journal_data.html')
169 if request.params.get('partial'):
169 if request.params.get('partial'):
170 return c.journal_data
170 return c.journal_data
171 return render('journal/public_journal.html')
171 return render('journal/public_journal.html')
172
172
173
173
174 @LoginRequired(api_access=True)
174 @LoginRequired(api_access=True)
175 def public_journal_atom(self):
175 def public_journal_atom(self):
176 """
176 """
177 Produce an atom-1.0 feed via feedgenerator module
177 Produce an atom-1.0 feed via feedgenerator module
178 """
178 """
179 c.following = self.sa.query(UserFollowing)\
179 c.following = self.sa.query(UserFollowing)\
180 .filter(UserFollowing.user_id == self.rhodecode_user.user_id)\
180 .filter(UserFollowing.user_id == self.rhodecode_user.user_id)\
181 .options(joinedload(UserFollowing.follows_repository))\
181 .options(joinedload(UserFollowing.follows_repository))\
182 .all()
182 .all()
183
183
184 journal = self._get_journal_data(c.following)
184 journal = self._get_journal_data(c.following)
185
185
186 feed = Atom1Feed(title=self.title % 'atom',
186 feed = Atom1Feed(title=self.title % 'atom',
187 link=url('public_journal_atom', qualified=True),
187 link=url('public_journal_atom', qualified=True),
188 description=_('Public journal'),
188 description=_('Public journal'),
189 language=self.language,
189 language=self.language,
190 ttl=self.ttl)
190 ttl=self.ttl)
191
191
192 for entry in journal[:self.feed_nr]:
192 for entry in journal[:self.feed_nr]:
193 #tmpl = h.action_parser(entry)[0]
193 #tmpl = h.action_parser(entry)[0]
194 action, action_extra = h.action_parser(entry, feed=True)
194 action, action_extra = h.action_parser(entry, feed=True)
195 title = "%s - %s %s" % (entry.user.short_contact, action,
195 title = "%s - %s %s" % (entry.user.short_contact, action,
196 entry.repository.repo_name)
196 entry.repository.repo_name)
197 desc = action_extra()
197 desc = action_extra()
198 feed.add_item(title=title,
198 feed.add_item(title=title,
199 pubdate=entry.action_date,
199 pubdate=entry.action_date,
200 link=url('', qualified=True),
200 link=url('', qualified=True),
201 author_email=entry.user.email,
201 author_email=entry.user.email,
202 author_name=entry.user.full_contact,
202 author_name=entry.user.full_contact,
203 description=desc)
203 description=desc)
204
204
205 response.content_type = feed.mime_type
205 response.content_type = feed.mime_type
206 return feed.writeString('utf-8')
206 return feed.writeString('utf-8')
207
207
208 @LoginRequired(api_access=True)
208 @LoginRequired(api_access=True)
209 def public_journal_rss(self):
209 def public_journal_rss(self):
210 """
210 """
211 Produce an rss2 feed via feedgenerator module
211 Produce an rss2 feed via feedgenerator module
212 """
212 """
213 c.following = self.sa.query(UserFollowing)\
213 c.following = self.sa.query(UserFollowing)\
214 .filter(UserFollowing.user_id == self.rhodecode_user.user_id)\
214 .filter(UserFollowing.user_id == self.rhodecode_user.user_id)\
215 .options(joinedload(UserFollowing.follows_repository))\
215 .options(joinedload(UserFollowing.follows_repository))\
216 .all()
216 .all()
217
217
218 journal = self._get_journal_data(c.following)
218 journal = self._get_journal_data(c.following)
219
219
220 feed = Rss201rev2Feed(title=self.title % 'rss',
220 feed = Rss201rev2Feed(title=self.title % 'rss',
221 link=url('public_journal_rss', qualified=True),
221 link=url('public_journal_rss', qualified=True),
222 description=_('Public journal'),
222 description=_('Public journal'),
223 language=self.language,
223 language=self.language,
224 ttl=self.ttl)
224 ttl=self.ttl)
225
225
226 for entry in journal[:self.feed_nr]:
226 for entry in journal[:self.feed_nr]:
227 #tmpl = h.action_parser(entry)[0]
227 #tmpl = h.action_parser(entry)[0]
228 action, action_extra = h.action_parser(entry, feed=True)
228 action, action_extra = h.action_parser(entry, feed=True)
229 title = "%s - %s %s" % (entry.user.short_contact, action,
229 title = "%s - %s %s" % (entry.user.short_contact, action,
230 entry.repository.repo_name)
230 entry.repository.repo_name)
231 desc = action_extra()
231 desc = action_extra()
232 feed.add_item(title=title,
232 feed.add_item(title=title,
233 pubdate=entry.action_date,
233 pubdate=entry.action_date,
234 link=url('', qualified=True),
234 link=url('', qualified=True),
235 author_email=entry.user.email,
235 author_email=entry.user.email,
236 author_name=entry.user.full_contact,
236 author_name=entry.user.full_contact,
237 description=desc)
237 description=desc)
238
238
239 response.content_type = feed.mime_type
239 response.content_type = feed.mime_type
240 return feed.writeString('utf-8')
240 return feed.writeString('utf-8')
241
241
242
242
243
243
244
244
245
245
246
246
247
247
248
248
@@ -1,150 +1,150 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.login
3 rhodecode.controllers.login
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Login controller for rhodeocode
6 Login controller for rhodeocode
7
7
8 :created_on: Apr 22, 2010
8 :created_on: Apr 22, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
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 formencode
29 import formencode
30
30
31 from formencode import htmlfill
31 from formencode import htmlfill
32
32
33 from pylons.i18n.translation import _
33 from pylons.i18n.translation import _
34 from pylons.controllers.util import abort, redirect
34 from pylons.controllers.util import abort, redirect
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
36
37 import rhodecode.lib.helpers as h
37 import rhodecode.lib.helpers as h
38 from rhodecode.lib.auth import AuthUser, HasPermissionAnyDecorator
38 from rhodecode.lib.auth import AuthUser, HasPermissionAnyDecorator
39 from rhodecode.lib.base import BaseController, render
39 from rhodecode.lib.base import BaseController, render
40 from rhodecode.model.forms import LoginForm, RegisterForm, PasswordResetForm
40 from rhodecode.model.forms import LoginForm, RegisterForm, PasswordResetForm
41 from rhodecode.model.user import UserModel
41 from rhodecode.model.user import UserModel
42
42
43
43
44 log = logging.getLogger(__name__)
44 log = logging.getLogger(__name__)
45
45
46 class LoginController(BaseController):
46 class LoginController(BaseController):
47
47
48 def __before__(self):
48 def __before__(self):
49 super(LoginController, self).__before__()
49 super(LoginController, self).__before__()
50
50
51 def index(self):
51 def index(self):
52 #redirect if already logged in
52 #redirect if already logged in
53 c.came_from = request.GET.get('came_from', None)
53 c.came_from = request.GET.get('came_from', None)
54
54
55 if c.rhodecode_user.is_authenticated \
55 if self.rhodecode_user.is_authenticated \
56 and c.rhodecode_user.username != 'default':
56 and self.rhodecode_user.username != 'default':
57
57
58 return redirect(url('home'))
58 return redirect(url('home'))
59
59
60 if request.POST:
60 if request.POST:
61 #import Login Form validator class
61 #import Login Form validator class
62 login_form = LoginForm()
62 login_form = LoginForm()
63 try:
63 try:
64 c.form_result = login_form.to_python(dict(request.POST))
64 c.form_result = login_form.to_python(dict(request.POST))
65 #form checks for username/password, now we're authenticated
65 #form checks for username/password, now we're authenticated
66 username = c.form_result['username']
66 username = c.form_result['username']
67 user = UserModel().get_by_username(username,
67 user = UserModel().get_by_username(username,
68 case_insensitive=True)
68 case_insensitive=True)
69 auth_user = AuthUser(user.user_id)
69 auth_user = AuthUser(user.user_id)
70 auth_user.set_authenticated()
70 auth_user.set_authenticated()
71 session['rhodecode_user'] = auth_user
71 session['rhodecode_user'] = auth_user
72 session.save()
72 session.save()
73
73
74 log.info('user %s is now authenticated and stored in session',
74 log.info('user %s is now authenticated and stored in session',
75 username)
75 username)
76 user.update_lastlogin()
76 user.update_lastlogin()
77
77
78 if c.came_from:
78 if c.came_from:
79 return redirect(c.came_from)
79 return redirect(c.came_from)
80 else:
80 else:
81 return redirect(url('home'))
81 return redirect(url('home'))
82
82
83 except formencode.Invalid, errors:
83 except formencode.Invalid, errors:
84 return htmlfill.render(
84 return htmlfill.render(
85 render('/login.html'),
85 render('/login.html'),
86 defaults=errors.value,
86 defaults=errors.value,
87 errors=errors.error_dict or {},
87 errors=errors.error_dict or {},
88 prefix_error=False,
88 prefix_error=False,
89 encoding="UTF-8")
89 encoding="UTF-8")
90
90
91 return render('/login.html')
91 return render('/login.html')
92
92
93 @HasPermissionAnyDecorator('hg.admin', 'hg.register.auto_activate',
93 @HasPermissionAnyDecorator('hg.admin', 'hg.register.auto_activate',
94 'hg.register.manual_activate')
94 'hg.register.manual_activate')
95 def register(self):
95 def register(self):
96 user_model = UserModel()
96 user_model = UserModel()
97 c.auto_active = False
97 c.auto_active = False
98 for perm in user_model.get_by_username('default', cache=False).user_perms:
98 for perm in user_model.get_by_username('default', cache=False).user_perms:
99 if perm.permission.permission_name == 'hg.register.auto_activate':
99 if perm.permission.permission_name == 'hg.register.auto_activate':
100 c.auto_active = True
100 c.auto_active = True
101 break
101 break
102
102
103 if request.POST:
103 if request.POST:
104
104
105 register_form = RegisterForm()()
105 register_form = RegisterForm()()
106 try:
106 try:
107 form_result = register_form.to_python(dict(request.POST))
107 form_result = register_form.to_python(dict(request.POST))
108 form_result['active'] = c.auto_active
108 form_result['active'] = c.auto_active
109 user_model.create_registration(form_result)
109 user_model.create_registration(form_result)
110 h.flash(_('You have successfully registered into rhodecode'),
110 h.flash(_('You have successfully registered into rhodecode'),
111 category='success')
111 category='success')
112 return redirect(url('login_home'))
112 return redirect(url('login_home'))
113
113
114 except formencode.Invalid, errors:
114 except formencode.Invalid, errors:
115 return htmlfill.render(
115 return htmlfill.render(
116 render('/register.html'),
116 render('/register.html'),
117 defaults=errors.value,
117 defaults=errors.value,
118 errors=errors.error_dict or {},
118 errors=errors.error_dict or {},
119 prefix_error=False,
119 prefix_error=False,
120 encoding="UTF-8")
120 encoding="UTF-8")
121
121
122 return render('/register.html')
122 return render('/register.html')
123
123
124 def password_reset(self):
124 def password_reset(self):
125 user_model = UserModel()
125 user_model = UserModel()
126 if request.POST:
126 if request.POST:
127
127
128 password_reset_form = PasswordResetForm()()
128 password_reset_form = PasswordResetForm()()
129 try:
129 try:
130 form_result = password_reset_form.to_python(dict(request.POST))
130 form_result = password_reset_form.to_python(dict(request.POST))
131 user_model.reset_password(form_result)
131 user_model.reset_password(form_result)
132 h.flash(_('Your new password was sent'),
132 h.flash(_('Your new password was sent'),
133 category='success')
133 category='success')
134 return redirect(url('login_home'))
134 return redirect(url('login_home'))
135
135
136 except formencode.Invalid, errors:
136 except formencode.Invalid, errors:
137 return htmlfill.render(
137 return htmlfill.render(
138 render('/password_reset.html'),
138 render('/password_reset.html'),
139 defaults=errors.value,
139 defaults=errors.value,
140 errors=errors.error_dict or {},
140 errors=errors.error_dict or {},
141 prefix_error=False,
141 prefix_error=False,
142 encoding="UTF-8")
142 encoding="UTF-8")
143
143
144 return render('/password_reset.html')
144 return render('/password_reset.html')
145
145
146 def logout(self):
146 def logout(self):
147 del session['rhodecode_user']
147 del session['rhodecode_user']
148 session.save()
148 session.save()
149 log.info('Logging out and setting user as Empty')
149 log.info('Logging out and setting user as Empty')
150 redirect(url('home'))
150 redirect(url('home'))
@@ -1,209 +1,209 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.settings
3 rhodecode.controllers.settings
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Settings controller for rhodecode
6 Settings controller for rhodecode
7
7
8 :created_on: Jun 30, 2010
8 :created_on: Jun 30, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
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
31
32 from formencode import htmlfill
32 from formencode import htmlfill
33
33
34 from pylons import tmpl_context as c, request, url
34 from pylons import tmpl_context as c, request, url
35 from pylons.controllers.util import redirect
35 from pylons.controllers.util import redirect
36 from pylons.i18n.translation import _
36 from pylons.i18n.translation import _
37
37
38 import rhodecode.lib.helpers as h
38 import rhodecode.lib.helpers as h
39
39
40 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAllDecorator, \
40 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAllDecorator, \
41 HasRepoPermissionAnyDecorator, NotAnonymous
41 HasRepoPermissionAnyDecorator, NotAnonymous
42 from rhodecode.lib.base import BaseRepoController, render
42 from rhodecode.lib.base import BaseRepoController, render
43 from rhodecode.lib.utils import invalidate_cache, action_logger
43 from rhodecode.lib.utils import invalidate_cache, action_logger
44
44
45 from rhodecode.model.forms import RepoSettingsForm, RepoForkForm
45 from rhodecode.model.forms import RepoSettingsForm, RepoForkForm
46 from rhodecode.model.repo import RepoModel
46 from rhodecode.model.repo import RepoModel
47 from rhodecode.model.db import User
47 from rhodecode.model.db import User
48
48
49 log = logging.getLogger(__name__)
49 log = logging.getLogger(__name__)
50
50
51 class SettingsController(BaseRepoController):
51 class SettingsController(BaseRepoController):
52
52
53 @LoginRequired()
53 @LoginRequired()
54 def __before__(self):
54 def __before__(self):
55 super(SettingsController, self).__before__()
55 super(SettingsController, self).__before__()
56
56
57 @HasRepoPermissionAllDecorator('repository.admin')
57 @HasRepoPermissionAllDecorator('repository.admin')
58 def index(self, repo_name):
58 def index(self, repo_name):
59 repo_model = RepoModel()
59 repo_model = RepoModel()
60 c.repo_info = repo = repo_model.get_by_repo_name(repo_name)
60 c.repo_info = repo = repo_model.get_by_repo_name(repo_name)
61 if not repo:
61 if not repo:
62 h.flash(_('%s repository is not mapped to db perhaps'
62 h.flash(_('%s repository is not mapped to db perhaps'
63 ' it was created or renamed from the file system'
63 ' it was created or renamed from the file system'
64 ' please run the application again'
64 ' please run the application again'
65 ' in order to rescan repositories') % repo_name,
65 ' in order to rescan repositories') % repo_name,
66 category='error')
66 category='error')
67
67
68 return redirect(url('home'))
68 return redirect(url('home'))
69
69
70 c.users_array = repo_model.get_users_js()
70 c.users_array = repo_model.get_users_js()
71 c.users_groups_array = repo_model.get_users_groups_js()
71 c.users_groups_array = repo_model.get_users_groups_js()
72
72
73 defaults = c.repo_info.get_dict()
73 defaults = c.repo_info.get_dict()
74
74
75 #fill owner
75 #fill owner
76 if c.repo_info.user:
76 if c.repo_info.user:
77 defaults.update({'user':c.repo_info.user.username})
77 defaults.update({'user':c.repo_info.user.username})
78 else:
78 else:
79 replacement_user = self.sa.query(User)\
79 replacement_user = self.sa.query(User)\
80 .filter(User.admin == True).first().username
80 .filter(User.admin == True).first().username
81 defaults.update({'user':replacement_user})
81 defaults.update({'user':replacement_user})
82
82
83 #fill repository users
83 #fill repository users
84 for p in c.repo_info.repo_to_perm:
84 for p in c.repo_info.repo_to_perm:
85 defaults.update({'u_perm_%s' % p.user.username:
85 defaults.update({'u_perm_%s' % p.user.username:
86 p.permission.permission_name})
86 p.permission.permission_name})
87
87
88 #fill repository groups
88 #fill repository groups
89 for p in c.repo_info.users_group_to_perm:
89 for p in c.repo_info.users_group_to_perm:
90 defaults.update({'g_perm_%s' % p.users_group.users_group_name:
90 defaults.update({'g_perm_%s' % p.users_group.users_group_name:
91 p.permission.permission_name})
91 p.permission.permission_name})
92
92
93 return htmlfill.render(
93 return htmlfill.render(
94 render('settings/repo_settings.html'),
94 render('settings/repo_settings.html'),
95 defaults=defaults,
95 defaults=defaults,
96 encoding="UTF-8",
96 encoding="UTF-8",
97 force_defaults=False
97 force_defaults=False
98 )
98 )
99
99
100 @HasRepoPermissionAllDecorator('repository.admin')
100 @HasRepoPermissionAllDecorator('repository.admin')
101 def update(self, repo_name):
101 def update(self, repo_name):
102 repo_model = RepoModel()
102 repo_model = RepoModel()
103 changed_name = repo_name
103 changed_name = repo_name
104 _form = RepoSettingsForm(edit=True, old_data={'repo_name':repo_name})()
104 _form = RepoSettingsForm(edit=True, old_data={'repo_name':repo_name})()
105 try:
105 try:
106 form_result = _form.to_python(dict(request.POST))
106 form_result = _form.to_python(dict(request.POST))
107 repo_model.update(repo_name, form_result)
107 repo_model.update(repo_name, form_result)
108 invalidate_cache('get_repo_cached_%s' % repo_name)
108 invalidate_cache('get_repo_cached_%s' % repo_name)
109 h.flash(_('Repository %s updated successfully' % repo_name),
109 h.flash(_('Repository %s updated successfully' % repo_name),
110 category='success')
110 category='success')
111 changed_name = form_result['repo_name']
111 changed_name = form_result['repo_name']
112 action_logger(self.rhodecode_user, 'user_updated_repo',
112 action_logger(self.rhodecode_user, 'user_updated_repo',
113 changed_name, '', self.sa)
113 changed_name, '', self.sa)
114 except formencode.Invalid, errors:
114 except formencode.Invalid, errors:
115 c.repo_info = repo_model.get_by_repo_name(repo_name)
115 c.repo_info = repo_model.get_by_repo_name(repo_name)
116 c.users_array = repo_model.get_users_js()
116 c.users_array = repo_model.get_users_js()
117 errors.value.update({'user':c.repo_info.user.username})
117 errors.value.update({'user':c.repo_info.user.username})
118 return htmlfill.render(
118 return htmlfill.render(
119 render('settings/repo_settings.html'),
119 render('settings/repo_settings.html'),
120 defaults=errors.value,
120 defaults=errors.value,
121 errors=errors.error_dict or {},
121 errors=errors.error_dict or {},
122 prefix_error=False,
122 prefix_error=False,
123 encoding="UTF-8")
123 encoding="UTF-8")
124 except Exception:
124 except Exception:
125 log.error(traceback.format_exc())
125 log.error(traceback.format_exc())
126 h.flash(_('error occurred during update of repository %s') \
126 h.flash(_('error occurred during update of repository %s') \
127 % repo_name, category='error')
127 % repo_name, category='error')
128
128
129 return redirect(url('repo_settings_home', repo_name=changed_name))
129 return redirect(url('repo_settings_home', repo_name=changed_name))
130
130
131
131
132 @HasRepoPermissionAllDecorator('repository.admin')
132 @HasRepoPermissionAllDecorator('repository.admin')
133 def delete(self, repo_name):
133 def delete(self, repo_name):
134 """DELETE /repos/repo_name: Delete an existing item"""
134 """DELETE /repos/repo_name: Delete an existing item"""
135 # Forms posted to this method should contain a hidden field:
135 # Forms posted to this method should contain a hidden field:
136 # <input type="hidden" name="_method" value="DELETE" />
136 # <input type="hidden" name="_method" value="DELETE" />
137 # Or using helpers:
137 # Or using helpers:
138 # h.form(url('repo_settings_delete', repo_name=ID),
138 # h.form(url('repo_settings_delete', repo_name=ID),
139 # method='delete')
139 # method='delete')
140 # url('repo_settings_delete', repo_name=ID)
140 # url('repo_settings_delete', repo_name=ID)
141
141
142 repo_model = RepoModel()
142 repo_model = RepoModel()
143 repo = repo_model.get_by_repo_name(repo_name)
143 repo = repo_model.get_by_repo_name(repo_name)
144 if not repo:
144 if not repo:
145 h.flash(_('%s repository is not mapped to db perhaps'
145 h.flash(_('%s repository is not mapped to db perhaps'
146 ' it was moved or renamed from the filesystem'
146 ' it was moved or renamed from the filesystem'
147 ' please run the application again'
147 ' please run the application again'
148 ' in order to rescan repositories') % repo_name,
148 ' in order to rescan repositories') % repo_name,
149 category='error')
149 category='error')
150
150
151 return redirect(url('home'))
151 return redirect(url('home'))
152 try:
152 try:
153 action_logger(self.rhodecode_user, 'user_deleted_repo',
153 action_logger(self.rhodecode_user, 'user_deleted_repo',
154 repo_name, '', self.sa)
154 repo_name, '', self.sa)
155 repo_model.delete(repo)
155 repo_model.delete(repo)
156 invalidate_cache('get_repo_cached_%s' % repo_name)
156 invalidate_cache('get_repo_cached_%s' % repo_name)
157 h.flash(_('deleted repository %s') % repo_name, category='success')
157 h.flash(_('deleted repository %s') % repo_name, category='success')
158 except Exception:
158 except Exception:
159 h.flash(_('An error occurred during deletion of %s') % repo_name,
159 h.flash(_('An error occurred during deletion of %s') % repo_name,
160 category='error')
160 category='error')
161
161
162 return redirect(url('home'))
162 return redirect(url('home'))
163
163
164 @NotAnonymous()
164 @NotAnonymous()
165 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
165 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
166 'repository.admin')
166 'repository.admin')
167 def fork(self, repo_name):
167 def fork(self, repo_name):
168 repo_model = RepoModel()
168 repo_model = RepoModel()
169 c.repo_info = repo = repo_model.get_by_repo_name(repo_name)
169 c.repo_info = repo = repo_model.get_by_repo_name(repo_name)
170 if not repo:
170 if not repo:
171 h.flash(_('%s repository is not mapped to db perhaps'
171 h.flash(_('%s repository is not mapped to db perhaps'
172 ' it was created or renamed from the file system'
172 ' it was created or renamed from the file system'
173 ' please run the application again'
173 ' please run the application again'
174 ' in order to rescan repositories') % repo_name,
174 ' in order to rescan repositories') % repo_name,
175 category='error')
175 category='error')
176
176
177 return redirect(url('home'))
177 return redirect(url('home'))
178
178
179 return render('settings/repo_fork.html')
179 return render('settings/repo_fork.html')
180
180
181 @NotAnonymous()
181 @NotAnonymous()
182 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
182 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
183 'repository.admin')
183 'repository.admin')
184 def fork_create(self, repo_name):
184 def fork_create(self, repo_name):
185 repo_model = RepoModel()
185 repo_model = RepoModel()
186 c.repo_info = repo_model.get_by_repo_name(repo_name)
186 c.repo_info = repo_model.get_by_repo_name(repo_name)
187 _form = RepoForkForm(old_data={'repo_type':c.repo_info.repo_type})()
187 _form = RepoForkForm(old_data={'repo_type':c.repo_info.repo_type})()
188 form_result = {}
188 form_result = {}
189 try:
189 try:
190 form_result = _form.to_python(dict(request.POST))
190 form_result = _form.to_python(dict(request.POST))
191 form_result.update({'repo_name':repo_name})
191 form_result.update({'repo_name':repo_name})
192 repo_model.create_fork(form_result, c.rhodecode_user)
192 repo_model.create_fork(form_result, self.rhodecode_user)
193 h.flash(_('forked %s repository as %s') \
193 h.flash(_('forked %s repository as %s') \
194 % (repo_name, form_result['fork_name']),
194 % (repo_name, form_result['fork_name']),
195 category='success')
195 category='success')
196 action_logger(self.rhodecode_user,
196 action_logger(self.rhodecode_user,
197 'user_forked_repo:%s' % form_result['fork_name'],
197 'user_forked_repo:%s' % form_result['fork_name'],
198 repo_name, '', self.sa)
198 repo_name, '', self.sa)
199 except formencode.Invalid, errors:
199 except formencode.Invalid, errors:
200 c.new_repo = errors.value['fork_name']
200 c.new_repo = errors.value['fork_name']
201 r = render('settings/repo_fork.html')
201 r = render('settings/repo_fork.html')
202
202
203 return htmlfill.render(
203 return htmlfill.render(
204 r,
204 r,
205 defaults=errors.value,
205 defaults=errors.value,
206 errors=errors.error_dict or {},
206 errors=errors.error_dict or {},
207 prefix_error=False,
207 prefix_error=False,
208 encoding="UTF-8")
208 encoding="UTF-8")
209 return redirect(url('home'))
209 return redirect(url('home'))
@@ -1,169 +1,169 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.summary
3 rhodecode.controllers.summary
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Summary controller for Rhodecode
6 Summary controller for Rhodecode
7
7
8 :created_on: Apr 18, 2010
8 :created_on: Apr 18, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
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 calendar
28 import calendar
29 import logging
29 import logging
30 from time import mktime
30 from time import mktime
31 from datetime import datetime, timedelta, date
31 from datetime import datetime, timedelta, date
32
32
33 from vcs.exceptions import ChangesetError
33 from vcs.exceptions import ChangesetError
34
34
35 from pylons import tmpl_context as c, request, url
35 from pylons import tmpl_context as c, request, url
36 from pylons.i18n.translation import _
36 from pylons.i18n.translation import _
37
37
38 from rhodecode.model.db import Statistics
38 from rhodecode.model.db import Statistics
39
39
40 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
40 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
41 from rhodecode.lib.base import BaseRepoController, render
41 from rhodecode.lib.base import BaseRepoController, render
42 from rhodecode.lib.utils import OrderedDict, EmptyChangeset
42 from rhodecode.lib.utils import OrderedDict, EmptyChangeset
43
43
44 from rhodecode.lib.celerylib import run_task
44 from rhodecode.lib.celerylib import run_task
45 from rhodecode.lib.celerylib.tasks import get_commits_stats
45 from rhodecode.lib.celerylib.tasks import get_commits_stats
46 from rhodecode.lib.helpers import RepoPage
46 from rhodecode.lib.helpers import RepoPage
47
47
48 try:
48 try:
49 import json
49 import json
50 except ImportError:
50 except ImportError:
51 #python 2.5 compatibility
51 #python 2.5 compatibility
52 import simplejson as json
52 import simplejson as json
53 log = logging.getLogger(__name__)
53 log = logging.getLogger(__name__)
54
54
55 class SummaryController(BaseRepoController):
55 class SummaryController(BaseRepoController):
56
56
57 @LoginRequired()
57 @LoginRequired()
58 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
58 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
59 'repository.admin')
59 'repository.admin')
60 def __before__(self):
60 def __before__(self):
61 super(SummaryController, self).__before__()
61 super(SummaryController, self).__before__()
62
62
63 def index(self):
63 def index(self):
64 c.repo, dbrepo = self.scm_model.get(c.repo_name)
64 c.repo, dbrepo = self.scm_model.get(c.repo_name)
65 c.dbrepo = dbrepo
65 c.dbrepo = dbrepo
66
66
67 c.following = self.scm_model.is_following_repo(c.repo_name,
67 c.following = self.scm_model.is_following_repo(c.repo_name,
68 c.rhodecode_user.user_id)
68 self.rhodecode_user.user_id)
69 def url_generator(**kw):
69 def url_generator(**kw):
70 return url('shortlog_home', repo_name=c.repo_name, **kw)
70 return url('shortlog_home', repo_name=c.repo_name, **kw)
71
71
72 c.repo_changesets = RepoPage(c.repo, page=1, items_per_page=10,
72 c.repo_changesets = RepoPage(c.repo, page=1, items_per_page=10,
73 url=url_generator)
73 url=url_generator)
74
74
75 e = request.environ
75 e = request.environ
76
76
77 if self.rhodecode_user.username == 'default':
77 if self.rhodecode_user.username == 'default':
78 #for default(anonymous) user we don't need to pass credentials
78 #for default(anonymous) user we don't need to pass credentials
79 username = ''
79 username = ''
80 password = ''
80 password = ''
81 else:
81 else:
82 username = str(c.rhodecode_user.username)
82 username = str(self.rhodecode_user.username)
83 password = '@'
83 password = '@'
84
84
85 uri = u'%(protocol)s://%(user)s%(password)s%(host)s%(prefix)s/%(repo_name)s' % {
85 uri = u'%(protocol)s://%(user)s%(password)s%(host)s%(prefix)s/%(repo_name)s' % {
86 'protocol': e.get('wsgi.url_scheme'),
86 'protocol': e.get('wsgi.url_scheme'),
87 'user':username,
87 'user':username,
88 'password':password,
88 'password':password,
89 'host':e.get('HTTP_HOST'),
89 'host':e.get('HTTP_HOST'),
90 'prefix':e.get('SCRIPT_NAME'),
90 'prefix':e.get('SCRIPT_NAME'),
91 'repo_name':c.repo_name, }
91 'repo_name':c.repo_name, }
92 c.clone_repo_url = uri
92 c.clone_repo_url = uri
93 c.repo_tags = OrderedDict()
93 c.repo_tags = OrderedDict()
94 for name, hash in c.repo.tags.items()[:10]:
94 for name, hash in c.repo.tags.items()[:10]:
95 try:
95 try:
96 c.repo_tags[name] = c.repo.get_changeset(hash)
96 c.repo_tags[name] = c.repo.get_changeset(hash)
97 except ChangesetError:
97 except ChangesetError:
98 c.repo_tags[name] = EmptyChangeset(hash)
98 c.repo_tags[name] = EmptyChangeset(hash)
99
99
100 c.repo_branches = OrderedDict()
100 c.repo_branches = OrderedDict()
101 for name, hash in c.repo.branches.items()[:10]:
101 for name, hash in c.repo.branches.items()[:10]:
102 try:
102 try:
103 c.repo_branches[name] = c.repo.get_changeset(hash)
103 c.repo_branches[name] = c.repo.get_changeset(hash)
104 except ChangesetError:
104 except ChangesetError:
105 c.repo_branches[name] = EmptyChangeset(hash)
105 c.repo_branches[name] = EmptyChangeset(hash)
106
106
107 td = date.today() + timedelta(days=1)
107 td = date.today() + timedelta(days=1)
108 td_1m = td - timedelta(days=calendar.mdays[td.month])
108 td_1m = td - timedelta(days=calendar.mdays[td.month])
109 td_1y = td - timedelta(days=365)
109 td_1y = td - timedelta(days=365)
110
110
111 ts_min_m = mktime(td_1m.timetuple())
111 ts_min_m = mktime(td_1m.timetuple())
112 ts_min_y = mktime(td_1y.timetuple())
112 ts_min_y = mktime(td_1y.timetuple())
113 ts_max_y = mktime(td.timetuple())
113 ts_max_y = mktime(td.timetuple())
114
114
115 if dbrepo.enable_statistics:
115 if dbrepo.enable_statistics:
116 c.no_data_msg = _('No data loaded yet')
116 c.no_data_msg = _('No data loaded yet')
117 run_task(get_commits_stats, c.repo.name, ts_min_y, ts_max_y)
117 run_task(get_commits_stats, c.repo.name, ts_min_y, ts_max_y)
118 else:
118 else:
119 c.no_data_msg = _('Statistics are disabled for this repository')
119 c.no_data_msg = _('Statistics are disabled for this repository')
120 c.ts_min = ts_min_m
120 c.ts_min = ts_min_m
121 c.ts_max = ts_max_y
121 c.ts_max = ts_max_y
122
122
123 stats = self.sa.query(Statistics)\
123 stats = self.sa.query(Statistics)\
124 .filter(Statistics.repository == dbrepo)\
124 .filter(Statistics.repository == dbrepo)\
125 .scalar()
125 .scalar()
126
126
127
127
128 if stats and stats.languages:
128 if stats and stats.languages:
129 c.no_data = False is dbrepo.enable_statistics
129 c.no_data = False is dbrepo.enable_statistics
130 lang_stats = json.loads(stats.languages)
130 lang_stats = json.loads(stats.languages)
131 c.commit_data = stats.commit_activity
131 c.commit_data = stats.commit_activity
132 c.overview_data = stats.commit_activity_combined
132 c.overview_data = stats.commit_activity_combined
133 c.trending_languages = json.dumps(OrderedDict(
133 c.trending_languages = json.dumps(OrderedDict(
134 sorted(lang_stats.items(), reverse=True,
134 sorted(lang_stats.items(), reverse=True,
135 key=lambda k: k[1])[:10]
135 key=lambda k: k[1])[:10]
136 )
136 )
137 )
137 )
138 else:
138 else:
139 c.commit_data = json.dumps({})
139 c.commit_data = json.dumps({})
140 c.overview_data = json.dumps([[ts_min_y, 0], [ts_max_y, 10] ])
140 c.overview_data = json.dumps([[ts_min_y, 0], [ts_max_y, 10] ])
141 c.trending_languages = json.dumps({})
141 c.trending_languages = json.dumps({})
142 c.no_data = True
142 c.no_data = True
143
143
144 c.enable_downloads = dbrepo.enable_downloads
144 c.enable_downloads = dbrepo.enable_downloads
145 if c.enable_downloads:
145 if c.enable_downloads:
146 c.download_options = self._get_download_links(c.repo)
146 c.download_options = self._get_download_links(c.repo)
147
147
148 return render('summary/summary.html')
148 return render('summary/summary.html')
149
149
150
150
151
151
152 def _get_download_links(self, repo):
152 def _get_download_links(self, repo):
153
153
154 download_l = []
154 download_l = []
155
155
156 branches_group = ([], _("Branches"))
156 branches_group = ([], _("Branches"))
157 tags_group = ([], _("Tags"))
157 tags_group = ([], _("Tags"))
158
158
159 for name, chs in c.rhodecode_repo.branches.items():
159 for name, chs in c.rhodecode_repo.branches.items():
160 #chs = chs.split(':')[-1]
160 #chs = chs.split(':')[-1]
161 branches_group[0].append((chs, name),)
161 branches_group[0].append((chs, name),)
162 download_l.append(branches_group)
162 download_l.append(branches_group)
163
163
164 for name, chs in c.rhodecode_repo.tags.items():
164 for name, chs in c.rhodecode_repo.tags.items():
165 #chs = chs.split(':')[-1]
165 #chs = chs.split(':')[-1]
166 tags_group[0].append((chs, name),)
166 tags_group[0].append((chs, name),)
167 download_l.append(tags_group)
167 download_l.append(tags_group)
168
168
169 return download_l
169 return download_l
General Comments 0
You need to be logged in to leave comments. Login now