##// END OF EJS Templates
- fixed issue with missing commits on some repos commands...
marcink -
r1807:1635a214 beta
parent child Browse files
Show More
@@ -1,424 +1,430
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 modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import logging
26 import logging
27 import traceback
27 import traceback
28 import formencode
28 import formencode
29 from formencode import htmlfill
29 from formencode import htmlfill
30
30
31 from paste.httpexceptions import HTTPInternalServerError
31 from paste.httpexceptions import HTTPInternalServerError
32 from pylons import request, session, tmpl_context as c, url
32 from pylons import request, session, tmpl_context as c, url
33 from pylons.controllers.util import redirect
33 from pylons.controllers.util import redirect
34 from pylons.i18n.translation import _
34 from pylons.i18n.translation import _
35 from sqlalchemy.exc import IntegrityError
35 from sqlalchemy.exc import IntegrityError
36
36
37 from rhodecode.lib import helpers as h
37 from rhodecode.lib import helpers as h
38 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
38 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
39 HasPermissionAnyDecorator
39 HasPermissionAnyDecorator
40 from rhodecode.lib.base import BaseController, render
40 from rhodecode.lib.base import BaseController, render
41 from rhodecode.lib.utils import invalidate_cache, action_logger, repo_name_slug
41 from rhodecode.lib.utils import invalidate_cache, action_logger, repo_name_slug
42 from rhodecode.lib.helpers import get_token
42 from rhodecode.lib.helpers import get_token
43 from rhodecode.model.meta import Session
43 from rhodecode.model.meta import Session
44 from rhodecode.model.db import User, Repository, UserFollowing, RepoGroup
44 from rhodecode.model.db import User, Repository, UserFollowing, RepoGroup
45 from rhodecode.model.forms import RepoForm
45 from rhodecode.model.forms import RepoForm
46 from rhodecode.model.scm import ScmModel
46 from rhodecode.model.scm import ScmModel
47 from rhodecode.model.repo import RepoModel
47 from rhodecode.model.repo import RepoModel
48
48
49 log = logging.getLogger(__name__)
49 log = logging.getLogger(__name__)
50
50
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 def __load_defaults(self):
66 def __load_defaults(self):
67 c.repo_groups = RepoGroup.groups_choices()
67 c.repo_groups = RepoGroup.groups_choices()
68 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
68 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
69
69
70 repo_model = RepoModel()
70 repo_model = RepoModel()
71 c.users_array = repo_model.get_users_js()
71 c.users_array = repo_model.get_users_js()
72 c.users_groups_array = repo_model.get_users_groups_js()
72 c.users_groups_array = repo_model.get_users_groups_js()
73
73
74 def __load_data(self, repo_name=None):
74 def __load_data(self, repo_name=None):
75 """
75 """
76 Load defaults settings for edit, and update
76 Load defaults settings for edit, and update
77
77
78 :param repo_name:
78 :param repo_name:
79 """
79 """
80 self.__load_defaults()
80 self.__load_defaults()
81
81
82 c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
82 c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
83 repo = db_repo.scm_instance
83 repo = db_repo.scm_instance
84
84
85 if c.repo_info is None:
85 if c.repo_info is None:
86 h.flash(_('%s repository is not mapped to db perhaps'
86 h.flash(_('%s repository is not mapped to db perhaps'
87 ' it was created or renamed from the filesystem'
87 ' it was created or renamed from the filesystem'
88 ' please run the application again'
88 ' please run the application again'
89 ' in order to rescan repositories') % repo_name,
89 ' in order to rescan repositories') % repo_name,
90 category='error')
90 category='error')
91
91
92 return redirect(url('repos'))
92 return redirect(url('repos'))
93
93
94 c.default_user_id = User.get_by_username('default').user_id
94 c.default_user_id = User.get_by_username('default').user_id
95 c.in_public_journal = UserFollowing.query()\
95 c.in_public_journal = UserFollowing.query()\
96 .filter(UserFollowing.user_id == c.default_user_id)\
96 .filter(UserFollowing.user_id == c.default_user_id)\
97 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
97 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
98
98
99 if c.repo_info.stats:
99 if c.repo_info.stats:
100 last_rev = c.repo_info.stats.stat_on_revision
100 # this is on what revision we ended up so we add +1 for count
101 last_rev = c.repo_info.stats.stat_on_revision + 1
101 else:
102 else:
102 last_rev = 0
103 last_rev = 0
103 c.stats_revision = last_rev
104 c.stats_revision = last_rev
104
105
105 c.repo_last_rev = repo.count() - 1 if repo.revisions else 0
106 c.repo_last_rev = repo.count() if repo.revisions else 0
106
107
107 if last_rev == 0 or c.repo_last_rev == 0:
108 if last_rev == 0 or c.repo_last_rev == 0:
108 c.stats_percentage = 0
109 c.stats_percentage = 0
109 else:
110 else:
110 c.stats_percentage = '%.2f' % ((float((last_rev)) /
111 c.stats_percentage = '%.2f' % ((float((last_rev)) /
111 c.repo_last_rev) * 100)
112 c.repo_last_rev) * 100)
112
113
113 defaults = RepoModel()._get_defaults(repo_name)
114 defaults = RepoModel()._get_defaults(repo_name)
114
115
115 c.repos_list = [('', _('--REMOVE FORK--'))]
116 c.repos_list = [('', _('--REMOVE FORK--'))]
116 c.repos_list += [(x.repo_id, x.repo_name) for x in
117 c.repos_list += [(x.repo_id, x.repo_name) for x in
117 Repository.query().order_by(Repository.repo_name).all()]
118 Repository.query().order_by(Repository.repo_name).all()]
118 return defaults
119 return defaults
119
120
120 @HasPermissionAllDecorator('hg.admin')
121 @HasPermissionAllDecorator('hg.admin')
121 def index(self, format='html'):
122 def index(self, format='html'):
122 """GET /repos: All items in the collection"""
123 """GET /repos: All items in the collection"""
123 # url('repos')
124 # url('repos')
124
125
125 c.repos_list = ScmModel().get_repos(Repository.query()
126 c.repos_list = ScmModel().get_repos(Repository.query()
126 .order_by(Repository.repo_name)
127 .order_by(Repository.repo_name)
127 .all(), sort_key='name_sort')
128 .all(), sort_key='name_sort')
128 return render('admin/repos/repos.html')
129 return render('admin/repos/repos.html')
129
130
130 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
131 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
131 def create(self):
132 def create(self):
132 """
133 """
133 POST /repos: Create a new item"""
134 POST /repos: Create a new item"""
134 # url('repos')
135 # url('repos')
135
136
136 self.__load_defaults()
137 self.__load_defaults()
137 form_result = {}
138 form_result = {}
138 try:
139 try:
139 form_result = RepoForm(repo_groups=c.repo_groups_choices)()\
140 form_result = RepoForm(repo_groups=c.repo_groups_choices)()\
140 .to_python(dict(request.POST))
141 .to_python(dict(request.POST))
141 RepoModel().create(form_result, self.rhodecode_user)
142 RepoModel().create(form_result, self.rhodecode_user)
142 if form_result['clone_uri']:
143 if form_result['clone_uri']:
143 h.flash(_('created repository %s from %s') \
144 h.flash(_('created repository %s from %s') \
144 % (form_result['repo_name'], form_result['clone_uri']),
145 % (form_result['repo_name'], form_result['clone_uri']),
145 category='success')
146 category='success')
146 else:
147 else:
147 h.flash(_('created repository %s') % form_result['repo_name'],
148 h.flash(_('created repository %s') % form_result['repo_name'],
148 category='success')
149 category='success')
149
150
150 if request.POST.get('user_created'):
151 if request.POST.get('user_created'):
151 # created by regular non admin user
152 # created by regular non admin user
152 action_logger(self.rhodecode_user, 'user_created_repo',
153 action_logger(self.rhodecode_user, 'user_created_repo',
153 form_result['repo_name_full'], '', self.sa)
154 form_result['repo_name_full'], '', self.sa)
154 else:
155 else:
155 action_logger(self.rhodecode_user, 'admin_created_repo',
156 action_logger(self.rhodecode_user, 'admin_created_repo',
156 form_result['repo_name_full'], '', self.sa)
157 form_result['repo_name_full'], '', self.sa)
157 Session.commit()
158 Session.commit()
158 except formencode.Invalid, errors:
159 except formencode.Invalid, errors:
159
160
160 c.new_repo = errors.value['repo_name']
161 c.new_repo = errors.value['repo_name']
161
162
162 if request.POST.get('user_created'):
163 if request.POST.get('user_created'):
163 r = render('admin/repos/repo_add_create_repository.html')
164 r = render('admin/repos/repo_add_create_repository.html')
164 else:
165 else:
165 r = render('admin/repos/repo_add.html')
166 r = render('admin/repos/repo_add.html')
166
167
167 return htmlfill.render(
168 return htmlfill.render(
168 r,
169 r,
169 defaults=errors.value,
170 defaults=errors.value,
170 errors=errors.error_dict or {},
171 errors=errors.error_dict or {},
171 prefix_error=False,
172 prefix_error=False,
172 encoding="UTF-8")
173 encoding="UTF-8")
173
174
174 except Exception:
175 except Exception:
175 log.error(traceback.format_exc())
176 log.error(traceback.format_exc())
176 msg = _('error occurred during creation of repository %s') \
177 msg = _('error occurred during creation of repository %s') \
177 % form_result.get('repo_name')
178 % form_result.get('repo_name')
178 h.flash(msg, category='error')
179 h.flash(msg, category='error')
179 if request.POST.get('user_created'):
180 if request.POST.get('user_created'):
180 return redirect(url('home'))
181 return redirect(url('home'))
181 return redirect(url('repos'))
182 return redirect(url('repos'))
182
183
183 @HasPermissionAllDecorator('hg.admin')
184 @HasPermissionAllDecorator('hg.admin')
184 def new(self, format='html'):
185 def new(self, format='html'):
185 """GET /repos/new: Form to create a new item"""
186 """GET /repos/new: Form to create a new item"""
186 new_repo = request.GET.get('repo', '')
187 new_repo = request.GET.get('repo', '')
187 c.new_repo = repo_name_slug(new_repo)
188 c.new_repo = repo_name_slug(new_repo)
188 self.__load_defaults()
189 self.__load_defaults()
189 return render('admin/repos/repo_add.html')
190 return render('admin/repos/repo_add.html')
190
191
191 @HasPermissionAllDecorator('hg.admin')
192 @HasPermissionAllDecorator('hg.admin')
192 def update(self, repo_name):
193 def update(self, repo_name):
193 """
194 """
194 PUT /repos/repo_name: Update an existing item"""
195 PUT /repos/repo_name: Update an existing item"""
195 # Forms posted to this method should contain a hidden field:
196 # Forms posted to this method should contain a hidden field:
196 # <input type="hidden" name="_method" value="PUT" />
197 # <input type="hidden" name="_method" value="PUT" />
197 # Or using helpers:
198 # Or using helpers:
198 # h.form(url('repo', repo_name=ID),
199 # h.form(url('repo', repo_name=ID),
199 # method='put')
200 # method='put')
200 # url('repo', repo_name=ID)
201 # url('repo', repo_name=ID)
201 self.__load_defaults()
202 self.__load_defaults()
202 repo_model = RepoModel()
203 repo_model = RepoModel()
203 changed_name = repo_name
204 changed_name = repo_name
204 _form = RepoForm(edit=True, old_data={'repo_name': repo_name},
205 _form = RepoForm(edit=True, old_data={'repo_name': repo_name},
205 repo_groups=c.repo_groups_choices)()
206 repo_groups=c.repo_groups_choices)()
206 try:
207 try:
207 form_result = _form.to_python(dict(request.POST))
208 form_result = _form.to_python(dict(request.POST))
208 repo = repo_model.update(repo_name, form_result)
209 repo = repo_model.update(repo_name, form_result)
209 invalidate_cache('get_repo_cached_%s' % repo_name)
210 invalidate_cache('get_repo_cached_%s' % repo_name)
210 h.flash(_('Repository %s updated successfully' % repo_name),
211 h.flash(_('Repository %s updated successfully' % repo_name),
211 category='success')
212 category='success')
212 changed_name = repo.repo_name
213 changed_name = repo.repo_name
213 action_logger(self.rhodecode_user, 'admin_updated_repo',
214 action_logger(self.rhodecode_user, 'admin_updated_repo',
214 changed_name, '', self.sa)
215 changed_name, '', self.sa)
215 Session.commit()
216 Session.commit()
216 except formencode.Invalid, errors:
217 except formencode.Invalid, errors:
217 defaults = self.__load_data(repo_name)
218 defaults = self.__load_data(repo_name)
218 defaults.update(errors.value)
219 defaults.update(errors.value)
219 return htmlfill.render(
220 return htmlfill.render(
220 render('admin/repos/repo_edit.html'),
221 render('admin/repos/repo_edit.html'),
221 defaults=defaults,
222 defaults=defaults,
222 errors=errors.error_dict or {},
223 errors=errors.error_dict or {},
223 prefix_error=False,
224 prefix_error=False,
224 encoding="UTF-8")
225 encoding="UTF-8")
225
226
226 except Exception:
227 except Exception:
227 log.error(traceback.format_exc())
228 log.error(traceback.format_exc())
228 h.flash(_('error occurred during update of repository %s') \
229 h.flash(_('error occurred during update of repository %s') \
229 % repo_name, category='error')
230 % repo_name, category='error')
230 return redirect(url('edit_repo', repo_name=changed_name))
231 return redirect(url('edit_repo', repo_name=changed_name))
231
232
232 @HasPermissionAllDecorator('hg.admin')
233 @HasPermissionAllDecorator('hg.admin')
233 def delete(self, repo_name):
234 def delete(self, repo_name):
234 """
235 """
235 DELETE /repos/repo_name: Delete an existing item"""
236 DELETE /repos/repo_name: Delete an existing item"""
236 # Forms posted to this method should contain a hidden field:
237 # Forms posted to this method should contain a hidden field:
237 # <input type="hidden" name="_method" value="DELETE" />
238 # <input type="hidden" name="_method" value="DELETE" />
238 # Or using helpers:
239 # Or using helpers:
239 # h.form(url('repo', repo_name=ID),
240 # h.form(url('repo', repo_name=ID),
240 # method='delete')
241 # method='delete')
241 # url('repo', repo_name=ID)
242 # url('repo', repo_name=ID)
242
243
243 repo_model = RepoModel()
244 repo_model = RepoModel()
244 repo = repo_model.get_by_repo_name(repo_name)
245 repo = repo_model.get_by_repo_name(repo_name)
245 if not repo:
246 if not repo:
246 h.flash(_('%s repository is not mapped to db perhaps'
247 h.flash(_('%s repository is not mapped to db perhaps'
247 ' it was moved or renamed from the filesystem'
248 ' it was moved or renamed from the filesystem'
248 ' please run the application again'
249 ' please run the application again'
249 ' in order to rescan repositories') % repo_name,
250 ' in order to rescan repositories') % repo_name,
250 category='error')
251 category='error')
251
252
252 return redirect(url('repos'))
253 return redirect(url('repos'))
253 try:
254 try:
254 action_logger(self.rhodecode_user, 'admin_deleted_repo',
255 action_logger(self.rhodecode_user, 'admin_deleted_repo',
255 repo_name, '', self.sa)
256 repo_name, '', self.sa)
256 repo_model.delete(repo)
257 repo_model.delete(repo)
257 invalidate_cache('get_repo_cached_%s' % repo_name)
258 invalidate_cache('get_repo_cached_%s' % repo_name)
258 h.flash(_('deleted repository %s') % repo_name, category='success')
259 h.flash(_('deleted repository %s') % repo_name, category='success')
259 Session.commit()
260 Session.commit()
260 except IntegrityError, e:
261 except IntegrityError, e:
261 if e.message.find('repositories_fork_id_fkey') != -1:
262 if e.message.find('repositories_fork_id_fkey') != -1:
262 log.error(traceback.format_exc())
263 log.error(traceback.format_exc())
263 h.flash(_('Cannot delete %s it still contains attached '
264 h.flash(_('Cannot delete %s it still contains attached '
264 'forks') % repo_name,
265 'forks') % repo_name,
265 category='warning')
266 category='warning')
266 else:
267 else:
267 log.error(traceback.format_exc())
268 log.error(traceback.format_exc())
268 h.flash(_('An error occurred during '
269 h.flash(_('An error occurred during '
269 'deletion of %s') % repo_name,
270 'deletion of %s') % repo_name,
270 category='error')
271 category='error')
271
272
272 except Exception, e:
273 except Exception, e:
273 log.error(traceback.format_exc())
274 log.error(traceback.format_exc())
274 h.flash(_('An error occurred during deletion of %s') % repo_name,
275 h.flash(_('An error occurred during deletion of %s') % repo_name,
275 category='error')
276 category='error')
276
277
277 return redirect(url('repos'))
278 return redirect(url('repos'))
278
279
279 @HasPermissionAllDecorator('hg.admin')
280 @HasPermissionAllDecorator('hg.admin')
280 def delete_perm_user(self, repo_name):
281 def delete_perm_user(self, repo_name):
281 """
282 """
282 DELETE an existing repository permission user
283 DELETE an existing repository permission user
283
284
284 :param repo_name:
285 :param repo_name:
285 """
286 """
286
287
287 try:
288 try:
288 repo_model = RepoModel()
289 repo_model = RepoModel()
289 repo_model.delete_perm_user(request.POST, repo_name)
290 repo_model.delete_perm_user(request.POST, repo_name)
291 Session.commit()
290 except Exception, e:
292 except Exception, e:
291 h.flash(_('An error occurred during deletion of repository user'),
293 h.flash(_('An error occurred during deletion of repository user'),
292 category='error')
294 category='error')
293 raise HTTPInternalServerError()
295 raise HTTPInternalServerError()
294
296
295 @HasPermissionAllDecorator('hg.admin')
297 @HasPermissionAllDecorator('hg.admin')
296 def delete_perm_users_group(self, repo_name):
298 def delete_perm_users_group(self, repo_name):
297 """
299 """
298 DELETE an existing repository permission users group
300 DELETE an existing repository permission users group
299
301
300 :param repo_name:
302 :param repo_name:
301 """
303 """
302 try:
304 try:
303 repo_model = RepoModel()
305 repo_model = RepoModel()
304 repo_model.delete_perm_users_group(request.POST, repo_name)
306 repo_model.delete_perm_users_group(request.POST, repo_name)
307 Session.commit()
305 except Exception, e:
308 except Exception, e:
306 h.flash(_('An error occurred during deletion of repository'
309 h.flash(_('An error occurred during deletion of repository'
307 ' users groups'),
310 ' users groups'),
308 category='error')
311 category='error')
309 raise HTTPInternalServerError()
312 raise HTTPInternalServerError()
310
313
311 @HasPermissionAllDecorator('hg.admin')
314 @HasPermissionAllDecorator('hg.admin')
312 def repo_stats(self, repo_name):
315 def repo_stats(self, repo_name):
313 """
316 """
314 DELETE an existing repository statistics
317 DELETE an existing repository statistics
315
318
316 :param repo_name:
319 :param repo_name:
317 """
320 """
318
321
319 try:
322 try:
320 repo_model = RepoModel()
323 repo_model = RepoModel()
321 repo_model.delete_stats(repo_name)
324 repo_model.delete_stats(repo_name)
325 Session.commit()
322 except Exception, e:
326 except Exception, e:
323 h.flash(_('An error occurred during deletion of repository stats'),
327 h.flash(_('An error occurred during deletion of repository stats'),
324 category='error')
328 category='error')
325 return redirect(url('edit_repo', repo_name=repo_name))
329 return redirect(url('edit_repo', repo_name=repo_name))
326
330
327 @HasPermissionAllDecorator('hg.admin')
331 @HasPermissionAllDecorator('hg.admin')
328 def repo_cache(self, repo_name):
332 def repo_cache(self, repo_name):
329 """
333 """
330 INVALIDATE existing repository cache
334 INVALIDATE existing repository cache
331
335
332 :param repo_name:
336 :param repo_name:
333 """
337 """
334
338
335 try:
339 try:
336 ScmModel().mark_for_invalidation(repo_name)
340 ScmModel().mark_for_invalidation(repo_name)
341 Session.commit()
337 except Exception, e:
342 except Exception, e:
338 h.flash(_('An error occurred during cache invalidation'),
343 h.flash(_('An error occurred during cache invalidation'),
339 category='error')
344 category='error')
340 return redirect(url('edit_repo', repo_name=repo_name))
345 return redirect(url('edit_repo', repo_name=repo_name))
341
346
342 @HasPermissionAllDecorator('hg.admin')
347 @HasPermissionAllDecorator('hg.admin')
343 def repo_public_journal(self, repo_name):
348 def repo_public_journal(self, repo_name):
344 """
349 """
345 Set's this repository to be visible in public journal,
350 Set's this repository to be visible in public journal,
346 in other words assing default user to follow this repo
351 in other words assing default user to follow this repo
347
352
348 :param repo_name:
353 :param repo_name:
349 """
354 """
350
355
351 cur_token = request.POST.get('auth_token')
356 cur_token = request.POST.get('auth_token')
352 token = get_token()
357 token = get_token()
353 if cur_token == token:
358 if cur_token == token:
354 try:
359 try:
355 repo_id = Repository.get_by_repo_name(repo_name).repo_id
360 repo_id = Repository.get_by_repo_name(repo_name).repo_id
356 user_id = User.get_by_username('default').user_id
361 user_id = User.get_by_username('default').user_id
357 self.scm_model.toggle_following_repo(repo_id, user_id)
362 self.scm_model.toggle_following_repo(repo_id, user_id)
358 h.flash(_('Updated repository visibility in public journal'),
363 h.flash(_('Updated repository visibility in public journal'),
359 category='success')
364 category='success')
365 Session.commit()
360 except:
366 except:
361 h.flash(_('An error occurred during setting this'
367 h.flash(_('An error occurred during setting this'
362 ' repository in public journal'),
368 ' repository in public journal'),
363 category='error')
369 category='error')
364
370
365 else:
371 else:
366 h.flash(_('Token mismatch'), category='error')
372 h.flash(_('Token mismatch'), category='error')
367 return redirect(url('edit_repo', repo_name=repo_name))
373 return redirect(url('edit_repo', repo_name=repo_name))
368
374
369 @HasPermissionAllDecorator('hg.admin')
375 @HasPermissionAllDecorator('hg.admin')
370 def repo_pull(self, repo_name):
376 def repo_pull(self, repo_name):
371 """
377 """
372 Runs task to update given repository with remote changes,
378 Runs task to update given repository with remote changes,
373 ie. make pull on remote location
379 ie. make pull on remote location
374
380
375 :param repo_name:
381 :param repo_name:
376 """
382 """
377 try:
383 try:
378 ScmModel().pull_changes(repo_name, self.rhodecode_user.username)
384 ScmModel().pull_changes(repo_name, self.rhodecode_user.username)
379 h.flash(_('Pulled from remote location'), category='success')
385 h.flash(_('Pulled from remote location'), category='success')
380 except Exception, e:
386 except Exception, e:
381 h.flash(_('An error occurred during pull from remote location'),
387 h.flash(_('An error occurred during pull from remote location'),
382 category='error')
388 category='error')
383
389
384 return redirect(url('edit_repo', repo_name=repo_name))
390 return redirect(url('edit_repo', repo_name=repo_name))
385
391
386 @HasPermissionAllDecorator('hg.admin')
392 @HasPermissionAllDecorator('hg.admin')
387 def repo_as_fork(self, repo_name):
393 def repo_as_fork(self, repo_name):
388 """
394 """
389 Mark given repository as a fork of another
395 Mark given repository as a fork of another
390
396
391 :param repo_name:
397 :param repo_name:
392 """
398 """
393 try:
399 try:
394 fork_id = request.POST.get('id_fork_of')
400 fork_id = request.POST.get('id_fork_of')
395 repo = ScmModel().mark_as_fork(repo_name, fork_id,
401 repo = ScmModel().mark_as_fork(repo_name, fork_id,
396 self.rhodecode_user.username)
402 self.rhodecode_user.username)
397 fork = repo.fork.repo_name if repo.fork else _('Nothing')
403 fork = repo.fork.repo_name if repo.fork else _('Nothing')
398 Session.commit()
404 Session.commit()
399 h.flash(_('Marked repo %s as fork of %s' % (repo_name,fork)),
405 h.flash(_('Marked repo %s as fork of %s' % (repo_name,fork)),
400 category='success')
406 category='success')
401 except Exception, e:
407 except Exception, e:
402 raise
408 raise
403 h.flash(_('An error occurred during this operation'),
409 h.flash(_('An error occurred during this operation'),
404 category='error')
410 category='error')
405
411
406 return redirect(url('edit_repo', repo_name=repo_name))
412 return redirect(url('edit_repo', repo_name=repo_name))
407
413
408 @HasPermissionAllDecorator('hg.admin')
414 @HasPermissionAllDecorator('hg.admin')
409 def show(self, repo_name, format='html'):
415 def show(self, repo_name, format='html'):
410 """GET /repos/repo_name: Show a specific item"""
416 """GET /repos/repo_name: Show a specific item"""
411 # url('repo', repo_name=ID)
417 # url('repo', repo_name=ID)
412
418
413 @HasPermissionAllDecorator('hg.admin')
419 @HasPermissionAllDecorator('hg.admin')
414 def edit(self, repo_name, format='html'):
420 def edit(self, repo_name, format='html'):
415 """GET /repos/repo_name/edit: Form to edit an existing item"""
421 """GET /repos/repo_name/edit: Form to edit an existing item"""
416 # url('edit_repo', repo_name=ID)
422 # url('edit_repo', repo_name=ID)
417 defaults = self.__load_data(repo_name)
423 defaults = self.__load_data(repo_name)
418
424
419 return htmlfill.render(
425 return htmlfill.render(
420 render('admin/repos/repo_edit.html'),
426 render('admin/repos/repo_edit.html'),
421 defaults=defaults,
427 defaults=defaults,
422 encoding="UTF-8",
428 encoding="UTF-8",
423 force_defaults=False
429 force_defaults=False
424 )
430 )
@@ -1,174 +1,174
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.forks
3 rhodecode.controllers.forks
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 forks controller for rhodecode
6 forks controller for rhodecode
7
7
8 :created_on: Apr 23, 2011
8 :created_on: Apr 23, 2011
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 import logging
25 import logging
26 import formencode
26 import formencode
27 import traceback
27 import traceback
28 from formencode import htmlfill
28 from formencode import htmlfill
29
29
30 from pylons import tmpl_context as c, request, url
30 from pylons import tmpl_context as c, request, url
31 from pylons.controllers.util import redirect
31 from pylons.controllers.util import redirect
32 from pylons.i18n.translation import _
32 from pylons.i18n.translation import _
33
33
34 import rhodecode.lib.helpers as h
34 import rhodecode.lib.helpers as h
35
35
36 from rhodecode.lib.helpers import Page
36 from rhodecode.lib.helpers import Page
37 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator, \
37 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator, \
38 NotAnonymous
38 NotAnonymous
39 from rhodecode.lib.base import BaseRepoController, render
39 from rhodecode.lib.base import BaseRepoController, render
40 from rhodecode.model.db import Repository, RepoGroup, UserFollowing, User
40 from rhodecode.model.db import Repository, RepoGroup, UserFollowing, User
41 from rhodecode.model.repo import RepoModel
41 from rhodecode.model.repo import RepoModel
42 from rhodecode.model.forms import RepoForkForm
42 from rhodecode.model.forms import RepoForkForm
43
43
44 log = logging.getLogger(__name__)
44 log = logging.getLogger(__name__)
45
45
46
46
47 class ForksController(BaseRepoController):
47 class ForksController(BaseRepoController):
48
48
49 @LoginRequired()
49 @LoginRequired()
50 def __before__(self):
50 def __before__(self):
51 super(ForksController, self).__before__()
51 super(ForksController, self).__before__()
52
52
53 def __load_defaults(self):
53 def __load_defaults(self):
54 c.repo_groups = RepoGroup.groups_choices()
54 c.repo_groups = RepoGroup.groups_choices()
55 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
55 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
56
56
57 def __load_data(self, repo_name=None):
57 def __load_data(self, repo_name=None):
58 """
58 """
59 Load defaults settings for edit, and update
59 Load defaults settings for edit, and update
60
60
61 :param repo_name:
61 :param repo_name:
62 """
62 """
63 self.__load_defaults()
63 self.__load_defaults()
64
64
65 c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
65 c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
66 repo = db_repo.scm_instance
66 repo = db_repo.scm_instance
67
67
68 if c.repo_info is None:
68 if c.repo_info is None:
69 h.flash(_('%s repository is not mapped to db perhaps'
69 h.flash(_('%s repository is not mapped to db perhaps'
70 ' it was created or renamed from the filesystem'
70 ' it was created or renamed from the filesystem'
71 ' please run the application again'
71 ' please run the application again'
72 ' in order to rescan repositories') % repo_name,
72 ' in order to rescan repositories') % repo_name,
73 category='error')
73 category='error')
74
74
75 return redirect(url('repos'))
75 return redirect(url('repos'))
76
76
77 c.default_user_id = User.get_by_username('default').user_id
77 c.default_user_id = User.get_by_username('default').user_id
78 c.in_public_journal = UserFollowing.query()\
78 c.in_public_journal = UserFollowing.query()\
79 .filter(UserFollowing.user_id == c.default_user_id)\
79 .filter(UserFollowing.user_id == c.default_user_id)\
80 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
80 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
81
81
82 if c.repo_info.stats:
82 if c.repo_info.stats:
83 last_rev = c.repo_info.stats.stat_on_revision
83 last_rev = c.repo_info.stats.stat_on_revision+1
84 else:
84 else:
85 last_rev = 0
85 last_rev = 0
86 c.stats_revision = last_rev
86 c.stats_revision = last_rev
87
87
88 c.repo_last_rev = repo.count() - 1 if repo.revisions else 0
88 c.repo_last_rev = repo.count() if repo.revisions else 0
89
89
90 if last_rev == 0 or c.repo_last_rev == 0:
90 if last_rev == 0 or c.repo_last_rev == 0:
91 c.stats_percentage = 0
91 c.stats_percentage = 0
92 else:
92 else:
93 c.stats_percentage = '%.2f' % ((float((last_rev)) /
93 c.stats_percentage = '%.2f' % ((float((last_rev)) /
94 c.repo_last_rev) * 100)
94 c.repo_last_rev) * 100)
95
95
96 defaults = RepoModel()._get_defaults(repo_name)
96 defaults = RepoModel()._get_defaults(repo_name)
97 # add prefix to fork
97 # add prefix to fork
98 defaults['repo_name'] = 'fork-' + defaults['repo_name']
98 defaults['repo_name'] = 'fork-' + defaults['repo_name']
99 return defaults
99 return defaults
100
100
101 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
101 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
102 'repository.admin')
102 'repository.admin')
103 def forks(self, repo_name):
103 def forks(self, repo_name):
104 p = int(request.params.get('page', 1))
104 p = int(request.params.get('page', 1))
105 repo_id = c.rhodecode_db_repo.repo_id
105 repo_id = c.rhodecode_db_repo.repo_id
106 d = Repository.get_repo_forks(repo_id)
106 d = Repository.get_repo_forks(repo_id)
107 c.forks_pager = Page(d, page=p, items_per_page=20)
107 c.forks_pager = Page(d, page=p, items_per_page=20)
108
108
109 c.forks_data = render('/forks/forks_data.html')
109 c.forks_data = render('/forks/forks_data.html')
110
110
111 if request.environ.get('HTTP_X_PARTIAL_XHR'):
111 if request.environ.get('HTTP_X_PARTIAL_XHR'):
112 return c.forks_data
112 return c.forks_data
113
113
114 return render('/forks/forks.html')
114 return render('/forks/forks.html')
115
115
116 @NotAnonymous()
116 @NotAnonymous()
117 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
117 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
118 'repository.admin')
118 'repository.admin')
119 def fork(self, repo_name):
119 def fork(self, repo_name):
120 c.repo_info = Repository.get_by_repo_name(repo_name)
120 c.repo_info = Repository.get_by_repo_name(repo_name)
121 if not c.repo_info:
121 if not c.repo_info:
122 h.flash(_('%s repository is not mapped to db perhaps'
122 h.flash(_('%s repository is not mapped to db perhaps'
123 ' it was created or renamed from the file system'
123 ' it was created or renamed from the file system'
124 ' please run the application again'
124 ' please run the application again'
125 ' in order to rescan repositories') % repo_name,
125 ' in order to rescan repositories') % repo_name,
126 category='error')
126 category='error')
127
127
128 return redirect(url('home'))
128 return redirect(url('home'))
129
129
130 defaults = self.__load_data(repo_name)
130 defaults = self.__load_data(repo_name)
131
131
132 return htmlfill.render(
132 return htmlfill.render(
133 render('forks/fork.html'),
133 render('forks/fork.html'),
134 defaults=defaults,
134 defaults=defaults,
135 encoding="UTF-8",
135 encoding="UTF-8",
136 force_defaults=False
136 force_defaults=False
137 )
137 )
138
138
139
139
140 @NotAnonymous()
140 @NotAnonymous()
141 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
141 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
142 'repository.admin')
142 'repository.admin')
143 def fork_create(self, repo_name):
143 def fork_create(self, repo_name):
144 self.__load_defaults()
144 self.__load_defaults()
145 c.repo_info = Repository.get_by_repo_name(repo_name)
145 c.repo_info = Repository.get_by_repo_name(repo_name)
146 _form = RepoForkForm(old_data={'repo_type': c.repo_info.repo_type},
146 _form = RepoForkForm(old_data={'repo_type': c.repo_info.repo_type},
147 repo_groups=c.repo_groups_choices,)()
147 repo_groups=c.repo_groups_choices,)()
148 form_result = {}
148 form_result = {}
149 try:
149 try:
150 form_result = _form.to_python(dict(request.POST))
150 form_result = _form.to_python(dict(request.POST))
151 # add org_path of repo so we can do a clone from it later
151 # add org_path of repo so we can do a clone from it later
152 form_result['org_path'] = c.repo_info.repo_name
152 form_result['org_path'] = c.repo_info.repo_name
153
153
154 # create fork is done sometimes async on celery, db transaction
154 # create fork is done sometimes async on celery, db transaction
155 # management is handled there.
155 # management is handled there.
156 RepoModel().create_fork(form_result, self.rhodecode_user)
156 RepoModel().create_fork(form_result, self.rhodecode_user)
157 h.flash(_('forked %s repository as %s') \
157 h.flash(_('forked %s repository as %s') \
158 % (repo_name, form_result['repo_name']),
158 % (repo_name, form_result['repo_name']),
159 category='success')
159 category='success')
160 except formencode.Invalid, errors:
160 except formencode.Invalid, errors:
161 c.new_repo = errors.value['repo_name']
161 c.new_repo = errors.value['repo_name']
162
162
163 return htmlfill.render(
163 return htmlfill.render(
164 render('forks/fork.html'),
164 render('forks/fork.html'),
165 defaults=errors.value,
165 defaults=errors.value,
166 errors=errors.error_dict or {},
166 errors=errors.error_dict or {},
167 prefix_error=False,
167 prefix_error=False,
168 encoding="UTF-8")
168 encoding="UTF-8")
169 except Exception:
169 except Exception:
170 log.error(traceback.format_exc())
170 log.error(traceback.format_exc())
171 h.flash(_('An error occurred during repository forking %s') %
171 h.flash(_('An error occurred during repository forking %s') %
172 repo_name, category='error')
172 repo_name, category='error')
173
173
174 return redirect(url('home'))
174 return redirect(url('home'))
@@ -1,228 +1,229
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 modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import traceback
26 import traceback
27 import calendar
27 import calendar
28 import logging
28 import logging
29 from time import mktime
29 from time import mktime
30 from datetime import timedelta, date
30 from datetime import timedelta, date
31 from itertools import product
31 from itertools import product
32 from urlparse import urlparse
32 from urlparse import urlparse
33
33
34 from vcs.exceptions import ChangesetError, EmptyRepositoryError, \
34 from vcs.exceptions import ChangesetError, EmptyRepositoryError, \
35 NodeDoesNotExistError
35 NodeDoesNotExistError
36
36
37 from pylons import tmpl_context as c, request, url, config
37 from pylons import tmpl_context as c, request, url, config
38 from pylons.i18n.translation import _
38 from pylons.i18n.translation import _
39
39
40 from beaker.cache import cache_region, region_invalidate
40 from beaker.cache import cache_region, region_invalidate
41
41
42 from rhodecode.model.db import Statistics, CacheInvalidation
42 from rhodecode.model.db import Statistics, CacheInvalidation
43 from rhodecode.lib import ALL_READMES, ALL_EXTS
43 from rhodecode.lib import ALL_READMES, ALL_EXTS
44 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
44 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
45 from rhodecode.lib.base import BaseRepoController, render
45 from rhodecode.lib.base import BaseRepoController, render
46 from rhodecode.lib.utils import EmptyChangeset
46 from rhodecode.lib.utils import EmptyChangeset
47 from rhodecode.lib.markup_renderer import MarkupRenderer
47 from rhodecode.lib.markup_renderer import MarkupRenderer
48 from rhodecode.lib.celerylib import run_task
48 from rhodecode.lib.celerylib import run_task
49 from rhodecode.lib.celerylib.tasks import get_commits_stats, \
49 from rhodecode.lib.celerylib.tasks import get_commits_stats, \
50 LANGUAGES_EXTENSIONS_MAP
50 LANGUAGES_EXTENSIONS_MAP
51 from rhodecode.lib.helpers import RepoPage
51 from rhodecode.lib.helpers import RepoPage
52 from rhodecode.lib.compat import json, OrderedDict
52 from rhodecode.lib.compat import json, OrderedDict
53
53
54 log = logging.getLogger(__name__)
54 log = logging.getLogger(__name__)
55
55
56 README_FILES = [''.join([x[0][0], x[1][0]]) for x in
56 README_FILES = [''.join([x[0][0], x[1][0]]) for x in
57 sorted(list(product(ALL_READMES, ALL_EXTS)),
57 sorted(list(product(ALL_READMES, ALL_EXTS)),
58 key=lambda y:y[0][1] + y[1][1])]
58 key=lambda y:y[0][1] + y[1][1])]
59
59
60
60 class SummaryController(BaseRepoController):
61 class SummaryController(BaseRepoController):
61
62
62 @LoginRequired()
63 @LoginRequired()
63 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
64 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
64 'repository.admin')
65 'repository.admin')
65 def __before__(self):
66 def __before__(self):
66 super(SummaryController, self).__before__()
67 super(SummaryController, self).__before__()
67
68
68 def index(self, repo_name):
69 def index(self, repo_name):
69 c.dbrepo = dbrepo = c.rhodecode_db_repo
70 c.dbrepo = dbrepo = c.rhodecode_db_repo
70 c.following = self.scm_model.is_following_repo(repo_name,
71 c.following = self.scm_model.is_following_repo(repo_name,
71 self.rhodecode_user.user_id)
72 self.rhodecode_user.user_id)
72
73
73 def url_generator(**kw):
74 def url_generator(**kw):
74 return url('shortlog_home', repo_name=repo_name, size=10, **kw)
75 return url('shortlog_home', repo_name=repo_name, size=10, **kw)
75
76
76 c.repo_changesets = RepoPage(c.rhodecode_repo, page=1,
77 c.repo_changesets = RepoPage(c.rhodecode_repo, page=1,
77 items_per_page=10, url=url_generator)
78 items_per_page=10, url=url_generator)
78
79
79 if self.rhodecode_user.username == 'default':
80 if self.rhodecode_user.username == 'default':
80 # for default(anonymous) user we don't need to pass credentials
81 # for default(anonymous) user we don't need to pass credentials
81 username = ''
82 username = ''
82 password = ''
83 password = ''
83 else:
84 else:
84 username = str(self.rhodecode_user.username)
85 username = str(self.rhodecode_user.username)
85 password = '@'
86 password = '@'
86
87
87 parsed_url = urlparse(url.current(qualified=True))
88 parsed_url = urlparse(url.current(qualified=True))
88
89
89 default_clone_uri = '{scheme}://{user}{pass}{netloc}{path}'
90 default_clone_uri = '{scheme}://{user}{pass}{netloc}{path}'
90
91
91 uri_tmpl = config.get('clone_uri', default_clone_uri)
92 uri_tmpl = config.get('clone_uri', default_clone_uri)
92 uri_tmpl = uri_tmpl.replace('{', '%(').replace('}', ')s')
93 uri_tmpl = uri_tmpl.replace('{', '%(').replace('}', ')s')
93
94
94 uri = uri_tmpl % {'user': username,
95 uri = uri_tmpl % {'user': username,
95 'pass': password,
96 'pass': password,
96 'scheme': parsed_url.scheme,
97 'scheme': parsed_url.scheme,
97 'netloc':parsed_url.netloc,
98 'netloc': parsed_url.netloc,
98 'path':parsed_url.path}
99 'path':parsed_url.path}
99
100
100 c.clone_repo_url = uri
101 c.clone_repo_url = uri
101 c.repo_tags = OrderedDict()
102 c.repo_tags = OrderedDict()
102 for name, hash in c.rhodecode_repo.tags.items()[:10]:
103 for name, hash in c.rhodecode_repo.tags.items()[:10]:
103 try:
104 try:
104 c.repo_tags[name] = c.rhodecode_repo.get_changeset(hash)
105 c.repo_tags[name] = c.rhodecode_repo.get_changeset(hash)
105 except ChangesetError:
106 except ChangesetError:
106 c.repo_tags[name] = EmptyChangeset(hash)
107 c.repo_tags[name] = EmptyChangeset(hash)
107
108
108 c.repo_branches = OrderedDict()
109 c.repo_branches = OrderedDict()
109 for name, hash in c.rhodecode_repo.branches.items()[:10]:
110 for name, hash in c.rhodecode_repo.branches.items()[:10]:
110 try:
111 try:
111 c.repo_branches[name] = c.rhodecode_repo.get_changeset(hash)
112 c.repo_branches[name] = c.rhodecode_repo.get_changeset(hash)
112 except ChangesetError:
113 except ChangesetError:
113 c.repo_branches[name] = EmptyChangeset(hash)
114 c.repo_branches[name] = EmptyChangeset(hash)
114
115
115 td = date.today() + timedelta(days=1)
116 td = date.today() + timedelta(days=1)
116 td_1m = td - timedelta(days=calendar.mdays[td.month])
117 td_1m = td - timedelta(days=calendar.mdays[td.month])
117 td_1y = td - timedelta(days=365)
118 td_1y = td - timedelta(days=365)
118
119
119 ts_min_m = mktime(td_1m.timetuple())
120 ts_min_m = mktime(td_1m.timetuple())
120 ts_min_y = mktime(td_1y.timetuple())
121 ts_min_y = mktime(td_1y.timetuple())
121 ts_max_y = mktime(td.timetuple())
122 ts_max_y = mktime(td.timetuple())
122
123
123 if dbrepo.enable_statistics:
124 if dbrepo.enable_statistics:
124 c.show_stats = True
125 c.show_stats = True
125 c.no_data_msg = _('No data loaded yet')
126 c.no_data_msg = _('No data loaded yet')
126 run_task(get_commits_stats, c.dbrepo.repo_name, ts_min_y, ts_max_y)
127 run_task(get_commits_stats, c.dbrepo.repo_name, ts_min_y, ts_max_y)
127 else:
128 else:
128 c.show_stats = False
129 c.show_stats = False
129 c.no_data_msg = _('Statistics are disabled for this repository')
130 c.no_data_msg = _('Statistics are disabled for this repository')
130 c.ts_min = ts_min_m
131 c.ts_min = ts_min_m
131 c.ts_max = ts_max_y
132 c.ts_max = ts_max_y
132
133
133 stats = self.sa.query(Statistics)\
134 stats = self.sa.query(Statistics)\
134 .filter(Statistics.repository == dbrepo)\
135 .filter(Statistics.repository == dbrepo)\
135 .scalar()
136 .scalar()
136
137
137 c.stats_percentage = 0
138 c.stats_percentage = 0
138
139
139 if stats and stats.languages:
140 if stats and stats.languages:
140 c.no_data = False is dbrepo.enable_statistics
141 c.no_data = False is dbrepo.enable_statistics
141 lang_stats_d = json.loads(stats.languages)
142 lang_stats_d = json.loads(stats.languages)
142 c.commit_data = stats.commit_activity
143 c.commit_data = stats.commit_activity
143 c.overview_data = stats.commit_activity_combined
144 c.overview_data = stats.commit_activity_combined
144
145
145 lang_stats = ((x, {"count": y,
146 lang_stats = ((x, {"count": y,
146 "desc": LANGUAGES_EXTENSIONS_MAP.get(x)})
147 "desc": LANGUAGES_EXTENSIONS_MAP.get(x)})
147 for x, y in lang_stats_d.items())
148 for x, y in lang_stats_d.items())
148
149
149 c.trending_languages = json.dumps(OrderedDict(
150 c.trending_languages = json.dumps(OrderedDict(
150 sorted(lang_stats, reverse=True,
151 sorted(lang_stats, reverse=True,
151 key=lambda k: k[1])[:10]
152 key=lambda k: k[1])[:10]
152 )
153 )
153 )
154 )
154 last_rev = stats.stat_on_revision
155 last_rev = stats.stat_on_revision + 1
155 c.repo_last_rev = c.rhodecode_repo.count() - 1 \
156 c.repo_last_rev = c.rhodecode_repo.count()\
156 if c.rhodecode_repo.revisions else 0
157 if c.rhodecode_repo.revisions else 0
157 if last_rev == 0 or c.repo_last_rev == 0:
158 if last_rev == 0 or c.repo_last_rev == 0:
158 pass
159 pass
159 else:
160 else:
160 c.stats_percentage = '%.2f' % ((float((last_rev)) /
161 c.stats_percentage = '%.2f' % ((float((last_rev)) /
161 c.repo_last_rev) * 100)
162 c.repo_last_rev) * 100)
162 else:
163 else:
163 c.commit_data = json.dumps({})
164 c.commit_data = json.dumps({})
164 c.overview_data = json.dumps([[ts_min_y, 0], [ts_max_y, 10]])
165 c.overview_data = json.dumps([[ts_min_y, 0], [ts_max_y, 10]])
165 c.trending_languages = json.dumps({})
166 c.trending_languages = json.dumps({})
166 c.no_data = True
167 c.no_data = True
167
168
168 c.enable_downloads = dbrepo.enable_downloads
169 c.enable_downloads = dbrepo.enable_downloads
169 if c.enable_downloads:
170 if c.enable_downloads:
170 c.download_options = self._get_download_links(c.rhodecode_repo)
171 c.download_options = self._get_download_links(c.rhodecode_repo)
171
172
172 c.readme_data, c.readme_file = self.__get_readme_data(c.rhodecode_repo)
173 c.readme_data, c.readme_file = self.__get_readme_data(c.rhodecode_repo)
173 return render('summary/summary.html')
174 return render('summary/summary.html')
174
175
175 def __get_readme_data(self, repo):
176 def __get_readme_data(self, repo):
176
177
177 @cache_region('long_term')
178 @cache_region('long_term')
178 def _get_readme_from_cache(key):
179 def _get_readme_from_cache(key):
179 readme_data = None
180 readme_data = None
180 readme_file = None
181 readme_file = None
181 log.debug('Fetching readme file')
182 log.debug('Fetching readme file')
182 try:
183 try:
183 cs = repo.get_changeset('tip')
184 cs = repo.get_changeset('tip')
184 renderer = MarkupRenderer()
185 renderer = MarkupRenderer()
185 for f in README_FILES:
186 for f in README_FILES:
186 try:
187 try:
187 readme = cs.get_node(f)
188 readme = cs.get_node(f)
188 readme_file = f
189 readme_file = f
189 readme_data = renderer.render(readme.content, f)
190 readme_data = renderer.render(readme.content, f)
190 log.debug('Found readme %s' % readme_file)
191 log.debug('Found readme %s' % readme_file)
191 break
192 break
192 except NodeDoesNotExistError:
193 except NodeDoesNotExistError:
193 continue
194 continue
194 except ChangesetError:
195 except ChangesetError:
195 pass
196 pass
196 except EmptyRepositoryError:
197 except EmptyRepositoryError:
197 pass
198 pass
198 except Exception:
199 except Exception:
199 log.error(traceback.format_exc())
200 log.error(traceback.format_exc())
200
201
201 return readme_data, readme_file
202 return readme_data, readme_file
202
203
203 key = repo.name + '_README'
204 key = repo.name + '_README'
204 inv = CacheInvalidation.invalidate(key)
205 inv = CacheInvalidation.invalidate(key)
205 if inv is not None:
206 if inv is not None:
206 region_invalidate(_get_readme_from_cache, None, key)
207 region_invalidate(_get_readme_from_cache, None, key)
207 CacheInvalidation.set_valid(inv.cache_key)
208 CacheInvalidation.set_valid(inv.cache_key)
208 return _get_readme_from_cache(key)
209 return _get_readme_from_cache(key)
209
210
210 def _get_download_links(self, repo):
211 def _get_download_links(self, repo):
211
212
212 download_l = []
213 download_l = []
213
214
214 branches_group = ([], _("Branches"))
215 branches_group = ([], _("Branches"))
215 tags_group = ([], _("Tags"))
216 tags_group = ([], _("Tags"))
216
217
217 for name, chs in c.rhodecode_repo.branches.items():
218 for name, chs in c.rhodecode_repo.branches.items():
218 #chs = chs.split(':')[-1]
219 #chs = chs.split(':')[-1]
219 branches_group[0].append((chs, name),)
220 branches_group[0].append((chs, name),)
220 download_l.append(branches_group)
221 download_l.append(branches_group)
221
222
222 for name, chs in c.rhodecode_repo.tags.items():
223 for name, chs in c.rhodecode_repo.tags.items():
223 #chs = chs.split(':')[-1]
224 #chs = chs.split(':')[-1]
224 tags_group[0].append((chs, name),)
225 tags_group[0].append((chs, name),)
225 download_l.append(tags_group)
226 download_l.append(tags_group)
226
227
227 return download_l
228 return download_l
228
229
@@ -1,414 +1,418
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.celerylib.tasks
3 rhodecode.lib.celerylib.tasks
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 RhodeCode task modules, containing all task that suppose to be run
6 RhodeCode task modules, containing all task that suppose to be run
7 by celery daemon
7 by celery daemon
8
8
9 :created_on: Oct 6, 2010
9 :created_on: Oct 6, 2010
10 :author: marcink
10 :author: marcink
11 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
12 :license: GPLv3, see COPYING for more details.
12 :license: GPLv3, see COPYING for more details.
13 """
13 """
14 # This program is free software: you can redistribute it and/or modify
14 # This program is free software: you can redistribute it and/or modify
15 # it under the terms of the GNU General Public License as published by
15 # it under the terms of the GNU General Public License as published by
16 # the Free Software Foundation, either version 3 of the License, or
16 # the Free Software Foundation, either version 3 of the License, or
17 # (at your option) any later version.
17 # (at your option) any later version.
18 #
18 #
19 # This program is distributed in the hope that it will be useful,
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
22 # GNU General Public License for more details.
23 #
23 #
24 # You should have received a copy of the GNU General Public License
24 # You should have received a copy of the GNU General Public License
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 from celery.decorators import task
26 from celery.decorators import task
27
27
28 import os
28 import os
29 import traceback
29 import traceback
30 import logging
30 import logging
31 from os.path import join as jn
31 from os.path import join as jn
32
32
33 from time import mktime
33 from time import mktime
34 from operator import itemgetter
34 from operator import itemgetter
35 from string import lower
35 from string import lower
36
36
37 from pylons import config, url
37 from pylons import config, url
38 from pylons.i18n.translation import _
38 from pylons.i18n.translation import _
39
39
40 from vcs import get_backend
40 from vcs import get_backend
41
41
42 from rhodecode import CELERY_ON
42 from rhodecode import CELERY_ON
43 from rhodecode.lib import LANGUAGES_EXTENSIONS_MAP, safe_str
43 from rhodecode.lib import LANGUAGES_EXTENSIONS_MAP, safe_str
44 from rhodecode.lib.celerylib import run_task, locked_task, str2bool, \
44 from rhodecode.lib.celerylib import run_task, locked_task, str2bool, \
45 __get_lockkey, LockHeld, DaemonLock
45 __get_lockkey, LockHeld, DaemonLock
46 from rhodecode.lib.helpers import person
46 from rhodecode.lib.helpers import person
47 from rhodecode.lib.rcmail.smtp_mailer import SmtpMailer
47 from rhodecode.lib.rcmail.smtp_mailer import SmtpMailer
48 from rhodecode.lib.utils import add_cache, action_logger
48 from rhodecode.lib.utils import add_cache, action_logger
49 from rhodecode.lib.compat import json, OrderedDict
49 from rhodecode.lib.compat import json, OrderedDict
50
50
51 from rhodecode.model import init_model
51 from rhodecode.model import init_model
52 from rhodecode.model import meta
52 from rhodecode.model import meta
53 from rhodecode.model.db import Statistics, Repository, User
53 from rhodecode.model.db import Statistics, Repository, User
54
54
55 from sqlalchemy import engine_from_config
55 from sqlalchemy import engine_from_config
56
56
57 add_cache(config)
57 add_cache(config)
58
58
59 __all__ = ['whoosh_index', 'get_commits_stats',
59 __all__ = ['whoosh_index', 'get_commits_stats',
60 'reset_user_password', 'send_email']
60 'reset_user_password', 'send_email']
61
61
62
62
63 def get_session():
63 def get_session():
64 if CELERY_ON:
64 if CELERY_ON:
65 engine = engine_from_config(config, 'sqlalchemy.db1.')
65 engine = engine_from_config(config, 'sqlalchemy.db1.')
66 init_model(engine)
66 init_model(engine)
67 sa = meta.Session
67 sa = meta.Session
68 return sa
68 return sa
69
69
70 def get_logger(cls):
70 def get_logger(cls):
71 if CELERY_ON:
71 if CELERY_ON:
72 try:
72 try:
73 log = cls.get_logger()
73 log = cls.get_logger()
74 except:
74 except:
75 log = logging.getLogger(__name__)
75 log = logging.getLogger(__name__)
76 else:
76 else:
77 log = logging.getLogger(__name__)
77 log = logging.getLogger(__name__)
78
78
79 return log
79 return log
80
80
81
81 @task(ignore_result=True)
82 @task(ignore_result=True)
82 @locked_task
83 @locked_task
83 def whoosh_index(repo_location, full_index):
84 def whoosh_index(repo_location, full_index):
84 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
85 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
85
86
86 #log = whoosh_index.get_logger()
87 # log = whoosh_index.get_logger(whoosh_index)
87
88
88 index_location = config['index_dir']
89 index_location = config['index_dir']
89 WhooshIndexingDaemon(index_location=index_location,
90 WhooshIndexingDaemon(index_location=index_location,
90 repo_location=repo_location, sa=get_session())\
91 repo_location=repo_location, sa=get_session())\
91 .run(full_index=full_index)
92 .run(full_index=full_index)
92
93
93
94
94 @task(ignore_result=True)
95 @task(ignore_result=True)
95 def get_commits_stats(repo_name, ts_min_y, ts_max_y):
96 def get_commits_stats(repo_name, ts_min_y, ts_max_y):
96 log = get_logger(get_commits_stats)
97 log = get_logger(get_commits_stats)
97
98
98 lockkey = __get_lockkey('get_commits_stats', repo_name, ts_min_y,
99 lockkey = __get_lockkey('get_commits_stats', repo_name, ts_min_y,
99 ts_max_y)
100 ts_max_y)
100 lockkey_path = config['here']
101 lockkey_path = config['here']
101
102
102 log.info('running task with lockkey %s', lockkey)
103 log.info('running task with lockkey %s', lockkey)
104
103 try:
105 try:
104 sa = get_session()
106 sa = get_session()
105 lock = l = DaemonLock(file_=jn(lockkey_path, lockkey))
107 lock = l = DaemonLock(file_=jn(lockkey_path, lockkey))
106
108
107 # for js data compatibilty cleans the key for person from '
109 # for js data compatibilty cleans the key for person from '
108 akc = lambda k: person(k).replace('"', "")
110 akc = lambda k: person(k).replace('"', "")
109
111
110 co_day_auth_aggr = {}
112 co_day_auth_aggr = {}
111 commits_by_day_aggregate = {}
113 commits_by_day_aggregate = {}
112 repo = Repository.get_by_repo_name(repo_name)
114 repo = Repository.get_by_repo_name(repo_name)
113 if repo is None:
115 if repo is None:
114 return True
116 return True
115
117
116 repo = repo.scm_instance
118 repo = repo.scm_instance
117 repo_size = len(repo.revisions)
119 repo_size = repo.count()
118 #return if repo have no revisions
120 # return if repo have no revisions
119 if repo_size < 1:
121 if repo_size < 1:
120 lock.release()
122 lock.release()
121 return True
123 return True
122
124
123 skip_date_limit = True
125 skip_date_limit = True
124 parse_limit = int(config['app_conf'].get('commit_parse_limit'))
126 parse_limit = int(config['app_conf'].get('commit_parse_limit'))
125 last_rev = 0
127 last_rev = None
126 last_cs = None
128 last_cs = None
127 timegetter = itemgetter('time')
129 timegetter = itemgetter('time')
128
130
129 dbrepo = sa.query(Repository)\
131 dbrepo = sa.query(Repository)\
130 .filter(Repository.repo_name == repo_name).scalar()
132 .filter(Repository.repo_name == repo_name).scalar()
131 cur_stats = sa.query(Statistics)\
133 cur_stats = sa.query(Statistics)\
132 .filter(Statistics.repository == dbrepo).scalar()
134 .filter(Statistics.repository == dbrepo).scalar()
133
135
134 if cur_stats is not None:
136 if cur_stats is not None:
135 last_rev = cur_stats.stat_on_revision
137 last_rev = cur_stats.stat_on_revision
136
138
137 if last_rev == repo.get_changeset().revision and repo_size > 1:
139 if last_rev == repo.get_changeset().revision and repo_size > 1:
138 # pass silently without any work if we're not on first revision or
140 # pass silently without any work if we're not on first revision or
139 # current state of parsing revision(from db marker) is the
141 # current state of parsing revision(from db marker) is the
140 # last revision
142 # last revision
141 lock.release()
143 lock.release()
142 return True
144 return True
143
145
144 if cur_stats:
146 if cur_stats:
145 commits_by_day_aggregate = OrderedDict(json.loads(
147 commits_by_day_aggregate = OrderedDict(json.loads(
146 cur_stats.commit_activity_combined))
148 cur_stats.commit_activity_combined))
147 co_day_auth_aggr = json.loads(cur_stats.commit_activity)
149 co_day_auth_aggr = json.loads(cur_stats.commit_activity)
148
150
149 log.debug('starting parsing %s', parse_limit)
151 log.debug('starting parsing %s', parse_limit)
150 lmktime = mktime
152 lmktime = mktime
151
153
152 last_rev = last_rev + 1 if last_rev > 0 else last_rev
154 last_rev = last_rev + 1 if last_rev >= 0 else 0
153
155 log.debug('Getting revisions from %s to %s' % (
156 last_rev, last_rev + parse_limit)
157 )
154 for cs in repo[last_rev:last_rev + parse_limit]:
158 for cs in repo[last_rev:last_rev + parse_limit]:
155 last_cs = cs # remember last parsed changeset
159 last_cs = cs # remember last parsed changeset
156 k = lmktime([cs.date.timetuple()[0], cs.date.timetuple()[1],
160 k = lmktime([cs.date.timetuple()[0], cs.date.timetuple()[1],
157 cs.date.timetuple()[2], 0, 0, 0, 0, 0, 0])
161 cs.date.timetuple()[2], 0, 0, 0, 0, 0, 0])
158
162
159 if akc(cs.author) in co_day_auth_aggr:
163 if akc(cs.author) in co_day_auth_aggr:
160 try:
164 try:
161 l = [timegetter(x) for x in
165 l = [timegetter(x) for x in
162 co_day_auth_aggr[akc(cs.author)]['data']]
166 co_day_auth_aggr[akc(cs.author)]['data']]
163 time_pos = l.index(k)
167 time_pos = l.index(k)
164 except ValueError:
168 except ValueError:
165 time_pos = False
169 time_pos = False
166
170
167 if time_pos >= 0 and time_pos is not False:
171 if time_pos >= 0 and time_pos is not False:
168
172
169 datadict = \
173 datadict = \
170 co_day_auth_aggr[akc(cs.author)]['data'][time_pos]
174 co_day_auth_aggr[akc(cs.author)]['data'][time_pos]
171
175
172 datadict["commits"] += 1
176 datadict["commits"] += 1
173 datadict["added"] += len(cs.added)
177 datadict["added"] += len(cs.added)
174 datadict["changed"] += len(cs.changed)
178 datadict["changed"] += len(cs.changed)
175 datadict["removed"] += len(cs.removed)
179 datadict["removed"] += len(cs.removed)
176
180
177 else:
181 else:
178 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
182 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
179
183
180 datadict = {"time": k,
184 datadict = {"time": k,
181 "commits": 1,
185 "commits": 1,
182 "added": len(cs.added),
186 "added": len(cs.added),
183 "changed": len(cs.changed),
187 "changed": len(cs.changed),
184 "removed": len(cs.removed),
188 "removed": len(cs.removed),
185 }
189 }
186 co_day_auth_aggr[akc(cs.author)]['data']\
190 co_day_auth_aggr[akc(cs.author)]['data']\
187 .append(datadict)
191 .append(datadict)
188
192
189 else:
193 else:
190 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
194 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
191 co_day_auth_aggr[akc(cs.author)] = {
195 co_day_auth_aggr[akc(cs.author)] = {
192 "label": akc(cs.author),
196 "label": akc(cs.author),
193 "data": [{"time":k,
197 "data": [{"time":k,
194 "commits":1,
198 "commits":1,
195 "added":len(cs.added),
199 "added":len(cs.added),
196 "changed":len(cs.changed),
200 "changed":len(cs.changed),
197 "removed":len(cs.removed),
201 "removed":len(cs.removed),
198 }],
202 }],
199 "schema": ["commits"],
203 "schema": ["commits"],
200 }
204 }
201
205
202 #gather all data by day
206 #gather all data by day
203 if k in commits_by_day_aggregate:
207 if k in commits_by_day_aggregate:
204 commits_by_day_aggregate[k] += 1
208 commits_by_day_aggregate[k] += 1
205 else:
209 else:
206 commits_by_day_aggregate[k] = 1
210 commits_by_day_aggregate[k] = 1
207
211
208 overview_data = sorted(commits_by_day_aggregate.items(),
212 overview_data = sorted(commits_by_day_aggregate.items(),
209 key=itemgetter(0))
213 key=itemgetter(0))
210
214
211 if not co_day_auth_aggr:
215 if not co_day_auth_aggr:
212 co_day_auth_aggr[akc(repo.contact)] = {
216 co_day_auth_aggr[akc(repo.contact)] = {
213 "label": akc(repo.contact),
217 "label": akc(repo.contact),
214 "data": [0, 1],
218 "data": [0, 1],
215 "schema": ["commits"],
219 "schema": ["commits"],
216 }
220 }
217
221
218 stats = cur_stats if cur_stats else Statistics()
222 stats = cur_stats if cur_stats else Statistics()
219 stats.commit_activity = json.dumps(co_day_auth_aggr)
223 stats.commit_activity = json.dumps(co_day_auth_aggr)
220 stats.commit_activity_combined = json.dumps(overview_data)
224 stats.commit_activity_combined = json.dumps(overview_data)
221
225
222 log.debug('last revison %s', last_rev)
226 log.debug('last revison %s', last_rev)
223 leftovers = len(repo.revisions[last_rev:])
227 leftovers = len(repo.revisions[last_rev:])
224 log.debug('revisions to parse %s', leftovers)
228 log.debug('revisions to parse %s', leftovers)
225
229
226 if last_rev == 0 or leftovers < parse_limit:
230 if last_rev == 0 or leftovers < parse_limit:
227 log.debug('getting code trending stats')
231 log.debug('getting code trending stats')
228 stats.languages = json.dumps(__get_codes_stats(repo_name))
232 stats.languages = json.dumps(__get_codes_stats(repo_name))
229
233
230 try:
234 try:
231 stats.repository = dbrepo
235 stats.repository = dbrepo
232 stats.stat_on_revision = last_cs.revision if last_cs else 0
236 stats.stat_on_revision = last_cs.revision if last_cs else 0
233 sa.add(stats)
237 sa.add(stats)
234 sa.commit()
238 sa.commit()
235 except:
239 except:
236 log.error(traceback.format_exc())
240 log.error(traceback.format_exc())
237 sa.rollback()
241 sa.rollback()
238 lock.release()
242 lock.release()
239 return False
243 return False
240
244
241 #final release
245 #final release
242 lock.release()
246 lock.release()
243
247
244 #execute another task if celery is enabled
248 #execute another task if celery is enabled
245 if len(repo.revisions) > 1 and CELERY_ON:
249 if len(repo.revisions) > 1 and CELERY_ON:
246 run_task(get_commits_stats, repo_name, ts_min_y, ts_max_y)
250 run_task(get_commits_stats, repo_name, ts_min_y, ts_max_y)
247 return True
251 return True
248 except LockHeld:
252 except LockHeld:
249 log.info('LockHeld')
253 log.info('LockHeld')
250 return 'Task with key %s already running' % lockkey
254 return 'Task with key %s already running' % lockkey
251
255
252 @task(ignore_result=True)
256 @task(ignore_result=True)
253 def send_password_link(user_email):
257 def send_password_link(user_email):
254 from rhodecode.model.notification import EmailNotificationModel
258 from rhodecode.model.notification import EmailNotificationModel
255
259
256 log = get_logger(send_password_link)
260 log = get_logger(send_password_link)
257
261
258 try:
262 try:
259 sa = get_session()
263 sa = get_session()
260 user = User.get_by_email(user_email)
264 user = User.get_by_email(user_email)
261 if user:
265 if user:
262 log.debug('password reset user found %s' % user)
266 log.debug('password reset user found %s' % user)
263 link = url('reset_password_confirmation', key=user.api_key,
267 link = url('reset_password_confirmation', key=user.api_key,
264 qualified=True)
268 qualified=True)
265 reg_type = EmailNotificationModel.TYPE_PASSWORD_RESET
269 reg_type = EmailNotificationModel.TYPE_PASSWORD_RESET
266 body = EmailNotificationModel().get_email_tmpl(reg_type,
270 body = EmailNotificationModel().get_email_tmpl(reg_type,
267 **{'user':user.short_contact,
271 **{'user':user.short_contact,
268 'reset_url':link})
272 'reset_url':link})
269 log.debug('sending email')
273 log.debug('sending email')
270 run_task(send_email, user_email,
274 run_task(send_email, user_email,
271 _("password reset link"), body)
275 _("password reset link"), body)
272 log.info('send new password mail to %s', user_email)
276 log.info('send new password mail to %s', user_email)
273 else:
277 else:
274 log.debug("password reset email %s not found" % user_email)
278 log.debug("password reset email %s not found" % user_email)
275 except:
279 except:
276 log.error(traceback.format_exc())
280 log.error(traceback.format_exc())
277 return False
281 return False
278
282
279 return True
283 return True
280
284
281 @task(ignore_result=True)
285 @task(ignore_result=True)
282 def reset_user_password(user_email):
286 def reset_user_password(user_email):
283 from rhodecode.lib import auth
287 from rhodecode.lib import auth
284
288
285 log = get_logger(reset_user_password)
289 log = get_logger(reset_user_password)
286
290
287 try:
291 try:
288 try:
292 try:
289 sa = get_session()
293 sa = get_session()
290 user = User.get_by_email(user_email)
294 user = User.get_by_email(user_email)
291 new_passwd = auth.PasswordGenerator().gen_password(8,
295 new_passwd = auth.PasswordGenerator().gen_password(8,
292 auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
296 auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
293 if user:
297 if user:
294 user.password = auth.get_crypt_password(new_passwd)
298 user.password = auth.get_crypt_password(new_passwd)
295 user.api_key = auth.generate_api_key(user.username)
299 user.api_key = auth.generate_api_key(user.username)
296 sa.add(user)
300 sa.add(user)
297 sa.commit()
301 sa.commit()
298 log.info('change password for %s', user_email)
302 log.info('change password for %s', user_email)
299 if new_passwd is None:
303 if new_passwd is None:
300 raise Exception('unable to generate new password')
304 raise Exception('unable to generate new password')
301 except:
305 except:
302 log.error(traceback.format_exc())
306 log.error(traceback.format_exc())
303 sa.rollback()
307 sa.rollback()
304
308
305 run_task(send_email, user_email,
309 run_task(send_email, user_email,
306 'Your new password',
310 'Your new password',
307 'Your new RhodeCode password:%s' % (new_passwd))
311 'Your new RhodeCode password:%s' % (new_passwd))
308 log.info('send new password mail to %s', user_email)
312 log.info('send new password mail to %s', user_email)
309
313
310 except:
314 except:
311 log.error('Failed to update user password')
315 log.error('Failed to update user password')
312 log.error(traceback.format_exc())
316 log.error(traceback.format_exc())
313
317
314 return True
318 return True
315
319
316
320
317 @task(ignore_result=True)
321 @task(ignore_result=True)
318 def send_email(recipients, subject, body, html_body=''):
322 def send_email(recipients, subject, body, html_body=''):
319 """
323 """
320 Sends an email with defined parameters from the .ini files.
324 Sends an email with defined parameters from the .ini files.
321
325
322 :param recipients: list of recipients, it this is empty the defined email
326 :param recipients: list of recipients, it this is empty the defined email
323 address from field 'email_to' is used instead
327 address from field 'email_to' is used instead
324 :param subject: subject of the mail
328 :param subject: subject of the mail
325 :param body: body of the mail
329 :param body: body of the mail
326 :param html_body: html version of body
330 :param html_body: html version of body
327 """
331 """
328 log = get_logger(send_email)
332 log = get_logger(send_email)
329 sa = get_session()
333 sa = get_session()
330 email_config = config
334 email_config = config
331 subject = "%s %s" % (email_config.get('email_prefix'), subject)
335 subject = "%s %s" % (email_config.get('email_prefix'), subject)
332 if not recipients:
336 if not recipients:
333 # if recipients are not defined we send to email_config + all admins
337 # if recipients are not defined we send to email_config + all admins
334 admins = [u.email for u in User.query()
338 admins = [u.email for u in User.query()
335 .filter(User.admin == True).all()]
339 .filter(User.admin == True).all()]
336 recipients = [email_config.get('email_to')] + admins
340 recipients = [email_config.get('email_to')] + admins
337
341
338 mail_from = email_config.get('app_email_from', 'RhodeCode')
342 mail_from = email_config.get('app_email_from', 'RhodeCode')
339 user = email_config.get('smtp_username')
343 user = email_config.get('smtp_username')
340 passwd = email_config.get('smtp_password')
344 passwd = email_config.get('smtp_password')
341 mail_server = email_config.get('smtp_server')
345 mail_server = email_config.get('smtp_server')
342 mail_port = email_config.get('smtp_port')
346 mail_port = email_config.get('smtp_port')
343 tls = str2bool(email_config.get('smtp_use_tls'))
347 tls = str2bool(email_config.get('smtp_use_tls'))
344 ssl = str2bool(email_config.get('smtp_use_ssl'))
348 ssl = str2bool(email_config.get('smtp_use_ssl'))
345 debug = str2bool(config.get('debug'))
349 debug = str2bool(config.get('debug'))
346 smtp_auth = email_config.get('smtp_auth')
350 smtp_auth = email_config.get('smtp_auth')
347
351
348 try:
352 try:
349 m = SmtpMailer(mail_from, user, passwd, mail_server, smtp_auth,
353 m = SmtpMailer(mail_from, user, passwd, mail_server, smtp_auth,
350 mail_port, ssl, tls, debug=debug)
354 mail_port, ssl, tls, debug=debug)
351 m.send(recipients, subject, body, html_body)
355 m.send(recipients, subject, body, html_body)
352 except:
356 except:
353 log.error('Mail sending failed')
357 log.error('Mail sending failed')
354 log.error(traceback.format_exc())
358 log.error(traceback.format_exc())
355 return False
359 return False
356 return True
360 return True
357
361
358
362
359 @task(ignore_result=True)
363 @task(ignore_result=True)
360 def create_repo_fork(form_data, cur_user):
364 def create_repo_fork(form_data, cur_user):
361 """
365 """
362 Creates a fork of repository using interval VCS methods
366 Creates a fork of repository using interval VCS methods
363
367
364 :param form_data:
368 :param form_data:
365 :param cur_user:
369 :param cur_user:
366 """
370 """
367 from rhodecode.model.repo import RepoModel
371 from rhodecode.model.repo import RepoModel
368
372
369 log = get_logger(create_repo_fork)
373 log = get_logger(create_repo_fork)
370
374
371 Session = get_session()
375 Session = get_session()
372 base_path = Repository.base_path()
376 base_path = Repository.base_path()
373
377
374 RepoModel(Session).create(form_data, cur_user, just_db=True, fork=True)
378 RepoModel(Session).create(form_data, cur_user, just_db=True, fork=True)
375
379
376 alias = form_data['repo_type']
380 alias = form_data['repo_type']
377 org_repo_name = form_data['org_path']
381 org_repo_name = form_data['org_path']
378 fork_name = form_data['repo_name_full']
382 fork_name = form_data['repo_name_full']
379 update_after_clone = form_data['update_after_clone']
383 update_after_clone = form_data['update_after_clone']
380 source_repo_path = os.path.join(base_path, org_repo_name)
384 source_repo_path = os.path.join(base_path, org_repo_name)
381 destination_fork_path = os.path.join(base_path, fork_name)
385 destination_fork_path = os.path.join(base_path, fork_name)
382
386
383 log.info('creating fork of %s as %s', source_repo_path,
387 log.info('creating fork of %s as %s', source_repo_path,
384 destination_fork_path)
388 destination_fork_path)
385 backend = get_backend(alias)
389 backend = get_backend(alias)
386 backend(safe_str(destination_fork_path), create=True,
390 backend(safe_str(destination_fork_path), create=True,
387 src_url=safe_str(source_repo_path),
391 src_url=safe_str(source_repo_path),
388 update_after_clone=update_after_clone)
392 update_after_clone=update_after_clone)
389 action_logger(cur_user, 'user_forked_repo:%s' % fork_name,
393 action_logger(cur_user, 'user_forked_repo:%s' % fork_name,
390 org_repo_name, '', Session)
394 org_repo_name, '', Session)
391
395
392 action_logger(cur_user, 'user_created_fork:%s' % fork_name,
396 action_logger(cur_user, 'user_created_fork:%s' % fork_name,
393 fork_name, '', Session)
397 fork_name, '', Session)
394 # finally commit at latest possible stage
398 # finally commit at latest possible stage
395 Session.commit()
399 Session.commit()
396
400
397 def __get_codes_stats(repo_name):
401 def __get_codes_stats(repo_name):
398 repo = Repository.get_by_repo_name(repo_name).scm_instance
402 repo = Repository.get_by_repo_name(repo_name).scm_instance
399
403
400 tip = repo.get_changeset()
404 tip = repo.get_changeset()
401 code_stats = {}
405 code_stats = {}
402
406
403 def aggregate(cs):
407 def aggregate(cs):
404 for f in cs[2]:
408 for f in cs[2]:
405 ext = lower(f.extension)
409 ext = lower(f.extension)
406 if ext in LANGUAGES_EXTENSIONS_MAP.keys() and not f.is_binary:
410 if ext in LANGUAGES_EXTENSIONS_MAP.keys() and not f.is_binary:
407 if ext in code_stats:
411 if ext in code_stats:
408 code_stats[ext] += 1
412 code_stats[ext] += 1
409 else:
413 else:
410 code_stats[ext] = 1
414 code_stats[ext] = 1
411
415
412 map(aggregate, tip.walk('/'))
416 map(aggregate, tip.walk('/'))
413
417
414 return code_stats or {}
418 return code_stats or {}
General Comments 0
You need to be logged in to leave comments. Login now