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