##// END OF EJS Templates
merge
marcink -
r823:81e8dfa3 merge beta
parent child Browse files
Show More
@@ -1,290 +1,305
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # repos controller for pylons
3 # repos controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on April 7, 2010
21 Created on April 7, 2010
22 admin controller for pylons
22 admin controller for pylons
23 @author: marcink
23 @author: marcink
24 """
24 """
25 from formencode import htmlfill
25 from formencode import htmlfill
26 from operator import itemgetter
26 from operator import itemgetter
27 from paste.httpexceptions import HTTPInternalServerError
27 from paste.httpexceptions import HTTPInternalServerError
28 from pylons import request, response, session, tmpl_context as c, url
28 from pylons import request, response, session, tmpl_context as c, url
29 from pylons.controllers.util import abort, redirect
29 from pylons.controllers.util import abort, redirect
30 from pylons.i18n.translation import _
30 from pylons.i18n.translation import _
31 from rhodecode.lib import helpers as h
31 from rhodecode.lib import helpers as h
32 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
32 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
33 HasPermissionAnyDecorator
33 HasPermissionAnyDecorator
34 from rhodecode.lib.base import BaseController, render
34 from rhodecode.lib.base import BaseController, render
35 from rhodecode.lib.utils import invalidate_cache, action_logger
35 from rhodecode.lib.utils import invalidate_cache, action_logger
36 from rhodecode.model.db import User
36 from rhodecode.model.db import User
37 from rhodecode.model.forms import RepoForm
37 from rhodecode.model.forms import RepoForm
38 from rhodecode.model.scm import ScmModel
38 from rhodecode.model.scm import ScmModel
39 from rhodecode.model.repo import RepoModel
39 from rhodecode.model.repo import RepoModel
40 import formencode
40 import formencode
41 import logging
41 import logging
42 import traceback
42 import traceback
43
43
44 log = logging.getLogger(__name__)
44 log = logging.getLogger(__name__)
45
45
46 class ReposController(BaseController):
46 class ReposController(BaseController):
47 """REST Controller styled on the Atom Publishing Protocol"""
47 """REST Controller styled on the Atom Publishing Protocol"""
48 # To properly map this controller, ensure your config/routing.py
48 # To properly map this controller, ensure your config/routing.py
49 # file has a resource setup:
49 # file has a resource setup:
50 # map.resource('repo', 'repos')
50 # map.resource('repo', 'repos')
51
51
52 @LoginRequired()
52 @LoginRequired()
53 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
53 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
54 def __before__(self):
54 def __before__(self):
55 c.admin_user = session.get('admin_user')
55 c.admin_user = session.get('admin_user')
56 c.admin_username = session.get('admin_username')
56 c.admin_username = session.get('admin_username')
57 super(ReposController, self).__before__()
57 super(ReposController, self).__before__()
58
58
59 @HasPermissionAllDecorator('hg.admin')
59 @HasPermissionAllDecorator('hg.admin')
60 def index(self, format='html'):
60 def index(self, format='html'):
61 """GET /repos: All items in the collection"""
61 """GET /repos: All items in the collection"""
62 # url('repos')
62 # url('repos')
63 cached_repo_list = ScmModel().get_repos()
63 cached_repo_list = ScmModel().get_repos()
64 c.repos_list = sorted(cached_repo_list, key=itemgetter('name_sort'))
64 c.repos_list = sorted(cached_repo_list, key=itemgetter('name_sort'))
65 return render('admin/repos/repos.html')
65 return render('admin/repos/repos.html')
66
66
67 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
67 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
68 def create(self):
68 def create(self):
69 """POST /repos: Create a new item"""
69 """POST /repos: Create a new item"""
70 # url('repos')
70 # url('repos')
71 repo_model = RepoModel()
71 repo_model = RepoModel()
72 _form = RepoForm()()
72 _form = RepoForm()()
73 form_result = {}
73 form_result = {}
74 try:
74 try:
75 form_result = _form.to_python(dict(request.POST))
75 form_result = _form.to_python(dict(request.POST))
76 repo_model.create(form_result, c.rhodecode_user)
76 repo_model.create(form_result, c.rhodecode_user)
77 h.flash(_('created repository %s') % form_result['repo_name'],
77 h.flash(_('created repository %s') % form_result['repo_name'],
78 category='success')
78 category='success')
79
79
80 if request.POST.get('user_created'):
80 if request.POST.get('user_created'):
81 action_logger(self.rhodecode_user, 'user_created_repo',
81 action_logger(self.rhodecode_user, 'user_created_repo',
82 form_result['repo_name'], '', self.sa)
82 form_result['repo_name'], '', self.sa)
83 else:
83 else:
84 action_logger(self.rhodecode_user, 'admin_created_repo',
84 action_logger(self.rhodecode_user, 'admin_created_repo',
85 form_result['repo_name'], '', self.sa)
85 form_result['repo_name'], '', self.sa)
86
86
87 except formencode.Invalid, errors:
87 except formencode.Invalid, errors:
88 c.new_repo = errors.value['repo_name']
88 c.new_repo = errors.value['repo_name']
89
89
90 if request.POST.get('user_created'):
90 if request.POST.get('user_created'):
91 r = render('admin/repos/repo_add_create_repository.html')
91 r = render('admin/repos/repo_add_create_repository.html')
92 else:
92 else:
93 r = render('admin/repos/repo_add.html')
93 r = render('admin/repos/repo_add.html')
94
94
95 return htmlfill.render(
95 return htmlfill.render(
96 r,
96 r,
97 defaults=errors.value,
97 defaults=errors.value,
98 errors=errors.error_dict or {},
98 errors=errors.error_dict or {},
99 prefix_error=False,
99 prefix_error=False,
100 encoding="UTF-8")
100 encoding="UTF-8")
101
101
102 except Exception:
102 except Exception:
103 log.error(traceback.format_exc())
103 log.error(traceback.format_exc())
104 msg = _('error occured during creation of repository %s') \
104 msg = _('error occured during creation of repository %s') \
105 % form_result.get('repo_name')
105 % form_result.get('repo_name')
106 h.flash(msg, category='error')
106 h.flash(msg, category='error')
107 if request.POST.get('user_created'):
107 if request.POST.get('user_created'):
108 return redirect(url('home'))
108 return redirect(url('home'))
109 return redirect(url('repos'))
109 return redirect(url('repos'))
110
110
111 @HasPermissionAllDecorator('hg.admin')
111 @HasPermissionAllDecorator('hg.admin')
112 def new(self, format='html'):
112 def new(self, format='html'):
113 """GET /repos/new: Form to create a new item"""
113 """GET /repos/new: Form to create a new item"""
114 new_repo = request.GET.get('repo', '')
114 new_repo = request.GET.get('repo', '')
115 c.new_repo = h.repo_name_slug(new_repo)
115 c.new_repo = h.repo_name_slug(new_repo)
116
116
117 return render('admin/repos/repo_add.html')
117 return render('admin/repos/repo_add.html')
118
118
119 @HasPermissionAllDecorator('hg.admin')
119 @HasPermissionAllDecorator('hg.admin')
120 def update(self, repo_name):
120 def update(self, repo_name):
121 """PUT /repos/repo_name: Update an existing item"""
121 """PUT /repos/repo_name: Update an existing item"""
122 # Forms posted to this method should contain a hidden field:
122 # Forms posted to this method should contain a hidden field:
123 # <input type="hidden" name="_method" value="PUT" />
123 # <input type="hidden" name="_method" value="PUT" />
124 # Or using helpers:
124 # Or using helpers:
125 # h.form(url('repo', repo_name=ID),
125 # h.form(url('repo', repo_name=ID),
126 # method='put')
126 # method='put')
127 # url('repo', repo_name=ID)
127 # url('repo', repo_name=ID)
128 repo_model = RepoModel()
128 repo_model = RepoModel()
129 changed_name = repo_name
129 changed_name = repo_name
130 _form = RepoForm(edit=True, old_data={'repo_name':repo_name})()
130 _form = RepoForm(edit=True, old_data={'repo_name':repo_name})()
131
131
132 try:
132 try:
133 form_result = _form.to_python(dict(request.POST))
133 form_result = _form.to_python(dict(request.POST))
134 repo_model.update(repo_name, form_result)
134 repo_model.update(repo_name, form_result)
135 invalidate_cache('get_repo_cached_%s' % repo_name)
135 invalidate_cache('get_repo_cached_%s' % repo_name)
136 h.flash(_('Repository %s updated successfully' % repo_name),
136 h.flash(_('Repository %s updated successfully' % repo_name),
137 category='success')
137 category='success')
138 changed_name = form_result['repo_name']
138 changed_name = form_result['repo_name']
139 action_logger(self.rhodecode_user, 'admin_updated_repo',
139 action_logger(self.rhodecode_user, 'admin_updated_repo',
140 changed_name, '', self.sa)
140 changed_name, '', self.sa)
141
141
142 except formencode.Invalid, errors:
142 except formencode.Invalid, errors:
143 c.repo_info = repo_model.get_by_repo_name(repo_name)
143 c.repo_info = repo_model.get_by_repo_name(repo_name)
144 if c.repo_info.stats:
145 last_rev = c.repo_info.stats.stat_on_revision
146 else:
147 last_rev = 0
148 c.stats_revision = last_rev
149 r = ScmModel().get(repo_name)
150 c.repo_last_rev = r.revisions[-1] if r.revisions else 0
151
152 if last_rev == 0:
153 c.stats_percentage = 0
154 else:
155 c.stats_percentage = '%.2f' % ((float((last_rev)) /
156 c.repo_last_rev) * 100)
157
144 c.users_array = repo_model.get_users_js()
158 c.users_array = repo_model.get_users_js()
145 errors.value.update({'user':c.repo_info.user.username})
159 errors.value.update({'user':c.repo_info.user.username})
146 return htmlfill.render(
160 return htmlfill.render(
147 render('admin/repos/repo_edit.html'),
161 render('admin/repos/repo_edit.html'),
148 defaults=errors.value,
162 defaults=errors.value,
149 errors=errors.error_dict or {},
163 errors=errors.error_dict or {},
150 prefix_error=False,
164 prefix_error=False,
151 encoding="UTF-8")
165 encoding="UTF-8")
152
166
153 except Exception:
167 except Exception:
154 log.error(traceback.format_exc())
168 log.error(traceback.format_exc())
155 h.flash(_('error occurred during update of repository %s') \
169 h.flash(_('error occurred during update of repository %s') \
156 % repo_name, category='error')
170 % repo_name, category='error')
157
171
158 return redirect(url('edit_repo', repo_name=changed_name))
172 return redirect(url('edit_repo', repo_name=changed_name))
159
173
160 @HasPermissionAllDecorator('hg.admin')
174 @HasPermissionAllDecorator('hg.admin')
161 def delete(self, repo_name):
175 def delete(self, repo_name):
162 """DELETE /repos/repo_name: Delete an existing item"""
176 """DELETE /repos/repo_name: Delete an existing item"""
163 # Forms posted to this method should contain a hidden field:
177 # Forms posted to this method should contain a hidden field:
164 # <input type="hidden" name="_method" value="DELETE" />
178 # <input type="hidden" name="_method" value="DELETE" />
165 # Or using helpers:
179 # Or using helpers:
166 # h.form(url('repo', repo_name=ID),
180 # h.form(url('repo', repo_name=ID),
167 # method='delete')
181 # method='delete')
168 # url('repo', repo_name=ID)
182 # url('repo', repo_name=ID)
169
183
170 repo_model = RepoModel()
184 repo_model = RepoModel()
171 repo = repo_model.get_by_repo_name(repo_name)
185 repo = repo_model.get_by_repo_name(repo_name)
172 if not repo:
186 if not repo:
173 h.flash(_('%s repository is not mapped to db perhaps'
187 h.flash(_('%s repository is not mapped to db perhaps'
174 ' it was moved or renamed from the filesystem'
188 ' it was moved or renamed from the filesystem'
175 ' please run the application again'
189 ' please run the application again'
176 ' in order to rescan repositories') % repo_name,
190 ' in order to rescan repositories') % repo_name,
177 category='error')
191 category='error')
178
192
179 return redirect(url('repos'))
193 return redirect(url('repos'))
180 try:
194 try:
181 action_logger(self.rhodecode_user, 'admin_deleted_repo',
195 action_logger(self.rhodecode_user, 'admin_deleted_repo',
182 repo_name, '', self.sa)
196 repo_name, '', self.sa)
183 repo_model.delete(repo)
197 repo_model.delete(repo)
184 invalidate_cache('get_repo_cached_%s' % repo_name)
198 invalidate_cache('get_repo_cached_%s' % repo_name)
185 h.flash(_('deleted repository %s') % repo_name, category='success')
199 h.flash(_('deleted repository %s') % repo_name, category='success')
186
200
187 except Exception, e:
201 except Exception, e:
188 log.error(traceback.format_exc())
202 log.error(traceback.format_exc())
189 h.flash(_('An error occured during deletion of %s') % repo_name,
203 h.flash(_('An error occured during deletion of %s') % repo_name,
190 category='error')
204 category='error')
191
205
192 return redirect(url('repos'))
206 return redirect(url('repos'))
193
207
194 @HasPermissionAllDecorator('hg.admin')
208 @HasPermissionAllDecorator('hg.admin')
195 def delete_perm_user(self, repo_name):
209 def delete_perm_user(self, repo_name):
196 """
210 """
197 DELETE an existing repository permission user
211 DELETE an existing repository permission user
198 :param repo_name:
212 :param repo_name:
199 """
213 """
200
214
201 try:
215 try:
202 repo_model = RepoModel()
216 repo_model = RepoModel()
203 repo_model.delete_perm_user(request.POST, repo_name)
217 repo_model.delete_perm_user(request.POST, repo_name)
204 except Exception, e:
218 except Exception, e:
205 h.flash(_('An error occured during deletion of repository user'),
219 h.flash(_('An error occured during deletion of repository user'),
206 category='error')
220 category='error')
207 raise HTTPInternalServerError()
221 raise HTTPInternalServerError()
208
222
209 @HasPermissionAllDecorator('hg.admin')
223 @HasPermissionAllDecorator('hg.admin')
210 def repo_stats(self, repo_name):
224 def repo_stats(self, repo_name):
211 """
225 """
212 DELETE an existing repository statistics
226 DELETE an existing repository statistics
213 :param repo_name:
227 :param repo_name:
214 """
228 """
215
229
216 try:
230 try:
217 repo_model = RepoModel()
231 repo_model = RepoModel()
218 repo_model.delete_stats(repo_name)
232 repo_model.delete_stats(repo_name)
219 except Exception, e:
233 except Exception, e:
220 h.flash(_('An error occured during deletion of repository stats'),
234 h.flash(_('An error occured during deletion of repository stats'),
221 category='error')
235 category='error')
222 return redirect(url('edit_repo', repo_name=repo_name))
236 return redirect(url('edit_repo', repo_name=repo_name))
223
237
224 @HasPermissionAllDecorator('hg.admin')
238 @HasPermissionAllDecorator('hg.admin')
225 def repo_cache(self, repo_name):
239 def repo_cache(self, repo_name):
226 """
240 """
227 INVALIDATE exisitings repository cache
241 INVALIDATE exisitings repository cache
228 :param repo_name:
242 :param repo_name:
229 """
243 """
230
244
231 try:
245 try:
232 ScmModel().mark_for_invalidation(repo_name)
246 ScmModel().mark_for_invalidation(repo_name)
233 except Exception, e:
247 except Exception, e:
234 h.flash(_('An error occurred during cache invalidation'),
248 h.flash(_('An error occurred during cache invalidation'),
235 category='error')
249 category='error')
236 return redirect(url('edit_repo', repo_name=repo_name))
250 return redirect(url('edit_repo', repo_name=repo_name))
237
251
238 @HasPermissionAllDecorator('hg.admin')
252 @HasPermissionAllDecorator('hg.admin')
239 def show(self, repo_name, format='html'):
253 def show(self, repo_name, format='html'):
240 """GET /repos/repo_name: Show a specific item"""
254 """GET /repos/repo_name: Show a specific item"""
241 # url('repo', repo_name=ID)
255 # url('repo', repo_name=ID)
242
256
243 @HasPermissionAllDecorator('hg.admin')
257 @HasPermissionAllDecorator('hg.admin')
244 def edit(self, repo_name, format='html'):
258 def edit(self, repo_name, format='html'):
245 """GET /repos/repo_name/edit: Form to edit an existing item"""
259 """GET /repos/repo_name/edit: Form to edit an existing item"""
246 # url('edit_repo', repo_name=ID)
260 # url('edit_repo', repo_name=ID)
247 repo_model = RepoModel()
261 repo_model = RepoModel()
248 c.repo_info = repo = repo_model.get_by_repo_name(repo_name)
262 c.repo_info = repo_model.get_by_repo_name(repo_name)
249 if repo.stats:
263 if c.repo_info.stats:
250 last_rev = repo.stats.stat_on_revision
264 last_rev = c.repo_info.stats.stat_on_revision
251 else:
265 else:
252 last_rev = 0
266 last_rev = 0
253 c.stats_revision = last_rev
267 c.stats_revision = last_rev
254 r = ScmModel().get(repo_name)
268 r = ScmModel().get(repo_name)
255 c.repo_last_rev = r.revisions[-1] if r.revisions else 0
269 c.repo_last_rev = r.revisions[-1] if r.revisions else 0
256
270
257 if last_rev == 0:
271 if last_rev == 0:
258 c.stats_percentage = 0
272 c.stats_percentage = 0
259 else:
273 else:
260 c.stats_percentage = '%.2f' % ((float((last_rev)) / c.repo_last_rev) * 100)
274 c.stats_percentage = '%.2f' % ((float((last_rev)) /
275 c.repo_last_rev) * 100)
261
276
262
277
263 if not repo:
278 if not c.repo_info:
264 h.flash(_('%s repository is not mapped to db perhaps'
279 h.flash(_('%s repository is not mapped to db perhaps'
265 ' it was created or renamed from the filesystem'
280 ' it was created or renamed from the filesystem'
266 ' please run the application again'
281 ' please run the application again'
267 ' in order to rescan repositories') % repo_name,
282 ' in order to rescan repositories') % repo_name,
268 category='error')
283 category='error')
269
284
270 return redirect(url('repos'))
285 return redirect(url('repos'))
271 defaults = c.repo_info.__dict__
286 defaults = c.repo_info.__dict__
272 if c.repo_info.user:
287 if c.repo_info.user:
273 defaults.update({'user':c.repo_info.user.username})
288 defaults.update({'user':c.repo_info.user.username})
274 else:
289 else:
275 replacement_user = self.sa.query(User)\
290 replacement_user = self.sa.query(User)\
276 .filter(User.admin == True).first().username
291 .filter(User.admin == True).first().username
277 defaults.update({'user':replacement_user})
292 defaults.update({'user':replacement_user})
278
293
279 c.users_array = repo_model.get_users_js()
294 c.users_array = repo_model.get_users_js()
280
295
281 for p in c.repo_info.repo_to_perm:
296 for p in c.repo_info.repo_to_perm:
282 defaults.update({'perm_%s' % p.user.username:
297 defaults.update({'perm_%s' % p.user.username:
283 p.permission.permission_name})
298 p.permission.permission_name})
284
299
285 return htmlfill.render(
300 return htmlfill.render(
286 render('admin/repos/repo_edit.html'),
301 render('admin/repos/repo_edit.html'),
287 defaults=defaults,
302 defaults=defaults,
288 encoding="UTF-8",
303 encoding="UTF-8",
289 force_defaults=False
304 force_defaults=False
290 )
305 )
@@ -1,143 +1,144
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 package.rhodecode.controllers.summary
3 package.rhodecode.controllers.summary
4 ~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~
5
5
6 Summary controller for Rhodecode
6 Summary controller for Rhodecode
7
7 :created_on: Apr 18, 2010
8 :created_on: Apr 18, 2010
8 :author: marcink
9 :author: marcink
9 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
11 """
12 """
12 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
13 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
14 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
15 # 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.
16 #
17 #
17 # 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,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
21 # GNU General Public License for more details.
21 #
22 #
22 # 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
23 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
24 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
26
27
27 import calendar
28 import calendar
28 import logging
29 import logging
29 from time import mktime
30 from time import mktime
30 from datetime import datetime, timedelta
31 from datetime import datetime, timedelta
31
32
32 from vcs.exceptions import ChangesetError
33 from vcs.exceptions import ChangesetError
33
34
34 from pylons import tmpl_context as c, request, url
35 from pylons import tmpl_context as c, request, url
35 from pylons.i18n.translation import _
36 from pylons.i18n.translation import _
36
37
37 from rhodecode.model.scm import ScmModel
38 from rhodecode.model.scm import ScmModel
38 from rhodecode.model.db import Statistics
39 from rhodecode.model.db import Statistics
39
40
40 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
41 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
41 from rhodecode.lib.base import BaseController, render
42 from rhodecode.lib.base import BaseController, render
42 from rhodecode.lib.utils import OrderedDict, EmptyChangeset
43 from rhodecode.lib.utils import OrderedDict, EmptyChangeset
43
44
44 from rhodecode.lib.celerylib import run_task
45 from rhodecode.lib.celerylib import run_task
45 from rhodecode.lib.celerylib.tasks import get_commits_stats
46 from rhodecode.lib.celerylib.tasks import get_commits_stats
46
47
47 from webhelpers.paginate import Page
48 from webhelpers.paginate import Page
48
49
49 try:
50 try:
50 import json
51 import json
51 except ImportError:
52 except ImportError:
52 #python 2.5 compatibility
53 #python 2.5 compatibility
53 import simplejson as json
54 import simplejson as json
54 log = logging.getLogger(__name__)
55 log = logging.getLogger(__name__)
55
56
56 class SummaryController(BaseController):
57 class SummaryController(BaseController):
57
58
58 @LoginRequired()
59 @LoginRequired()
59 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
60 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
60 'repository.admin')
61 'repository.admin')
61 def __before__(self):
62 def __before__(self):
62 super(SummaryController, self).__before__()
63 super(SummaryController, self).__before__()
63
64
64 def index(self):
65 def index(self):
65 scm_model = ScmModel()
66 scm_model = ScmModel()
66 c.repo_info = scm_model.get_repo(c.repo_name)
67 c.repo_info = scm_model.get_repo(c.repo_name)
67 c.following = scm_model.is_following_repo(c.repo_name,
68 c.following = scm_model.is_following_repo(c.repo_name,
68 c.rhodecode_user.user_id)
69 c.rhodecode_user.user_id)
69 def url_generator(**kw):
70 def url_generator(**kw):
70 return url('shortlog_home', repo_name=c.repo_name, **kw)
71 return url('shortlog_home', repo_name=c.repo_name, **kw)
71
72
72 c.repo_changesets = Page(c.repo_info, page=1, items_per_page=10,
73 c.repo_changesets = Page(c.repo_info, page=1, items_per_page=10,
73 url=url_generator)
74 url=url_generator)
74
75
75 e = request.environ
76 e = request.environ
76
77
77 if self.rhodecode_user.username == 'default':
78 if self.rhodecode_user.username == 'default':
78 password = ':default'
79 password = ':default'
79 else:
80 else:
80 password = ''
81 password = ''
81
82
82 uri = u'%(protocol)s://%(user)s%(password)s@%(host)s%(prefix)s/%(repo_name)s' % {
83 uri = u'%(protocol)s://%(user)s%(password)s@%(host)s%(prefix)s/%(repo_name)s' % {
83 'protocol': e.get('wsgi.url_scheme'),
84 'protocol': e.get('wsgi.url_scheme'),
84 'user':str(c.rhodecode_user.username),
85 'user':str(c.rhodecode_user.username),
85 'password':password,
86 'password':password,
86 'host':e.get('HTTP_HOST'),
87 'host':e.get('HTTP_HOST'),
87 'prefix':e.get('SCRIPT_NAME'),
88 'prefix':e.get('SCRIPT_NAME'),
88 'repo_name':c.repo_name, }
89 'repo_name':c.repo_name, }
89 c.clone_repo_url = uri
90 c.clone_repo_url = uri
90 c.repo_tags = OrderedDict()
91 c.repo_tags = OrderedDict()
91 for name, hash in c.repo_info.tags.items()[:10]:
92 for name, hash in c.repo_info.tags.items()[:10]:
92 try:
93 try:
93 c.repo_tags[name] = c.repo_info.get_changeset(hash)
94 c.repo_tags[name] = c.repo_info.get_changeset(hash)
94 except ChangesetError:
95 except ChangesetError:
95 c.repo_tags[name] = EmptyChangeset(hash)
96 c.repo_tags[name] = EmptyChangeset(hash)
96
97
97 c.repo_branches = OrderedDict()
98 c.repo_branches = OrderedDict()
98 for name, hash in c.repo_info.branches.items()[:10]:
99 for name, hash in c.repo_info.branches.items()[:10]:
99 try:
100 try:
100 c.repo_branches[name] = c.repo_info.get_changeset(hash)
101 c.repo_branches[name] = c.repo_info.get_changeset(hash)
101 except ChangesetError:
102 except ChangesetError:
102 c.repo_branches[name] = EmptyChangeset(hash)
103 c.repo_branches[name] = EmptyChangeset(hash)
103
104
104 td = datetime.today() + timedelta(days=1)
105 td = datetime.today() + timedelta(days=1)
105 y, m, d = td.year, td.month, td.day
106 y, m, d = td.year, td.month, td.day
106
107
107 ts_min_y = mktime((y - 1, (td - timedelta(days=calendar.mdays[m])).month,
108 ts_min_y = mktime((y - 1, (td - timedelta(days=calendar.mdays[m])).month,
108 d, 0, 0, 0, 0, 0, 0,))
109 d, 0, 0, 0, 0, 0, 0,))
109 ts_min_m = mktime((y, (td - timedelta(days=calendar.mdays[m])).month,
110 ts_min_m = mktime((y, (td - timedelta(days=calendar.mdays[m])).month,
110 d, 0, 0, 0, 0, 0, 0,))
111 d, 0, 0, 0, 0, 0, 0,))
111
112
112 ts_max_y = mktime((y, m, d, 0, 0, 0, 0, 0, 0,))
113 ts_max_y = mktime((y, m, d, 0, 0, 0, 0, 0, 0,))
113 if c.repo_info.dbrepo.enable_statistics:
114 if c.repo_info.dbrepo.enable_statistics:
114 c.no_data_msg = _('No data loaded yet')
115 c.no_data_msg = _('No data loaded yet')
115 run_task(get_commits_stats, c.repo_info.name, ts_min_y, ts_max_y)
116 run_task(get_commits_stats, c.repo_info.name, ts_min_y, ts_max_y)
116 else:
117 else:
117 c.no_data_msg = _('Statistics are disabled for this repository')
118 c.no_data_msg = _('Statistics update are disabled for this repository')
118 c.ts_min = ts_min_m
119 c.ts_min = ts_min_m
119 c.ts_max = ts_max_y
120 c.ts_max = ts_max_y
120
121
121 stats = self.sa.query(Statistics)\
122 stats = self.sa.query(Statistics)\
122 .filter(Statistics.repository == c.repo_info.dbrepo)\
123 .filter(Statistics.repository == c.repo_info.dbrepo)\
123 .scalar()
124 .scalar()
124
125
125
126
126 if stats and stats.languages:
127 if stats and stats.languages:
127 c.no_data = False
128 c.no_data = False is c.repo_info.dbrepo.enable_statistics
128 lang_stats = json.loads(stats.languages)
129 lang_stats = json.loads(stats.languages)
129 c.commit_data = stats.commit_activity
130 c.commit_data = stats.commit_activity
130 c.overview_data = stats.commit_activity_combined
131 c.overview_data = stats.commit_activity_combined
131 c.trending_languages = json.dumps(OrderedDict(
132 c.trending_languages = json.dumps(OrderedDict(
132 sorted(lang_stats.items(), reverse=True,
133 sorted(lang_stats.items(), reverse=True,
133 key=lambda k: k[1])[:10]
134 key=lambda k: k[1])[:10]
134 )
135 )
135 )
136 )
136 else:
137 else:
137 c.commit_data = json.dumps({})
138 c.commit_data = json.dumps({})
138 c.overview_data = json.dumps([[ts_min_y, 0], [ts_max_y, 10] ])
139 c.overview_data = json.dumps([[ts_min_y, 0], [ts_max_y, 10] ])
139 c.trending_languages = json.dumps({})
140 c.trending_languages = json.dumps({})
140 c.no_data = True
141 c.no_data = True
141
142
142 return render('summary/summary.html')
143 return render('summary/summary.html')
143
144
@@ -1,264 +1,262
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.repo
3 rhodecode.model.repo
4 ~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~
5
5
6 Repository model for rhodecode
6 Repository model for rhodecode
7
7
8 :created_on: Jun 5, 2010
8 :created_on: Jun 5, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27 import os
27 import os
28 import shutil
28 import shutil
29 import logging
29 import logging
30 import traceback
30 import traceback
31 from datetime import datetime
31 from datetime import datetime
32
32
33 from pylons import app_globals as g
33 from pylons import app_globals as g
34
34
35 from rhodecode.model import BaseModel
35 from rhodecode.model import BaseModel
36 from rhodecode.model.caching_query import FromCache
36 from rhodecode.model.caching_query import FromCache
37 from rhodecode.model.db import Repository, RepoToPerm, User, Permission, \
37 from rhodecode.model.db import Repository, RepoToPerm, User, Permission, \
38 Statistics
38 Statistics
39 from rhodecode.model.user import UserModel
39 from rhodecode.model.user import UserModel
40
40
41 from vcs.backends import get_backend
41 from vcs.backends import get_backend
42
42
43 log = logging.getLogger(__name__)
43 log = logging.getLogger(__name__)
44
44
45 class RepoModel(BaseModel):
45 class RepoModel(BaseModel):
46
46
47 def get(self, repo_id, cache=False):
47 def get(self, repo_id, cache=False):
48 repo = self.sa.query(Repository)\
48 repo = self.sa.query(Repository)\
49 .filter(Repository.repo_id == repo_id)
49 .filter(Repository.repo_id == repo_id)
50
50
51 if cache:
51 if cache:
52 repo = repo.options(FromCache("sql_cache_short",
52 repo = repo.options(FromCache("sql_cache_short",
53 "get_repo_%s" % repo_id))
53 "get_repo_%s" % repo_id))
54 return repo.scalar()
54 return repo.scalar()
55
55
56
56
57 def get_by_repo_name(self, repo_name, cache=False):
57 def get_by_repo_name(self, repo_name, cache=False):
58 repo = self.sa.query(Repository)\
58 repo = self.sa.query(Repository)\
59 .filter(Repository.repo_name == repo_name)
59 .filter(Repository.repo_name == repo_name)
60
60
61 if cache:
61 if cache:
62 repo = repo.options(FromCache("sql_cache_short",
62 repo = repo.options(FromCache("sql_cache_short",
63 "get_repo_%s" % repo_name))
63 "get_repo_%s" % repo_name))
64 return repo.scalar()
64 return repo.scalar()
65
65
66 def get_users_js(self):
66 def get_users_js(self):
67
67
68 users = self.sa.query(User).filter(User.active == True).all()
68 users = self.sa.query(User).filter(User.active == True).all()
69 u_tmpl = '''{id:%s, fname:"%s", lname:"%s", nname:"%s"},'''
69 u_tmpl = '''{id:%s, fname:"%s", lname:"%s", nname:"%s"},'''
70 users_array = '[%s];' % '\n'.join([u_tmpl % (u.user_id, u.name,
70 users_array = '[%s];' % '\n'.join([u_tmpl % (u.user_id, u.name,
71 u.lastname, u.username)
71 u.lastname, u.username)
72 for u in users])
72 for u in users])
73 return users_array
73 return users_array
74
74
75
75
76 def update(self, repo_name, form_data):
76 def update(self, repo_name, form_data):
77 try:
77 try:
78 cur_repo = self.get_by_repo_name(repo_name, cache=False)
79 user_model = UserModel(self.sa)
78
80
79 #update permissions
81 #update permissions
80 for username, perm in form_data['perms_updates']:
82 for username, perm in form_data['perms_updates']:
81 r2p = self.sa.query(RepoToPerm)\
83 r2p = self.sa.query(RepoToPerm)\
82 .filter(RepoToPerm.user == UserModel(self.sa)\
84 .filter(RepoToPerm.user == user_model.get_by_username(username))\
83 .get_by_username(username, cache=False))\
85 .filter(RepoToPerm.repository == cur_repo)\
84 .filter(RepoToPerm.repository == \
85 self.get_by_repo_name(repo_name))\
86 .one()
86 .one()
87
87
88 r2p.permission_id = self.sa.query(Permission).filter(
88 r2p.permission = self.sa.query(Permission)\
89 Permission.permission_name ==
89 .filter(Permission.permission_name == perm)\
90 perm).one().permission_id
90 .scalar()
91 self.sa.add(r2p)
91 self.sa.add(r2p)
92
92
93 #set new permissions
93 #set new permissions
94 for username, perm in form_data['perms_new']:
94 for username, perm in form_data['perms_new']:
95 r2p = RepoToPerm()
95 r2p = RepoToPerm()
96 r2p.repository = self.get_by_repo_name(repo_name)
96 r2p.repository = cur_repo
97 r2p.user = UserModel(self.sa).get_by_username(username, cache=False)
97 r2p.user = user_model.get_by_username(username, cache=False)
98
98
99 r2p.permission_id = self.sa.query(Permission).filter(
99 r2p.permission = self.sa.query(Permission)\
100 Permission.permission_name == perm)\
100 .filter(Permission.permission_name == perm)\
101 .one().permission_id
101 .scalar()
102 self.sa.add(r2p)
102 self.sa.add(r2p)
103
103
104 #update current repo
104 #update current repo
105 cur_repo = self.get_by_repo_name(repo_name, cache=False)
106
107 for k, v in form_data.items():
105 for k, v in form_data.items():
108 if k == 'user':
106 if k == 'user':
109 cur_repo.user_id = v
107 cur_repo.user = user_model.get(v)
110 else:
108 else:
111 setattr(cur_repo, k, v)
109 setattr(cur_repo, k, v)
112
110
113 self.sa.add(cur_repo)
111 self.sa.add(cur_repo)
114
112
115 if repo_name != form_data['repo_name']:
113 if repo_name != form_data['repo_name']:
116 #rename our data
114 #rename our data
117 self.__rename_repo(repo_name, form_data['repo_name'])
115 self.__rename_repo(repo_name, form_data['repo_name'])
118
116
119 self.sa.commit()
117 self.sa.commit()
120 except:
118 except:
121 log.error(traceback.format_exc())
119 log.error(traceback.format_exc())
122 self.sa.rollback()
120 self.sa.rollback()
123 raise
121 raise
124
122
125 def create(self, form_data, cur_user, just_db=False, fork=False):
123 def create(self, form_data, cur_user, just_db=False, fork=False):
126 try:
124 try:
127 if fork:
125 if fork:
128 #force str since hg doesn't go with unicode
126 #force str since hg doesn't go with unicode
129 repo_name = str(form_data['fork_name'])
127 repo_name = str(form_data['fork_name'])
130 org_name = str(form_data['repo_name'])
128 org_name = str(form_data['repo_name'])
131
129
132 else:
130 else:
133 org_name = repo_name = str(form_data['repo_name'])
131 org_name = repo_name = str(form_data['repo_name'])
134 new_repo = Repository()
132 new_repo = Repository()
135 for k, v in form_data.items():
133 for k, v in form_data.items():
136 if k == 'repo_name':
134 if k == 'repo_name':
137 v = repo_name
135 v = repo_name
138 setattr(new_repo, k, v)
136 setattr(new_repo, k, v)
139
137
140 if fork:
138 if fork:
141 parent_repo = self.sa.query(Repository)\
139 parent_repo = self.sa.query(Repository)\
142 .filter(Repository.repo_name == org_name).scalar()
140 .filter(Repository.repo_name == org_name).scalar()
143 new_repo.fork = parent_repo
141 new_repo.fork = parent_repo
144
142
145 new_repo.user_id = cur_user.user_id
143 new_repo.user_id = cur_user.user_id
146 self.sa.add(new_repo)
144 self.sa.add(new_repo)
147
145
148 #create default permission
146 #create default permission
149 repo_to_perm = RepoToPerm()
147 repo_to_perm = RepoToPerm()
150 default = 'repository.read'
148 default = 'repository.read'
151 for p in UserModel(self.sa).get_by_username('default', cache=False).user_perms:
149 for p in UserModel(self.sa).get_by_username('default', cache=False).user_perms:
152 if p.permission.permission_name.startswith('repository.'):
150 if p.permission.permission_name.startswith('repository.'):
153 default = p.permission.permission_name
151 default = p.permission.permission_name
154 break
152 break
155
153
156 default_perm = 'repository.none' if form_data['private'] else default
154 default_perm = 'repository.none' if form_data['private'] else default
157
155
158 repo_to_perm.permission_id = self.sa.query(Permission)\
156 repo_to_perm.permission_id = self.sa.query(Permission)\
159 .filter(Permission.permission_name == default_perm)\
157 .filter(Permission.permission_name == default_perm)\
160 .one().permission_id
158 .one().permission_id
161
159
162 repo_to_perm.repository_id = new_repo.repo_id
160 repo_to_perm.repository_id = new_repo.repo_id
163 repo_to_perm.user_id = UserModel(self.sa)\
161 repo_to_perm.user_id = UserModel(self.sa)\
164 .get_by_username('default', cache=False).user_id
162 .get_by_username('default', cache=False).user_id
165
163
166 self.sa.add(repo_to_perm)
164 self.sa.add(repo_to_perm)
167 self.sa.commit()
165 self.sa.commit()
168
166
169
167
170 #now automatically start following this repository as owner
168 #now automatically start following this repository as owner
171 from rhodecode.model.scm import ScmModel
169 from rhodecode.model.scm import ScmModel
172 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
170 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
173 cur_user.user_id)
171 cur_user.user_id)
174
172
175 if not just_db:
173 if not just_db:
176 self.__create_repo(repo_name, form_data['repo_type'])
174 self.__create_repo(repo_name, form_data['repo_type'])
177 except:
175 except:
178 log.error(traceback.format_exc())
176 log.error(traceback.format_exc())
179 self.sa.rollback()
177 self.sa.rollback()
180 raise
178 raise
181
179
182 def create_fork(self, form_data, cur_user):
180 def create_fork(self, form_data, cur_user):
183 from rhodecode.lib.celerylib import tasks, run_task
181 from rhodecode.lib.celerylib import tasks, run_task
184 run_task(tasks.create_repo_fork, form_data, cur_user)
182 run_task(tasks.create_repo_fork, form_data, cur_user)
185
183
186 def delete(self, repo):
184 def delete(self, repo):
187 try:
185 try:
188 self.sa.delete(repo)
186 self.sa.delete(repo)
189 self.__delete_repo(repo)
187 self.__delete_repo(repo)
190 self.sa.commit()
188 self.sa.commit()
191 except:
189 except:
192 log.error(traceback.format_exc())
190 log.error(traceback.format_exc())
193 self.sa.rollback()
191 self.sa.rollback()
194 raise
192 raise
195
193
196 def delete_perm_user(self, form_data, repo_name):
194 def delete_perm_user(self, form_data, repo_name):
197 try:
195 try:
198 self.sa.query(RepoToPerm)\
196 self.sa.query(RepoToPerm)\
199 .filter(RepoToPerm.repository \
197 .filter(RepoToPerm.repository \
200 == self.get_by_repo_name(repo_name))\
198 == self.get_by_repo_name(repo_name))\
201 .filter(RepoToPerm.user_id == form_data['user_id']).delete()
199 .filter(RepoToPerm.user_id == form_data['user_id']).delete()
202 self.sa.commit()
200 self.sa.commit()
203 except:
201 except:
204 log.error(traceback.format_exc())
202 log.error(traceback.format_exc())
205 self.sa.rollback()
203 self.sa.rollback()
206 raise
204 raise
207
205
208 def delete_stats(self, repo_name):
206 def delete_stats(self, repo_name):
209 try:
207 try:
210 self.sa.query(Statistics)\
208 self.sa.query(Statistics)\
211 .filter(Statistics.repository == \
209 .filter(Statistics.repository == \
212 self.get_by_repo_name(repo_name)).delete()
210 self.get_by_repo_name(repo_name)).delete()
213 self.sa.commit()
211 self.sa.commit()
214 except:
212 except:
215 log.error(traceback.format_exc())
213 log.error(traceback.format_exc())
216 self.sa.rollback()
214 self.sa.rollback()
217 raise
215 raise
218
216
219
217
220 def __create_repo(self, repo_name, alias):
218 def __create_repo(self, repo_name, alias):
221 """
219 """
222 makes repository on filesystem
220 makes repository on filesystem
223 :param repo_name:
221 :param repo_name:
224 :param alias:
222 :param alias:
225 """
223 """
226 from rhodecode.lib.utils import check_repo
224 from rhodecode.lib.utils import check_repo
227 repo_path = os.path.join(g.base_path, repo_name)
225 repo_path = os.path.join(g.base_path, repo_name)
228 if check_repo(repo_name, g.base_path):
226 if check_repo(repo_name, g.base_path):
229 log.info('creating repo %s in %s', repo_name, repo_path)
227 log.info('creating repo %s in %s', repo_name, repo_path)
230 backend = get_backend(alias)
228 backend = get_backend(alias)
231 backend(repo_path, create=True)
229 backend(repo_path, create=True)
232
230
233 def __rename_repo(self, old, new):
231 def __rename_repo(self, old, new):
234 """
232 """
235 renames repository on filesystem
233 renames repository on filesystem
236 :param old: old name
234 :param old: old name
237 :param new: new name
235 :param new: new name
238 """
236 """
239 log.info('renaming repo from %s to %s', old, new)
237 log.info('renaming repo from %s to %s', old, new)
240
238
241 old_path = os.path.join(g.base_path, old)
239 old_path = os.path.join(g.base_path, old)
242 new_path = os.path.join(g.base_path, new)
240 new_path = os.path.join(g.base_path, new)
243 if os.path.isdir(new_path):
241 if os.path.isdir(new_path):
244 raise Exception('Was trying to rename to already existing dir %s',
242 raise Exception('Was trying to rename to already existing dir %s',
245 new_path)
243 new_path)
246 shutil.move(old_path, new_path)
244 shutil.move(old_path, new_path)
247
245
248 def __delete_repo(self, repo):
246 def __delete_repo(self, repo):
249 """
247 """
250 removes repo from filesystem, the removal is acctually made by
248 removes repo from filesystem, the removal is acctually made by
251 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
249 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
252 repository is no longer valid for rhodecode, can be undeleted later on
250 repository is no longer valid for rhodecode, can be undeleted later on
253 by reverting the renames on this repository
251 by reverting the renames on this repository
254 :param repo: repo object
252 :param repo: repo object
255 """
253 """
256 rm_path = os.path.join(g.base_path, repo.repo_name)
254 rm_path = os.path.join(g.base_path, repo.repo_name)
257 log.info("Removing %s", rm_path)
255 log.info("Removing %s", rm_path)
258 #disable hg/git
256 #disable hg/git
259 alias = repo.repo_type
257 alias = repo.repo_type
260 shutil.move(os.path.join(rm_path, '.%s' % alias),
258 shutil.move(os.path.join(rm_path, '.%s' % alias),
261 os.path.join(rm_path, 'rm__.%s' % alias))
259 os.path.join(rm_path, 'rm__.%s' % alias))
262 #disable repo
260 #disable repo
263 shutil.move(rm_path, os.path.join(g.base_path, 'rm__%s__%s' \
261 shutil.move(rm_path, os.path.join(g.base_path, 'rm__%s__%s' \
264 % (datetime.today(), repo.repo_name)))
262 % (datetime.today(), repo.repo_name)))
@@ -1,643 +1,643
1 <%inherit file="/base/base.html"/>
1 <%inherit file="/base/base.html"/>
2
2
3 <%def name="title()">
3 <%def name="title()">
4 ${c.repo_name} ${_('Summary')} - ${c.rhodecode_name}
4 ${c.repo_name} ${_('Summary')} - ${c.rhodecode_name}
5 </%def>
5 </%def>
6
6
7 <%def name="breadcrumbs_links()">
7 <%def name="breadcrumbs_links()">
8 ${h.link_to(u'Home',h.url('/'))}
8 ${h.link_to(u'Home',h.url('/'))}
9 &raquo;
9 &raquo;
10 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
10 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
11 &raquo;
11 &raquo;
12 ${_('summary')}
12 ${_('summary')}
13 </%def>
13 </%def>
14
14
15 <%def name="page_nav()">
15 <%def name="page_nav()">
16 ${self.menu('summary')}
16 ${self.menu('summary')}
17 </%def>
17 </%def>
18
18
19 <%def name="main()">
19 <%def name="main()">
20 <div class="box box-left">
20 <div class="box box-left">
21 <!-- box / title -->
21 <!-- box / title -->
22 <div class="title">
22 <div class="title">
23 ${self.breadcrumbs()}
23 ${self.breadcrumbs()}
24 </div>
24 </div>
25 <!-- end box / title -->
25 <!-- end box / title -->
26 <div class="form">
26 <div class="form">
27 <div class="fields">
27 <div class="fields">
28
28
29 <div class="field">
29 <div class="field">
30 <div class="label">
30 <div class="label">
31 <label>${_('Name')}:</label>
31 <label>${_('Name')}:</label>
32 </div>
32 </div>
33 <div class="input-short">
33 <div class="input-short">
34 %if c.repo_info.dbrepo.repo_type =='hg':
34 %if c.repo_info.dbrepo.repo_type =='hg':
35 <img style="margin-bottom:2px" class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="/images/icons/hgicon.png"/>
35 <img style="margin-bottom:2px" class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="/images/icons/hgicon.png"/>
36 %endif
36 %endif
37 %if c.repo_info.dbrepo.repo_type =='git':
37 %if c.repo_info.dbrepo.repo_type =='git':
38 <img style="margin-bottom:2px" class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="/images/icons/giticon.png"/>
38 <img style="margin-bottom:2px" class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="/images/icons/giticon.png"/>
39 %endif
39 %endif
40
40
41 %if c.repo_info.dbrepo.private:
41 %if c.repo_info.dbrepo.private:
42 <img style="margin-bottom:2px" class="icon" title="${_('private repository')}" alt="${_('private repository')}" src="/images/icons/lock.png"/>
42 <img style="margin-bottom:2px" class="icon" title="${_('private repository')}" alt="${_('private repository')}" src="/images/icons/lock.png"/>
43 %else:
43 %else:
44 <img style="margin-bottom:2px" class="icon" title="${_('public repository')}" alt="${_('public repository')}" src="/images/icons/lock_open.png"/>
44 <img style="margin-bottom:2px" class="icon" title="${_('public repository')}" alt="${_('public repository')}" src="/images/icons/lock_open.png"/>
45 %endif
45 %endif
46 <span style="font-size: 1.6em;font-weight: bold;vertical-align: baseline;">${c.repo_info.name}</span>
46 <span style="font-size: 1.6em;font-weight: bold;vertical-align: baseline;">${c.repo_info.name}</span>
47 %if c.rhodecode_user.username != 'default':
47 %if c.rhodecode_user.username != 'default':
48 %if c.following:
48 %if c.following:
49 <span id="follow_toggle" class="following" title="${_('Stop following this repository')}"
49 <span id="follow_toggle" class="following" title="${_('Stop following this repository')}"
50 onclick="javascript:toggleFollowingRepo(${c.repo_info.dbrepo.repo_id},'${str(h.get_token())}')">
50 onclick="javascript:toggleFollowingRepo(${c.repo_info.dbrepo.repo_id},'${str(h.get_token())}')">
51 </span>
51 </span>
52 %else:
52 %else:
53 <span id="follow_toggle" class="follow" title="${_('Start following this repository')}"
53 <span id="follow_toggle" class="follow" title="${_('Start following this repository')}"
54 onclick="javascript:toggleFollowingRepo(${c.repo_info.dbrepo.repo_id},'${str(h.get_token())}')">
54 onclick="javascript:toggleFollowingRepo(${c.repo_info.dbrepo.repo_id},'${str(h.get_token())}')">
55 </span>
55 </span>
56 %endif
56 %endif
57 %endif:
57 %endif:
58 <br/>
58 <br/>
59 %if c.repo_info.dbrepo.fork:
59 %if c.repo_info.dbrepo.fork:
60 <span style="margin-top:5px">
60 <span style="margin-top:5px">
61 <a href="${h.url('summary_home',repo_name=c.repo_info.dbrepo.fork.repo_name)}">
61 <a href="${h.url('summary_home',repo_name=c.repo_info.dbrepo.fork.repo_name)}">
62 <img class="icon" alt="${_('public')}"
62 <img class="icon" alt="${_('public')}"
63 title="${_('Fork of')} ${c.repo_info.dbrepo.fork.repo_name}"
63 title="${_('Fork of')} ${c.repo_info.dbrepo.fork.repo_name}"
64 src="/images/icons/arrow_divide.png"/>
64 src="/images/icons/arrow_divide.png"/>
65 ${_('Fork of')} ${c.repo_info.dbrepo.fork.repo_name}
65 ${_('Fork of')} ${c.repo_info.dbrepo.fork.repo_name}
66 </a>
66 </a>
67 </span>
67 </span>
68 %endif
68 %endif
69 </div>
69 </div>
70 </div>
70 </div>
71
71
72
72
73 <div class="field">
73 <div class="field">
74 <div class="label">
74 <div class="label">
75 <label>${_('Description')}:</label>
75 <label>${_('Description')}:</label>
76 </div>
76 </div>
77 <div class="input-short">
77 <div class="input-short">
78 ${c.repo_info.dbrepo.description}
78 ${c.repo_info.dbrepo.description}
79 </div>
79 </div>
80 </div>
80 </div>
81
81
82
82
83 <div class="field">
83 <div class="field">
84 <div class="label">
84 <div class="label">
85 <label>${_('Contact')}:</label>
85 <label>${_('Contact')}:</label>
86 </div>
86 </div>
87 <div class="input-short">
87 <div class="input-short">
88 <div class="gravatar">
88 <div class="gravatar">
89 <img alt="gravatar" src="${h.gravatar_url(c.repo_info.dbrepo.user.email)}"/>
89 <img alt="gravatar" src="${h.gravatar_url(c.repo_info.dbrepo.user.email)}"/>
90 </div>
90 </div>
91 ${_('Username')}: ${c.repo_info.dbrepo.user.username}<br/>
91 ${_('Username')}: ${c.repo_info.dbrepo.user.username}<br/>
92 ${_('Name')}: ${c.repo_info.dbrepo.user.name} ${c.repo_info.dbrepo.user.lastname}<br/>
92 ${_('Name')}: ${c.repo_info.dbrepo.user.name} ${c.repo_info.dbrepo.user.lastname}<br/>
93 ${_('Email')}: <a href="mailto:${c.repo_info.dbrepo.user.email}">${c.repo_info.dbrepo.user.email}</a>
93 ${_('Email')}: <a href="mailto:${c.repo_info.dbrepo.user.email}">${c.repo_info.dbrepo.user.email}</a>
94 </div>
94 </div>
95 </div>
95 </div>
96
96
97 <div class="field">
97 <div class="field">
98 <div class="label">
98 <div class="label">
99 <label>${_('Last change')}:</label>
99 <label>${_('Last change')}:</label>
100 </div>
100 </div>
101 <div class="input-short">
101 <div class="input-short">
102 ${h.age(c.repo_info.last_change)} - ${c.repo_info.last_change}
102 ${h.age(c.repo_info.last_change)} - ${c.repo_info.last_change}
103 ${_('by')} ${h.get_changeset_safe(c.repo_info,'tip').author}
103 ${_('by')} ${h.get_changeset_safe(c.repo_info,'tip').author}
104
104
105 </div>
105 </div>
106 </div>
106 </div>
107
107
108 <div class="field">
108 <div class="field">
109 <div class="label">
109 <div class="label">
110 <label>${_('Clone url')}:</label>
110 <label>${_('Clone url')}:</label>
111 </div>
111 </div>
112 <div class="input-short">
112 <div class="input-short">
113 <input type="text" id="clone_url" readonly="readonly" value="hg clone ${c.clone_repo_url}" size="70"/>
113 <input type="text" id="clone_url" readonly="readonly" value="hg clone ${c.clone_repo_url}" size="70"/>
114 </div>
114 </div>
115 </div>
115 </div>
116
116
117 <div class="field">
117 <div class="field">
118 <div class="label">
118 <div class="label">
119 <label>${_('Trending source files')}:</label>
119 <label>${_('Trending source files')}:</label>
120 </div>
120 </div>
121 <div class="input-short">
121 <div class="input-short">
122 <div id="lang_stats">
122 <div id="lang_stats">
123
123
124 </div>
124 </div>
125 <script type="text/javascript">
125 <script type="text/javascript">
126 YUE.onDOMReady(function(e){
126 YUE.onDOMReady(function(e){
127 id = 'clone_url';
127 id = 'clone_url';
128 YUE.on(id,'click',function(e){
128 YUE.on(id,'click',function(e){
129 YUD.get('clone_url').select();
129 YUD.get('clone_url').select();
130 })
130 })
131 })
131 })
132 var data = ${c.trending_languages|n};
132 var data = ${c.trending_languages|n};
133 var total = 0;
133 var total = 0;
134 var no_data = true;
134 var no_data = true;
135 for (k in data){
135 for (k in data){
136 total += data[k];
136 total += data[k];
137 no_data = false;
137 no_data = false;
138 }
138 }
139 var tbl = document.createElement('table');
139 var tbl = document.createElement('table');
140 tbl.setAttribute('class','trending_language_tbl');
140 tbl.setAttribute('class','trending_language_tbl');
141 var cnt =0;
141 var cnt =0;
142 for (k in data){
142 for (k in data){
143 cnt+=1;
143 cnt+=1;
144 var hide = cnt>2;
144 var hide = cnt>2;
145 var tr = document.createElement('tr');
145 var tr = document.createElement('tr');
146 if (hide){
146 if (hide){
147 tr.setAttribute('style','display:none');
147 tr.setAttribute('style','display:none');
148 tr.setAttribute('class','stats_hidden');
148 tr.setAttribute('class','stats_hidden');
149 }
149 }
150 var percentage = Math.round((data[k]/total*100),2);
150 var percentage = Math.round((data[k]/total*100),2);
151 var value = data[k];
151 var value = data[k];
152 var td1 = document.createElement('td');
152 var td1 = document.createElement('td');
153 td1.width=150;
153 td1.width=150;
154 var trending_language_label = document.createElement('div');
154 var trending_language_label = document.createElement('div');
155 trending_language_label.innerHTML = k;
155 trending_language_label.innerHTML = k;
156 td1.appendChild(trending_language_label);
156 td1.appendChild(trending_language_label);
157
157
158 var td2 = document.createElement('td');
158 var td2 = document.createElement('td');
159 td2.setAttribute('style','padding-right:14px !important');
159 td2.setAttribute('style','padding-right:14px !important');
160 var trending_language = document.createElement('div');
160 var trending_language = document.createElement('div');
161 var nr_files = value+" ${_('files')}";
161 var nr_files = value+" ${_('files')}";
162
162
163 trending_language.title = k+" "+nr_files;
163 trending_language.title = k+" "+nr_files;
164
164
165 if (percentage>20){
165 if (percentage>20){
166 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"% "+nr_files+ "</b>";
166 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"% "+nr_files+ "</b>";
167 }
167 }
168 else{
168 else{
169 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"%</b>";
169 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"%</b>";
170 }
170 }
171
171
172 trending_language.setAttribute("class", 'trending_language top-right-rounded-corner bottom-right-rounded-corner');
172 trending_language.setAttribute("class", 'trending_language top-right-rounded-corner bottom-right-rounded-corner');
173 trending_language.style.width=percentage+"%";
173 trending_language.style.width=percentage+"%";
174 td2.appendChild(trending_language);
174 td2.appendChild(trending_language);
175
175
176 tr.appendChild(td1);
176 tr.appendChild(td1);
177 tr.appendChild(td2);
177 tr.appendChild(td2);
178 tbl.appendChild(tr);
178 tbl.appendChild(tr);
179 if(cnt == 2){
179 if(cnt == 2){
180 var show_more = document.createElement('tr');
180 var show_more = document.createElement('tr');
181 var td=document.createElement('td');
181 var td=document.createElement('td');
182 lnk = document.createElement('a');
182 lnk = document.createElement('a');
183 lnk.href='#';
183 lnk.href='#';
184 lnk.innerHTML = "${_("show more")}";
184 lnk.innerHTML = "${_("show more")}";
185 lnk.id='code_stats_show_more';
185 lnk.id='code_stats_show_more';
186 td.appendChild(lnk);
186 td.appendChild(lnk);
187 show_more.appendChild(td);
187 show_more.appendChild(td);
188 show_more.appendChild(document.createElement('td'));
188 show_more.appendChild(document.createElement('td'));
189 tbl.appendChild(show_more);
189 tbl.appendChild(show_more);
190 }
190 }
191
191
192 }
192 }
193 if(no_data){
193 if(no_data){
194 var tr = document.createElement('tr');
194 var tr = document.createElement('tr');
195 var td1 = document.createElement('td');
195 var td1 = document.createElement('td');
196 td1.innerHTML = "${c.no_data_msg}";
196 td1.innerHTML = "${c.no_data_msg}";
197 tr.appendChild(td1);
197 tr.appendChild(td1);
198 tbl.appendChild(tr);
198 tbl.appendChild(tr);
199 }
199 }
200 YUD.get('lang_stats').appendChild(tbl);
200 YUD.get('lang_stats').appendChild(tbl);
201 YUE.on('code_stats_show_more','click',function(){
201 YUE.on('code_stats_show_more','click',function(){
202 l = YUD.getElementsByClassName('stats_hidden')
202 l = YUD.getElementsByClassName('stats_hidden')
203 for (e in l){
203 for (e in l){
204 YUD.setStyle(l[e],'display','');
204 YUD.setStyle(l[e],'display','');
205 };
205 };
206 YUD.setStyle(YUD.get('code_stats_show_more'),
206 YUD.setStyle(YUD.get('code_stats_show_more'),
207 'display','none');
207 'display','none');
208 })
208 })
209
209
210 </script>
210 </script>
211
211
212 </div>
212 </div>
213 </div>
213 </div>
214
214
215 <div class="field">
215 <div class="field">
216 <div class="label">
216 <div class="label">
217 <label>${_('Download')}:</label>
217 <label>${_('Download')}:</label>
218 </div>
218 </div>
219 <div class="input-short">
219 <div class="input-short">
220 %for cnt,archive in enumerate(c.repo_info._get_archives()):
220 %for cnt,archive in enumerate(c.repo_info._get_archives()):
221 %if cnt >=1:
221 %if cnt >=1:
222 |
222 |
223 %endif
223 %endif
224 ${h.link_to(c.repo_info.name+'.'+archive['type'],
224 ${h.link_to(c.repo_info.name+'.'+archive['type'],
225 h.url('files_archive_home',repo_name=c.repo_info.name,
225 h.url('files_archive_home',repo_name=c.repo_info.name,
226 revision='tip',fileformat=archive['extension']),class_="archive_icon")}
226 revision='tip',fileformat=archive['extension']),class_="archive_icon")}
227 %endfor
227 %endfor
228 </div>
228 </div>
229 </div>
229 </div>
230
230
231 <div class="field">
231 <div class="field">
232 <div class="label">
232 <div class="label">
233 <label>${_('Feeds')}:</label>
233 <label>${_('Feeds')}:</label>
234 </div>
234 </div>
235 <div class="input-short">
235 <div class="input-short">
236 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.repo_info.name),class_='rss_icon')}
236 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.repo_info.name),class_='rss_icon')}
237 ${h.link_to(_('Atom'),h.url('atom_feed_home',repo_name=c.repo_info.name),class_='atom_icon')}
237 ${h.link_to(_('Atom'),h.url('atom_feed_home',repo_name=c.repo_info.name),class_='atom_icon')}
238 </div>
238 </div>
239 </div>
239 </div>
240 </div>
240 </div>
241 </div>
241 </div>
242 </div>
242 </div>
243
243
244 <div class="box box-right" style="min-height:455px">
244 <div class="box box-right" style="min-height:455px">
245 <!-- box / title -->
245 <!-- box / title -->
246 <div class="title">
246 <div class="title">
247 <h5>${_('Commit activity by day / author')}</h5>
247 <h5>${_('Commit activity by day / author')}</h5>
248 </div>
248 </div>
249
249
250 <div class="table">
250 <div class="table">
251
251
252 %if c.no_data:
252 %if c.no_data:
253 <div style="padding:0 10px 10px 15px;font-size: 1.2em;">${c.no_data_msg}</div>
253 <div style="padding:0 10px 10px 15px;font-size: 1.2em;">${c.no_data_msg}</div>
254 %endif:
254 %endif:
255 <div id="commit_history" style="width:460px;height:300px;float:left"></div>
255 <div id="commit_history" style="width:460px;height:300px;float:left"></div>
256 <div style="clear: both;height: 10px"></div>
256 <div style="clear: both;height: 10px"></div>
257 <div id="overview" style="width:460px;height:100px;float:left"></div>
257 <div id="overview" style="width:460px;height:100px;float:left"></div>
258
258
259 <div id="legend_data" style="clear:both;margin-top:10px;">
259 <div id="legend_data" style="clear:both;margin-top:10px;">
260 <div id="legend_container"></div>
260 <div id="legend_container"></div>
261 <div id="legend_choices">
261 <div id="legend_choices">
262 <table id="legend_choices_tables" style="font-size:smaller;color:#545454"></table>
262 <table id="legend_choices_tables" style="font-size:smaller;color:#545454"></table>
263 </div>
263 </div>
264 </div>
264 </div>
265 <script type="text/javascript">
265 <script type="text/javascript">
266 /**
266 /**
267 * Plots summary graph
267 * Plots summary graph
268 *
268 *
269 * @class SummaryPlot
269 * @class SummaryPlot
270 * @param {from} initial from for detailed graph
270 * @param {from} initial from for detailed graph
271 * @param {to} initial to for detailed graph
271 * @param {to} initial to for detailed graph
272 * @param {dataset}
272 * @param {dataset}
273 * @param {overview_dataset}
273 * @param {overview_dataset}
274 */
274 */
275 function SummaryPlot(from,to,dataset,overview_dataset) {
275 function SummaryPlot(from,to,dataset,overview_dataset) {
276 var initial_ranges = {
276 var initial_ranges = {
277 "xaxis":{
277 "xaxis":{
278 "from":from,
278 "from":from,
279 "to":to,
279 "to":to,
280 },
280 },
281 };
281 };
282 var dataset = dataset;
282 var dataset = dataset;
283 var overview_dataset = [overview_dataset];
283 var overview_dataset = [overview_dataset];
284 var choiceContainer = YUD.get("legend_choices");
284 var choiceContainer = YUD.get("legend_choices");
285 var choiceContainerTable = YUD.get("legend_choices_tables");
285 var choiceContainerTable = YUD.get("legend_choices_tables");
286 var plotContainer = YUD.get('commit_history');
286 var plotContainer = YUD.get('commit_history');
287 var overviewContainer = YUD.get('overview');
287 var overviewContainer = YUD.get('overview');
288
288
289 var plot_options = {
289 var plot_options = {
290 bars: {show:true,align:'center',lineWidth:4},
290 bars: {show:true,align:'center',lineWidth:4},
291 legend: {show:true, container:"legend_container"},
291 legend: {show:true, container:"legend_container"},
292 points: {show:true,radius:0,fill:false},
292 points: {show:true,radius:0,fill:false},
293 yaxis: {tickDecimals:0,},
293 yaxis: {tickDecimals:0,},
294 xaxis: {
294 xaxis: {
295 mode: "time",
295 mode: "time",
296 timeformat: "%d/%m",
296 timeformat: "%d/%m",
297 min:from,
297 min:from,
298 max:to,
298 max:to,
299 },
299 },
300 grid: {
300 grid: {
301 hoverable: true,
301 hoverable: true,
302 clickable: true,
302 clickable: true,
303 autoHighlight:true,
303 autoHighlight:true,
304 color: "#999"
304 color: "#999"
305 },
305 },
306 //selection: {mode: "x"}
306 //selection: {mode: "x"}
307 };
307 };
308 var overview_options = {
308 var overview_options = {
309 legend:{show:false},
309 legend:{show:false},
310 bars: {show:true,barWidth: 2,},
310 bars: {show:true,barWidth: 2,},
311 shadowSize: 0,
311 shadowSize: 0,
312 xaxis: {mode: "time", timeformat: "%d/%m/%y",},
312 xaxis: {mode: "time", timeformat: "%d/%m/%y",},
313 yaxis: {ticks: 3, min: 0,tickDecimals:0,},
313 yaxis: {ticks: 3, min: 0,tickDecimals:0,},
314 grid: {color: "#999",},
314 grid: {color: "#999",},
315 selection: {mode: "x"}
315 selection: {mode: "x"}
316 };
316 };
317
317
318 /**
318 /**
319 *get dummy data needed in few places
319 *get dummy data needed in few places
320 */
320 */
321 function getDummyData(label){
321 function getDummyData(label){
322 return {"label":label,
322 return {"label":label,
323 "data":[{"time":0,
323 "data":[{"time":0,
324 "commits":0,
324 "commits":0,
325 "added":0,
325 "added":0,
326 "changed":0,
326 "changed":0,
327 "removed":0,
327 "removed":0,
328 }],
328 }],
329 "schema":["commits"],
329 "schema":["commits"],
330 "color":'#ffffff',
330 "color":'#ffffff',
331 }
331 }
332 }
332 }
333
333
334 /**
334 /**
335 * generate checkboxes accordindly to data
335 * generate checkboxes accordindly to data
336 * @param keys
336 * @param keys
337 * @returns
337 * @returns
338 */
338 */
339 function generateCheckboxes(data) {
339 function generateCheckboxes(data) {
340 //append checkboxes
340 //append checkboxes
341 var i = 0;
341 var i = 0;
342 choiceContainerTable.innerHTML = '';
342 choiceContainerTable.innerHTML = '';
343 for(var pos in data) {
343 for(var pos in data) {
344
344
345 data[pos].color = i;
345 data[pos].color = i;
346 i++;
346 i++;
347 if(data[pos].label != ''){
347 if(data[pos].label != ''){
348 choiceContainerTable.innerHTML += '<tr><td>'+
348 choiceContainerTable.innerHTML += '<tr><td>'+
349 '<input type="checkbox" name="' + data[pos].label +'" checked="checked" />'
349 '<input type="checkbox" name="' + data[pos].label +'" checked="checked" />'
350 +data[pos].label+
350 +data[pos].label+
351 '</td></tr>';
351 '</td></tr>';
352 }
352 }
353 }
353 }
354 }
354 }
355
355
356 /**
356 /**
357 * ToolTip show
357 * ToolTip show
358 */
358 */
359 function showTooltip(x, y, contents) {
359 function showTooltip(x, y, contents) {
360 var div=document.getElementById('tooltip');
360 var div=document.getElementById('tooltip');
361 if(!div) {
361 if(!div) {
362 div = document.createElement('div');
362 div = document.createElement('div');
363 div.id="tooltip";
363 div.id="tooltip";
364 div.style.position="absolute";
364 div.style.position="absolute";
365 div.style.border='1px solid #fdd';
365 div.style.border='1px solid #fdd';
366 div.style.padding='2px';
366 div.style.padding='2px';
367 div.style.backgroundColor='#fee';
367 div.style.backgroundColor='#fee';
368 document.body.appendChild(div);
368 document.body.appendChild(div);
369 }
369 }
370 YUD.setStyle(div, 'opacity', 0);
370 YUD.setStyle(div, 'opacity', 0);
371 div.innerHTML = contents;
371 div.innerHTML = contents;
372 div.style.top=(y + 5) + "px";
372 div.style.top=(y + 5) + "px";
373 div.style.left=(x + 5) + "px";
373 div.style.left=(x + 5) + "px";
374
374
375 var anim = new YAHOO.util.Anim(div, {opacity: {to: 0.8}}, 0.2);
375 var anim = new YAHOO.util.Anim(div, {opacity: {to: 0.8}}, 0.2);
376 anim.animate();
376 anim.animate();
377 }
377 }
378
378
379 /**
379 /**
380 * This function will detect if selected period has some changesets
380 * This function will detect if selected period has some changesets
381 for this user if it does this data is then pushed for displaying
381 for this user if it does this data is then pushed for displaying
382 Additionally it will only display users that are selected by the checkbox
382 Additionally it will only display users that are selected by the checkbox
383 */
383 */
384 function getDataAccordingToRanges(ranges) {
384 function getDataAccordingToRanges(ranges) {
385
385
386 var data = [];
386 var data = [];
387 var keys = [];
387 var keys = [];
388 for(var key in dataset){
388 for(var key in dataset){
389 var push = false;
389 var push = false;
390
390
391 //method1 slow !!
391 //method1 slow !!
392 //*
392 //*
393 for(var ds in dataset[key].data){
393 for(var ds in dataset[key].data){
394 commit_data = dataset[key].data[ds];
394 commit_data = dataset[key].data[ds];
395 if (commit_data.time >= ranges.xaxis.from && commit_data.time <= ranges.xaxis.to){
395 if (commit_data.time >= ranges.xaxis.from && commit_data.time <= ranges.xaxis.to){
396 push = true;
396 push = true;
397 break;
397 break;
398 }
398 }
399 }
399 }
400 //*/
400 //*/
401
401
402 /*//method2 sorted commit data !!!
402 /*//method2 sorted commit data !!!
403
403
404 var first_commit = dataset[key].data[0].time;
404 var first_commit = dataset[key].data[0].time;
405 var last_commit = dataset[key].data[dataset[key].data.length-1].time;
405 var last_commit = dataset[key].data[dataset[key].data.length-1].time;
406
406
407 if (first_commit >= ranges.xaxis.from && last_commit <= ranges.xaxis.to){
407 if (first_commit >= ranges.xaxis.from && last_commit <= ranges.xaxis.to){
408 push = true;
408 push = true;
409 }
409 }
410 //*/
410 //*/
411
411
412 if(push){
412 if(push){
413 data.push(dataset[key]);
413 data.push(dataset[key]);
414 }
414 }
415 }
415 }
416 if(data.length >= 1){
416 if(data.length >= 1){
417 return data;
417 return data;
418 }
418 }
419 else{
419 else{
420 //just return dummy data for graph to plot itself
420 //just return dummy data for graph to plot itself
421 return [getDummyData('')];
421 return [getDummyData('')];
422 }
422 }
423
423
424 }
424 }
425
425
426 /**
426 /**
427 * redraw using new checkbox data
427 * redraw using new checkbox data
428 */
428 */
429 function plotchoiced(e,args){
429 function plotchoiced(e,args){
430 var cur_data = args[0];
430 var cur_data = args[0];
431 var cur_ranges = args[1];
431 var cur_ranges = args[1];
432
432
433 var new_data = [];
433 var new_data = [];
434 var inputs = choiceContainer.getElementsByTagName("input");
434 var inputs = choiceContainer.getElementsByTagName("input");
435
435
436 //show only checked labels
436 //show only checked labels
437 for(var i=0; i<inputs.length; i++) {
437 for(var i=0; i<inputs.length; i++) {
438 var checkbox_key = inputs[i].name;
438 var checkbox_key = inputs[i].name;
439
439
440 if(inputs[i].checked){
440 if(inputs[i].checked){
441 for(var d in cur_data){
441 for(var d in cur_data){
442 if(cur_data[d].label == checkbox_key){
442 if(cur_data[d].label == checkbox_key){
443 new_data.push(cur_data[d]);
443 new_data.push(cur_data[d]);
444 }
444 }
445 }
445 }
446 }
446 }
447 else{
447 else{
448 //push dummy data to not hide the label
448 //push dummy data to not hide the label
449 new_data.push(getDummyData(checkbox_key));
449 new_data.push(getDummyData(checkbox_key));
450 }
450 }
451 }
451 }
452
452
453 var new_options = YAHOO.lang.merge(plot_options, {
453 var new_options = YAHOO.lang.merge(plot_options, {
454 xaxis: {
454 xaxis: {
455 min: cur_ranges.xaxis.from,
455 min: cur_ranges.xaxis.from,
456 max: cur_ranges.xaxis.to,
456 max: cur_ranges.xaxis.to,
457 mode:"time",
457 mode:"time",
458 timeformat: "%d/%m",
458 timeformat: "%d/%m",
459 },
459 },
460 });
460 });
461 if (!new_data){
461 if (!new_data){
462 new_data = [[0,1]];
462 new_data = [[0,1]];
463 }
463 }
464 // do the zooming
464 // do the zooming
465 plot = YAHOO.widget.Flot(plotContainer, new_data, new_options);
465 plot = YAHOO.widget.Flot(plotContainer, new_data, new_options);
466
466
467 plot.subscribe("plotselected", plotselected);
467 plot.subscribe("plotselected", plotselected);
468
468
469 //resubscribe plothover
469 //resubscribe plothover
470 plot.subscribe("plothover", plothover);
470 plot.subscribe("plothover", plothover);
471
471
472 // don't fire event on the overview to prevent eternal loop
472 // don't fire event on the overview to prevent eternal loop
473 overview.setSelection(cur_ranges, true);
473 overview.setSelection(cur_ranges, true);
474
474
475 }
475 }
476
476
477 /**
477 /**
478 * plot only selected items from overview
478 * plot only selected items from overview
479 * @param ranges
479 * @param ranges
480 * @returns
480 * @returns
481 */
481 */
482 function plotselected(ranges,cur_data) {
482 function plotselected(ranges,cur_data) {
483 //updates the data for new plot
483 //updates the data for new plot
484 data = getDataAccordingToRanges(ranges);
484 data = getDataAccordingToRanges(ranges);
485 generateCheckboxes(data);
485 generateCheckboxes(data);
486
486
487 var new_options = YAHOO.lang.merge(plot_options, {
487 var new_options = YAHOO.lang.merge(plot_options, {
488 xaxis: {
488 xaxis: {
489 min: ranges.xaxis.from,
489 min: ranges.xaxis.from,
490 max: ranges.xaxis.to,
490 max: ranges.xaxis.to,
491 mode:"time",
491 mode:"time",
492 timeformat: "%d/%m",
492 timeformat: "%d/%m",
493 },
493 },
494 yaxis: {
494 yaxis: {
495 min: ranges.yaxis.from,
495 min: ranges.yaxis.from,
496 max: ranges.yaxis.to,
496 max: ranges.yaxis.to,
497 },
497 },
498
498
499 });
499 });
500 // do the zooming
500 // do the zooming
501 plot = YAHOO.widget.Flot(plotContainer, data, new_options);
501 plot = YAHOO.widget.Flot(plotContainer, data, new_options);
502
502
503 plot.subscribe("plotselected", plotselected);
503 plot.subscribe("plotselected", plotselected);
504
504
505 //resubscribe plothover
505 //resubscribe plothover
506 plot.subscribe("plothover", plothover);
506 plot.subscribe("plothover", plothover);
507
507
508 // don't fire event on the overview to prevent eternal loop
508 // don't fire event on the overview to prevent eternal loop
509 overview.setSelection(ranges, true);
509 overview.setSelection(ranges, true);
510
510
511 //resubscribe choiced
511 //resubscribe choiced
512 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, ranges]);
512 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, ranges]);
513 }
513 }
514
514
515 var previousPoint = null;
515 var previousPoint = null;
516
516
517 function plothover(o) {
517 function plothover(o) {
518 var pos = o.pos;
518 var pos = o.pos;
519 var item = o.item;
519 var item = o.item;
520
520
521 //YUD.get("x").innerHTML = pos.x.toFixed(2);
521 //YUD.get("x").innerHTML = pos.x.toFixed(2);
522 //YUD.get("y").innerHTML = pos.y.toFixed(2);
522 //YUD.get("y").innerHTML = pos.y.toFixed(2);
523 if (item) {
523 if (item) {
524 if (previousPoint != item.datapoint) {
524 if (previousPoint != item.datapoint) {
525 previousPoint = item.datapoint;
525 previousPoint = item.datapoint;
526
526
527 var tooltip = YUD.get("tooltip");
527 var tooltip = YUD.get("tooltip");
528 if(tooltip) {
528 if(tooltip) {
529 tooltip.parentNode.removeChild(tooltip);
529 tooltip.parentNode.removeChild(tooltip);
530 }
530 }
531 var x = item.datapoint.x.toFixed(2);
531 var x = item.datapoint.x.toFixed(2);
532 var y = item.datapoint.y.toFixed(2);
532 var y = item.datapoint.y.toFixed(2);
533
533
534 if (!item.series.label){
534 if (!item.series.label){
535 item.series.label = 'commits';
535 item.series.label = 'commits';
536 }
536 }
537 var d = new Date(x*1000);
537 var d = new Date(x*1000);
538 var fd = d.toDateString()
538 var fd = d.toDateString()
539 var nr_commits = parseInt(y);
539 var nr_commits = parseInt(y);
540
540
541 var cur_data = dataset[item.series.label].data[item.dataIndex];
541 var cur_data = dataset[item.series.label].data[item.dataIndex];
542 var added = cur_data.added;
542 var added = cur_data.added;
543 var changed = cur_data.changed;
543 var changed = cur_data.changed;
544 var removed = cur_data.removed;
544 var removed = cur_data.removed;
545
545
546 var nr_commits_suffix = " ${_('commits')} ";
546 var nr_commits_suffix = " ${_('commits')} ";
547 var added_suffix = " ${_('files added')} ";
547 var added_suffix = " ${_('files added')} ";
548 var changed_suffix = " ${_('files changed')} ";
548 var changed_suffix = " ${_('files changed')} ";
549 var removed_suffix = " ${_('files removed')} ";
549 var removed_suffix = " ${_('files removed')} ";
550
550
551
551
552 if(nr_commits == 1){nr_commits_suffix = " ${_('commit')} ";}
552 if(nr_commits == 1){nr_commits_suffix = " ${_('commit')} ";}
553 if(added==1){added_suffix=" ${_('file added')} ";}
553 if(added==1){added_suffix=" ${_('file added')} ";}
554 if(changed==1){changed_suffix=" ${_('file changed')} ";}
554 if(changed==1){changed_suffix=" ${_('file changed')} ";}
555 if(removed==1){removed_suffix=" ${_('file removed')} ";}
555 if(removed==1){removed_suffix=" ${_('file removed')} ";}
556
556
557 showTooltip(item.pageX, item.pageY, item.series.label + " on " + fd
557 showTooltip(item.pageX, item.pageY, item.series.label + " on " + fd
558 +'<br/>'+
558 +'<br/>'+
559 nr_commits + nr_commits_suffix+'<br/>'+
559 nr_commits + nr_commits_suffix+'<br/>'+
560 added + added_suffix +'<br/>'+
560 added + added_suffix +'<br/>'+
561 changed + changed_suffix + '<br/>'+
561 changed + changed_suffix + '<br/>'+
562 removed + removed_suffix + '<br/>');
562 removed + removed_suffix + '<br/>');
563 }
563 }
564 }
564 }
565 else {
565 else {
566 var tooltip = YUD.get("tooltip");
566 var tooltip = YUD.get("tooltip");
567
567
568 if(tooltip) {
568 if(tooltip) {
569 tooltip.parentNode.removeChild(tooltip);
569 tooltip.parentNode.removeChild(tooltip);
570 }
570 }
571 previousPoint = null;
571 previousPoint = null;
572 }
572 }
573 }
573 }
574
574
575 /**
575 /**
576 * MAIN EXECUTION
576 * MAIN EXECUTION
577 */
577 */
578
578
579 var data = getDataAccordingToRanges(initial_ranges);
579 var data = getDataAccordingToRanges(initial_ranges);
580 generateCheckboxes(data);
580 generateCheckboxes(data);
581
581
582 //main plot
582 //main plot
583 var plot = YAHOO.widget.Flot(plotContainer,data,plot_options);
583 var plot = YAHOO.widget.Flot(plotContainer,data,plot_options);
584
584
585 //overview
585 //overview
586 var overview = YAHOO.widget.Flot(overviewContainer, overview_dataset, overview_options);
586 var overview = YAHOO.widget.Flot(overviewContainer, overview_dataset, overview_options);
587
587
588 //show initial selection on overview
588 //show initial selection on overview
589 overview.setSelection(initial_ranges);
589 overview.setSelection(initial_ranges);
590
590
591 plot.subscribe("plotselected", plotselected);
591 plot.subscribe("plotselected", plotselected);
592
592
593 overview.subscribe("plotselected", function (ranges) {
593 overview.subscribe("plotselected", function (ranges) {
594 plot.setSelection(ranges);
594 plot.setSelection(ranges);
595 });
595 });
596
596
597 plot.subscribe("plothover", plothover);
597 plot.subscribe("plothover", plothover);
598
598
599 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, initial_ranges]);
599 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, initial_ranges]);
600 }
600 }
601 SummaryPlot(${c.ts_min},${c.ts_max},${c.commit_data|n},${c.overview_data|n});
601 SummaryPlot(${c.ts_min},${c.ts_max},${c.commit_data|n},${c.overview_data|n});
602 </script>
602 </script>
603
603
604 </div>
604 </div>
605 </div>
605 </div>
606
606
607 <div class="box">
607 <div class="box">
608 <div class="title">
608 <div class="title">
609 <div class="breadcrumbs">${h.link_to(_('Shortlog'),h.url('shortlog_home',repo_name=c.repo_name))}</div>
609 <div class="breadcrumbs">${h.link_to(_('Shortlog'),h.url('shortlog_home',repo_name=c.repo_name))}</div>
610 </div>
610 </div>
611 <div class="table">
611 <div class="table">
612 <div id="shortlog_data">
612 <div id="shortlog_data">
613 <%include file='../shortlog/shortlog_data.html'/>
613 <%include file='../shortlog/shortlog_data.html'/>
614 </div>
614 </div>
615 ##%if c.repo_changesets:
615 ##%if c.repo_changesets:
616 ## ${h.link_to(_('show more'),h.url('changelog_home',repo_name=c.repo_name))}
616 ## ${h.link_to(_('show more'),h.url('changelog_home',repo_name=c.repo_name))}
617 ##%endif
617 ##%endif
618 </div>
618 </div>
619 </div>
619 </div>
620 <div class="box">
620 <div class="box">
621 <div class="title">
621 <div class="title">
622 <div class="breadcrumbs">${h.link_to(_('Last ten tags'),h.url('tags_home',repo_name=c.repo_name))}</div>
622 <div class="breadcrumbs">${h.link_to(_('Tags'),h.url('tags_home',repo_name=c.repo_name))}</div>
623 </div>
623 </div>
624 <div class="table">
624 <div class="table">
625 <%include file='../tags/tags_data.html'/>
625 <%include file='../tags/tags_data.html'/>
626 %if c.repo_changesets:
626 %if c.repo_changesets:
627 ${h.link_to(_('show more'),h.url('tags_home',repo_name=c.repo_name))}
627 ${h.link_to(_('show more'),h.url('tags_home',repo_name=c.repo_name))}
628 %endif
628 %endif
629 </div>
629 </div>
630 </div>
630 </div>
631 <div class="box">
631 <div class="box">
632 <div class="title">
632 <div class="title">
633 <div class="breadcrumbs">${h.link_to(_('Last ten branches'),h.url('branches_home',repo_name=c.repo_name))}</div>
633 <div class="breadcrumbs">${h.link_to(_('Branches'),h.url('branches_home',repo_name=c.repo_name))}</div>
634 </div>
634 </div>
635 <div class="table">
635 <div class="table">
636 <%include file='../branches/branches_data.html'/>
636 <%include file='../branches/branches_data.html'/>
637 %if c.repo_changesets:
637 %if c.repo_changesets:
638 ${h.link_to(_('show more'),h.url('branches_home',repo_name=c.repo_name))}
638 ${h.link_to(_('show more'),h.url('branches_home',repo_name=c.repo_name))}
639 %endif
639 %endif
640 </div>
640 </div>
641 </div>
641 </div>
642
642
643 </%def> No newline at end of file
643 </%def>
General Comments 0
You need to be logged in to leave comments. Login now