##// END OF EJS Templates
added branch/tag options to download links in summary
marcink -
r942:32318ec7 beta
parent child Browse files
Show More
@@ -1,273 +1,281 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.files
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Files controller for RhodeCode
7 7
8 8 :created_on: Apr 21, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2009-2011 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 tempfile
28 28 import logging
29 29 import rhodecode.lib.helpers as h
30 30
31 31 from mercurial import archival
32 32
33 33 from pylons import request, response, session, tmpl_context as c, url
34 34 from pylons.i18n.translation import _
35 35 from pylons.controllers.util import redirect
36 36
37 37 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
38 38 from rhodecode.lib.base import BaseController, render
39 39 from rhodecode.lib.utils import EmptyChangeset
40 40 from rhodecode.model.scm import ScmModel
41 41
42 42 from vcs.exceptions import RepositoryError, ChangesetError, \
43 43 ChangesetDoesNotExistError, EmptyRepositoryError
44 44 from vcs.nodes import FileNode
45 45 from vcs.utils import diffs as differ
46 46
47 47 log = logging.getLogger(__name__)
48 48
49 49 class FilesController(BaseController):
50 50
51 51 @LoginRequired()
52 52 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
53 53 'repository.admin')
54 54 def __before__(self):
55 55 super(FilesController, self).__before__()
56 56 c.cut_off_limit = self.cut_off_limit
57 57
58 58 def index(self, repo_name, revision, f_path):
59 59 hg_model = ScmModel()
60 60 c.repo = hg_model.get_repo(c.repo_name)
61 61
62 62 try:
63 63 #reditect to given revision from form
64 64 post_revision = request.POST.get('at_rev', None)
65 65 if post_revision:
66 66 post_revision = c.repo.get_changeset(post_revision).raw_id
67 67 redirect(url('files_home', repo_name=c.repo_name,
68 68 revision=post_revision, f_path=f_path))
69 69
70 70 c.branch = request.GET.get('branch', None)
71 71
72 72 c.f_path = f_path
73 73
74 74 c.changeset = c.repo.get_changeset(revision)
75 75 cur_rev = c.changeset.revision
76 76
77 77 #prev link
78 78 try:
79 79 prev_rev = c.repo.get_changeset(cur_rev).prev(c.branch).raw_id
80 80 c.url_prev = url('files_home', repo_name=c.repo_name,
81 81 revision=prev_rev, f_path=f_path)
82 82 if c.branch:
83 83 c.url_prev += '?branch=%s' % c.branch
84 84 except ChangesetDoesNotExistError:
85 85 c.url_prev = '#'
86 86
87 87 #next link
88 88 try:
89 89 next_rev = c.repo.get_changeset(cur_rev).next(c.branch).raw_id
90 90 c.url_next = url('files_home', repo_name=c.repo_name,
91 91 revision=next_rev, f_path=f_path)
92 92 if c.branch:
93 93 c.url_next += '?branch=%s' % c.branch
94 94 except ChangesetDoesNotExistError:
95 95 c.url_next = '#'
96 96
97 97 #files
98 98 try:
99 99 c.files_list = c.changeset.get_node(f_path)
100 100 c.file_history = self._get_history(c.repo, c.files_list, f_path)
101 101 except RepositoryError, e:
102 102 h.flash(str(e), category='warning')
103 103 redirect(h.url('files_home', repo_name=repo_name, revision=revision))
104 104
105 105 except EmptyRepositoryError, e:
106 106 h.flash(_('There are no files yet'), category='warning')
107 107 redirect(h.url('summary_home', repo_name=repo_name))
108 108
109 109 except RepositoryError, e:
110 110 h.flash(str(e), category='warning')
111 111 redirect(h.url('files_home', repo_name=repo_name, revision='tip'))
112 112
113 113
114 114
115 115 return render('files/files.html')
116 116
117 117 def rawfile(self, repo_name, revision, f_path):
118 118 hg_model = ScmModel()
119 119 c.repo = hg_model.get_repo(c.repo_name)
120 120 file_node = c.repo.get_changeset(revision).get_node(f_path)
121 121 response.content_type = file_node.mimetype
122 122 response.content_disposition = 'attachment; filename=%s' \
123 123 % f_path.split('/')[-1]
124 124 return file_node.content
125 125
126 126 def raw(self, repo_name, revision, f_path):
127 127 hg_model = ScmModel()
128 128 c.repo = hg_model.get_repo(c.repo_name)
129 129 file_node = c.repo.get_changeset(revision).get_node(f_path)
130 130 response.content_type = 'text/plain'
131 131
132 132 return file_node.content
133 133
134 134 def annotate(self, repo_name, revision, f_path):
135 135 hg_model = ScmModel()
136 136 c.repo = hg_model.get_repo(c.repo_name)
137 137
138 138 try:
139 139 c.cs = c.repo.get_changeset(revision)
140 140 c.file = c.cs.get_node(f_path)
141 141 except RepositoryError, e:
142 142 h.flash(str(e), category='warning')
143 143 redirect(h.url('files_home', repo_name=repo_name, revision=revision))
144 144
145 145 c.file_history = self._get_history(c.repo, c.file, f_path)
146 146
147 147 c.f_path = f_path
148 148
149 149 return render('files/files_annotate.html')
150 150
151 151 def archivefile(self, repo_name, fname):
152 info = fname.split('.')
153 revision, fileformat = info[0], '.' + '.'.join(info[1:])
154 152 archive_specs = {
155 153 '.tar.bz2': ('application/x-tar', 'tbz2'),
156 154 '.tar.gz': ('application/x-tar', 'tgz'),
157 155 '.zip': ('application/zip', 'zip'),
158 156 }
157
158 fileformat = None
159 revision = None
160
161 for ext in archive_specs.keys():
162 archive_spec = fname.split(ext)
163 if len(archive_spec) == 2:
164 fileformat = archive_spec[1] or ext
165 revision = archive_spec[0]
166
159 167 if not archive_specs.has_key(fileformat):
160 return _('Unknown archive type %s') % fileformat
168 return _('Unknown archive type')
161 169
162 170 repo = ScmModel().get_repo(repo_name)
163 171
164 172 try:
165 173 repo.get_changeset(revision)
166 174 except ChangesetDoesNotExistError:
167 175 return _('Unknown revision %s') % revision
168 176
169 177 archive = tempfile.TemporaryFile()
170 178 localrepo = repo.repo
171 179 fname = '%s-%s%s' % (repo_name, revision, fileformat)
172 180 archival.archive(localrepo, archive, revision, archive_specs[fileformat][1],
173 181 prefix='%s-%s' % (repo_name, revision))
174 182 response.content_type = archive_specs[fileformat][0]
175 183 response.content_disposition = 'attachment; filename=%s' % fname
176 184 archive.seek(0)
177 185
178 186 def read_in_chunks(file_object, chunk_size=1024 * 40):
179 187 """Lazy function (generator) to read a file piece by piece.
180 188 Default chunk size: 40k."""
181 189 while True:
182 190 data = file_object.read(chunk_size)
183 191 if not data:
184 192 break
185 193 yield data
186 194
187 195 return read_in_chunks(archive)
188 196
189 197 def diff(self, repo_name, f_path):
190 198 hg_model = ScmModel()
191 199 diff1 = request.GET.get('diff1')
192 200 diff2 = request.GET.get('diff2')
193 201 c.action = request.GET.get('diff')
194 202 c.no_changes = diff1 == diff2
195 203 c.f_path = f_path
196 204 c.repo = hg_model.get_repo(c.repo_name)
197 205
198 206 try:
199 207 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
200 208 c.changeset_1 = c.repo.get_changeset(diff1)
201 209 node1 = c.changeset_1.get_node(f_path)
202 210 else:
203 211 c.changeset_1 = EmptyChangeset()
204 212 node1 = FileNode('.', '', changeset=c.changeset_1)
205 213
206 214 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
207 215 c.changeset_2 = c.repo.get_changeset(diff2)
208 216 node2 = c.changeset_2.get_node(f_path)
209 217 else:
210 218 c.changeset_2 = EmptyChangeset()
211 219 node2 = FileNode('.', '', changeset=c.changeset_2)
212 220 except RepositoryError:
213 221 return redirect(url('files_home',
214 222 repo_name=c.repo_name, f_path=f_path))
215 223
216 224 f_udiff = differ.get_udiff(node1, node2)
217 225 diff = differ.DiffProcessor(f_udiff)
218 226
219 227 if c.action == 'download':
220 228 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
221 229 response.content_type = 'text/plain'
222 230 response.content_disposition = 'attachment; filename=%s' \
223 231 % diff_name
224 232 return diff.raw_diff()
225 233
226 234 elif c.action == 'raw':
227 235 response.content_type = 'text/plain'
228 236 return diff.raw_diff()
229 237
230 238 elif c.action == 'diff':
231 239 if node1.size > self.cut_off_limit or node2.size > self.cut_off_limit:
232 240 c.cur_diff = _('Diff is to big to display')
233 241 else:
234 242 c.cur_diff = diff.as_html()
235 243 else:
236 244 #default option
237 245 if node1.size > self.cut_off_limit or node2.size > self.cut_off_limit:
238 246 c.cur_diff = _('Diff is to big to display')
239 247 else:
240 248 c.cur_diff = diff.as_html()
241 249
242 250 if not c.cur_diff: c.no_changes = True
243 251 return render('files/file_diff.html')
244 252
245 253 def _get_history(self, repo, node, f_path):
246 254 from vcs.nodes import NodeKind
247 255 if not node.kind is NodeKind.FILE:
248 256 return []
249 257 changesets = node.history
250 258 hist_l = []
251 259
252 260 changesets_group = ([], _("Changesets"))
253 261 branches_group = ([], _("Branches"))
254 262 tags_group = ([], _("Tags"))
255 263
256 264 for chs in changesets:
257 265 n_desc = 'r%s:%s' % (chs.revision, chs.short_id)
258 266 changesets_group[0].append((chs.raw_id, n_desc,))
259 267
260 268 hist_l.append(changesets_group)
261 269
262 270 for name, chs in c.repository_branches.items():
263 271 #chs = chs.split(':')[-1]
264 272 branches_group[0].append((chs, name),)
265 273 hist_l.append(branches_group)
266 274
267 275 for name, chs in c.repository_tags.items():
268 276 #chs = chs.split(':')[-1]
269 277 tags_group[0].append((chs, name),)
270 278 hist_l.append(tags_group)
271 279
272 280 return hist_l
273 281
@@ -1,146 +1,168 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.summary
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Summary controller for Rhodecode
7 7
8 8 :created_on: Apr 18, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2009-2011 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
28 28 import calendar
29 29 import logging
30 30 from time import mktime
31 31 from datetime import datetime, timedelta, date
32 32
33 33 from vcs.exceptions import ChangesetError
34 34
35 35 from pylons import tmpl_context as c, request, url
36 36 from pylons.i18n.translation import _
37 37
38 38 from rhodecode.model.scm import ScmModel
39 39 from rhodecode.model.db import Statistics
40 40
41 41 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
42 42 from rhodecode.lib.base import BaseController, render
43 43 from rhodecode.lib.utils import OrderedDict, EmptyChangeset
44 44
45 45 from rhodecode.lib.celerylib import run_task
46 46 from rhodecode.lib.celerylib.tasks import get_commits_stats
47 47
48 48 from webhelpers.paginate import Page
49 49
50 50 try:
51 51 import json
52 52 except ImportError:
53 53 #python 2.5 compatibility
54 54 import simplejson as json
55 55 log = logging.getLogger(__name__)
56 56
57 57 class SummaryController(BaseController):
58 58
59 59 @LoginRequired()
60 60 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
61 61 'repository.admin')
62 62 def __before__(self):
63 63 super(SummaryController, self).__before__()
64 64
65 65 def index(self):
66 66 scm_model = ScmModel()
67 67 c.repo_info = scm_model.get_repo(c.repo_name)
68 68 c.following = scm_model.is_following_repo(c.repo_name,
69 69 c.rhodecode_user.user_id)
70 70 def url_generator(**kw):
71 71 return url('shortlog_home', repo_name=c.repo_name, **kw)
72 72
73 73 c.repo_changesets = Page(c.repo_info, page=1, items_per_page=10,
74 74 url=url_generator)
75 75
76 76 e = request.environ
77 77
78 78 if self.rhodecode_user.username == 'default':
79 79 #for default(anonymous) user we don't need to pass credentials
80 80 username = ''
81 81 password = ''
82 82 else:
83 83 username = str(c.rhodecode_user.username)
84 84 password = '@'
85 85
86 86 uri = u'%(protocol)s://%(user)s%(password)s%(host)s%(prefix)s/%(repo_name)s' % {
87 87 'protocol': e.get('wsgi.url_scheme'),
88 88 'user':username,
89 89 'password':password,
90 90 'host':e.get('HTTP_HOST'),
91 91 'prefix':e.get('SCRIPT_NAME'),
92 92 'repo_name':c.repo_name, }
93 93 c.clone_repo_url = uri
94 94 c.repo_tags = OrderedDict()
95 95 for name, hash in c.repo_info.tags.items()[:10]:
96 96 try:
97 97 c.repo_tags[name] = c.repo_info.get_changeset(hash)
98 98 except ChangesetError:
99 99 c.repo_tags[name] = EmptyChangeset(hash)
100 100
101 101 c.repo_branches = OrderedDict()
102 102 for name, hash in c.repo_info.branches.items()[:10]:
103 103 try:
104 104 c.repo_branches[name] = c.repo_info.get_changeset(hash)
105 105 except ChangesetError:
106 106 c.repo_branches[name] = EmptyChangeset(hash)
107 107
108 108 td = date.today() + timedelta(days=1)
109 109 td_1m = td - timedelta(days=calendar.mdays[td.month])
110 110 td_1y = td - timedelta(days=365)
111 111
112 112 ts_min_m = mktime(td_1m.timetuple())
113 113 ts_min_y = mktime(td_1y.timetuple())
114 114 ts_max_y = mktime(td.timetuple())
115 115
116 116 if c.repo_info.dbrepo.enable_statistics:
117 117 c.no_data_msg = _('No data loaded yet')
118 118 run_task(get_commits_stats, c.repo_info.name, ts_min_y, ts_max_y)
119 119 else:
120 120 c.no_data_msg = _('Statistics update are disabled for this repository')
121 121 c.ts_min = ts_min_m
122 122 c.ts_max = ts_max_y
123 123
124 124 stats = self.sa.query(Statistics)\
125 125 .filter(Statistics.repository == c.repo_info.dbrepo)\
126 126 .scalar()
127 127
128 128
129 129 if stats and stats.languages:
130 130 c.no_data = False is c.repo_info.dbrepo.enable_statistics
131 131 lang_stats = json.loads(stats.languages)
132 132 c.commit_data = stats.commit_activity
133 133 c.overview_data = stats.commit_activity_combined
134 134 c.trending_languages = json.dumps(OrderedDict(
135 135 sorted(lang_stats.items(), reverse=True,
136 136 key=lambda k: k[1])[:10]
137 137 )
138 138 )
139 139 else:
140 140 c.commit_data = json.dumps({})
141 141 c.overview_data = json.dumps([[ts_min_y, 0], [ts_max_y, 10] ])
142 142 c.trending_languages = json.dumps({})
143 143 c.no_data = True
144 144
145 c.download_options = self._get_download_links(c.repo_info)
146
145 147 return render('summary/summary.html')
146 148
149
150
151 def _get_download_links(self, repo):
152
153 download_l = []
154
155 branches_group = ([], _("Branches"))
156 tags_group = ([], _("Tags"))
157
158 for name, chs in c.repository_branches.items():
159 #chs = chs.split(':')[-1]
160 branches_group[0].append((chs, name),)
161 download_l.append(branches_group)
162
163 for name, chs in c.repository_tags.items():
164 #chs = chs.split(':')[-1]
165 tags_group[0].append((chs, name),)
166 download_l.append(tags_group)
167
168 return download_l
@@ -1,647 +1,652 b''
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 <div id="lang_stats">
123
124 </div>
122 <div id="lang_stats"></div>
123 </div>
124 </div>
125
126 <div class="field">
127 <div class="label">
128 <label>${_('Download')}:</label>
129 </div>
130 <div class="input-short">
131
132 ${h.select('download_options','tip',c.download_options)}
133
134 %for cnt,archive in enumerate(c.repo_info._get_archives()):
135 %if cnt >=1:
136 |
137 %endif
138 ${h.link_to(archive['type'],
139 h.url('files_archive_home',repo_name=c.repo_info.name,
140 fname='tip'+archive['extension']),class_="archive_icon")}
141 %endfor
142 </div>
143 </div>
144
145 <div class="field">
146 <div class="label">
147 <label>${_('Feeds')}:</label>
148 </div>
149 <div class="input-short">
150 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.repo_info.name),class_='rss_icon')}
151 ${h.link_to(_('Atom'),h.url('atom_feed_home',repo_name=c.repo_info.name),class_='atom_icon')}
152 </div>
153 </div>
154 </div>
155 </div>
125 156 <script type="text/javascript">
126 157 YUE.onDOMReady(function(e){
127 158 id = 'clone_url';
128 159 YUE.on(id,'click',function(e){
129 160 YUD.get('clone_url').select();
130 161 })
131 162 })
132 163 var data = ${c.trending_languages|n};
133 164 var total = 0;
134 165 var no_data = true;
135 166 for (k in data){
136 167 total += data[k];
137 168 no_data = false;
138 169 }
139 170 var tbl = document.createElement('table');
140 171 tbl.setAttribute('class','trending_language_tbl');
141 172 var cnt =0;
142 173 for (k in data){
143 174 cnt+=1;
144 175 var hide = cnt>2;
145 176 var tr = document.createElement('tr');
146 177 if (hide){
147 178 tr.setAttribute('style','display:none');
148 179 tr.setAttribute('class','stats_hidden');
149 180 }
150 181 var percentage = Math.round((data[k]/total*100),2);
151 182 var value = data[k];
152 183 var td1 = document.createElement('td');
153 184 td1.width=150;
154 185 var trending_language_label = document.createElement('div');
155 186 trending_language_label.innerHTML = k;
156 187 td1.appendChild(trending_language_label);
157 188
158 189 var td2 = document.createElement('td');
159 190 td2.setAttribute('style','padding-right:14px !important');
160 191 var trending_language = document.createElement('div');
161 192 var nr_files = value+" ${_('files')}";
162 193
163 194 trending_language.title = k+" "+nr_files;
164 195
165 196 if (percentage>20){
166 197 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"% "+nr_files+ "</b>";
167 198 }
168 199 else{
169 200 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"%</b>";
170 201 }
171 202
172 203 trending_language.setAttribute("class", 'trending_language top-right-rounded-corner bottom-right-rounded-corner');
173 204 trending_language.style.width=percentage+"%";
174 205 td2.appendChild(trending_language);
175 206
176 207 tr.appendChild(td1);
177 208 tr.appendChild(td2);
178 209 tbl.appendChild(tr);
179 210 if(cnt == 2){
180 211 var show_more = document.createElement('tr');
181 212 var td=document.createElement('td');
182 213 lnk = document.createElement('a');
183 214 lnk.href='#';
184 215 lnk.innerHTML = "${_("show more")}";
185 216 lnk.id='code_stats_show_more';
186 217 td.appendChild(lnk);
187 218 show_more.appendChild(td);
188 219 show_more.appendChild(document.createElement('td'));
189 220 tbl.appendChild(show_more);
190 221 }
191 222
192 223 }
193 224 if(no_data){
194 225 var tr = document.createElement('tr');
195 226 var td1 = document.createElement('td');
196 227 td1.innerHTML = "${c.no_data_msg}";
197 228 tr.appendChild(td1);
198 229 tbl.appendChild(tr);
199 230 }
200 231 YUD.get('lang_stats').appendChild(tbl);
201 232 YUE.on('code_stats_show_more','click',function(){
202 233 l = YUD.getElementsByClassName('stats_hidden')
203 234 for (e in l){
204 235 YUD.setStyle(l[e],'display','');
205 236 };
206 237 YUD.setStyle(YUD.get('code_stats_show_more'),
207 238 'display','none');
208 239 })
209
210 </script>
211
212 </div>
213 </div>
214
215 <div class="field">
216 <div class="label">
217 <label>${_('Download')}:</label>
218 </div>
219 <div class="input-short">
220 %for cnt,archive in enumerate(c.repo_info._get_archives()):
221 %if cnt >=1:
222 |
223 %endif
224 ${h.link_to(c.repo_info.name+'.'+archive['type'],
225 h.url('files_archive_home',repo_name=c.repo_info.name,
226 fname='tip'+archive['extension']),class_="archive_icon")}
227 %endfor
228 </div>
229 </div>
230
231 <div class="field">
232 <div class="label">
233 <label>${_('Feeds')}:</label>
234 </div>
235 <div class="input-short">
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')}
238 </div>
239 </div>
240 </div>
241 </div>
240
241
242 YUE.on('download_options','change',function(e){
243 var new_cs = e.target.options[e.target.selectedIndex].value;
244 })
245
246 </script>
242 247 </div>
243 248
244 249 <div class="box box-right" style="min-height:455px">
245 250 <!-- box / title -->
246 251 <div class="title">
247 252 <h5>${_('Commit activity by day / author')}</h5>
248 253 </div>
249 254
250 255 <div class="table">
251 256
252 257 %if c.no_data:
253 258 <div style="padding:0 10px 10px 15px;font-size: 1.2em;">${c.no_data_msg}
254 259 %if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
255 260 [${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name))}]
256 261 %endif
257 262 </div>
258 263 %endif:
259 264 <div id="commit_history" style="width:460px;height:300px;float:left"></div>
260 265 <div style="clear: both;height: 10px"></div>
261 266 <div id="overview" style="width:460px;height:100px;float:left"></div>
262 267
263 268 <div id="legend_data" style="clear:both;margin-top:10px;">
264 269 <div id="legend_container"></div>
265 270 <div id="legend_choices">
266 271 <table id="legend_choices_tables" style="font-size:smaller;color:#545454"></table>
267 272 </div>
268 273 </div>
269 274 <script type="text/javascript">
270 275 /**
271 276 * Plots summary graph
272 277 *
273 278 * @class SummaryPlot
274 279 * @param {from} initial from for detailed graph
275 280 * @param {to} initial to for detailed graph
276 281 * @param {dataset}
277 282 * @param {overview_dataset}
278 283 */
279 284 function SummaryPlot(from,to,dataset,overview_dataset) {
280 285 var initial_ranges = {
281 286 "xaxis":{
282 287 "from":from,
283 288 "to":to,
284 289 },
285 290 };
286 291 var dataset = dataset;
287 292 var overview_dataset = [overview_dataset];
288 293 var choiceContainer = YUD.get("legend_choices");
289 294 var choiceContainerTable = YUD.get("legend_choices_tables");
290 295 var plotContainer = YUD.get('commit_history');
291 296 var overviewContainer = YUD.get('overview');
292 297
293 298 var plot_options = {
294 299 bars: {show:true,align:'center',lineWidth:4},
295 300 legend: {show:true, container:"legend_container"},
296 301 points: {show:true,radius:0,fill:false},
297 302 yaxis: {tickDecimals:0,},
298 303 xaxis: {
299 304 mode: "time",
300 305 timeformat: "%d/%m",
301 306 min:from,
302 307 max:to,
303 308 },
304 309 grid: {
305 310 hoverable: true,
306 311 clickable: true,
307 312 autoHighlight:true,
308 313 color: "#999"
309 314 },
310 315 //selection: {mode: "x"}
311 316 };
312 317 var overview_options = {
313 318 legend:{show:false},
314 319 bars: {show:true,barWidth: 2,},
315 320 shadowSize: 0,
316 321 xaxis: {mode: "time", timeformat: "%d/%m/%y",},
317 322 yaxis: {ticks: 3, min: 0,tickDecimals:0,},
318 323 grid: {color: "#999",},
319 324 selection: {mode: "x"}
320 325 };
321 326
322 327 /**
323 328 *get dummy data needed in few places
324 329 */
325 330 function getDummyData(label){
326 331 return {"label":label,
327 332 "data":[{"time":0,
328 333 "commits":0,
329 334 "added":0,
330 335 "changed":0,
331 336 "removed":0,
332 337 }],
333 338 "schema":["commits"],
334 339 "color":'#ffffff',
335 340 }
336 341 }
337 342
338 343 /**
339 344 * generate checkboxes accordindly to data
340 345 * @param keys
341 346 * @returns
342 347 */
343 348 function generateCheckboxes(data) {
344 349 //append checkboxes
345 350 var i = 0;
346 351 choiceContainerTable.innerHTML = '';
347 352 for(var pos in data) {
348 353
349 354 data[pos].color = i;
350 355 i++;
351 356 if(data[pos].label != ''){
352 357 choiceContainerTable.innerHTML += '<tr><td>'+
353 358 '<input type="checkbox" name="' + data[pos].label +'" checked="checked" />'
354 359 +data[pos].label+
355 360 '</td></tr>';
356 361 }
357 362 }
358 363 }
359 364
360 365 /**
361 366 * ToolTip show
362 367 */
363 368 function showTooltip(x, y, contents) {
364 369 var div=document.getElementById('tooltip');
365 370 if(!div) {
366 371 div = document.createElement('div');
367 372 div.id="tooltip";
368 373 div.style.position="absolute";
369 374 div.style.border='1px solid #fdd';
370 375 div.style.padding='2px';
371 376 div.style.backgroundColor='#fee';
372 377 document.body.appendChild(div);
373 378 }
374 379 YUD.setStyle(div, 'opacity', 0);
375 380 div.innerHTML = contents;
376 381 div.style.top=(y + 5) + "px";
377 382 div.style.left=(x + 5) + "px";
378 383
379 384 var anim = new YAHOO.util.Anim(div, {opacity: {to: 0.8}}, 0.2);
380 385 anim.animate();
381 386 }
382 387
383 388 /**
384 389 * This function will detect if selected period has some changesets
385 390 for this user if it does this data is then pushed for displaying
386 391 Additionally it will only display users that are selected by the checkbox
387 392 */
388 393 function getDataAccordingToRanges(ranges) {
389 394
390 395 var data = [];
391 396 var keys = [];
392 397 for(var key in dataset){
393 398 var push = false;
394 399
395 400 //method1 slow !!
396 401 //*
397 402 for(var ds in dataset[key].data){
398 403 commit_data = dataset[key].data[ds];
399 404 if (commit_data.time >= ranges.xaxis.from && commit_data.time <= ranges.xaxis.to){
400 405 push = true;
401 406 break;
402 407 }
403 408 }
404 409 //*/
405 410
406 411 /*//method2 sorted commit data !!!
407 412
408 413 var first_commit = dataset[key].data[0].time;
409 414 var last_commit = dataset[key].data[dataset[key].data.length-1].time;
410 415
411 416 if (first_commit >= ranges.xaxis.from && last_commit <= ranges.xaxis.to){
412 417 push = true;
413 418 }
414 419 //*/
415 420
416 421 if(push){
417 422 data.push(dataset[key]);
418 423 }
419 424 }
420 425 if(data.length >= 1){
421 426 return data;
422 427 }
423 428 else{
424 429 //just return dummy data for graph to plot itself
425 430 return [getDummyData('')];
426 431 }
427 432
428 433 }
429 434
430 435 /**
431 436 * redraw using new checkbox data
432 437 */
433 438 function plotchoiced(e,args){
434 439 var cur_data = args[0];
435 440 var cur_ranges = args[1];
436 441
437 442 var new_data = [];
438 443 var inputs = choiceContainer.getElementsByTagName("input");
439 444
440 445 //show only checked labels
441 446 for(var i=0; i<inputs.length; i++) {
442 447 var checkbox_key = inputs[i].name;
443 448
444 449 if(inputs[i].checked){
445 450 for(var d in cur_data){
446 451 if(cur_data[d].label == checkbox_key){
447 452 new_data.push(cur_data[d]);
448 453 }
449 454 }
450 455 }
451 456 else{
452 457 //push dummy data to not hide the label
453 458 new_data.push(getDummyData(checkbox_key));
454 459 }
455 460 }
456 461
457 462 var new_options = YAHOO.lang.merge(plot_options, {
458 463 xaxis: {
459 464 min: cur_ranges.xaxis.from,
460 465 max: cur_ranges.xaxis.to,
461 466 mode:"time",
462 467 timeformat: "%d/%m",
463 468 },
464 469 });
465 470 if (!new_data){
466 471 new_data = [[0,1]];
467 472 }
468 473 // do the zooming
469 474 plot = YAHOO.widget.Flot(plotContainer, new_data, new_options);
470 475
471 476 plot.subscribe("plotselected", plotselected);
472 477
473 478 //resubscribe plothover
474 479 plot.subscribe("plothover", plothover);
475 480
476 481 // don't fire event on the overview to prevent eternal loop
477 482 overview.setSelection(cur_ranges, true);
478 483
479 484 }
480 485
481 486 /**
482 487 * plot only selected items from overview
483 488 * @param ranges
484 489 * @returns
485 490 */
486 491 function plotselected(ranges,cur_data) {
487 492 //updates the data for new plot
488 493 data = getDataAccordingToRanges(ranges);
489 494 generateCheckboxes(data);
490 495
491 496 var new_options = YAHOO.lang.merge(plot_options, {
492 497 xaxis: {
493 498 min: ranges.xaxis.from,
494 499 max: ranges.xaxis.to,
495 500 mode:"time",
496 501 timeformat: "%d/%m",
497 502 },
498 503 yaxis: {
499 504 min: ranges.yaxis.from,
500 505 max: ranges.yaxis.to,
501 506 },
502 507
503 508 });
504 509 // do the zooming
505 510 plot = YAHOO.widget.Flot(plotContainer, data, new_options);
506 511
507 512 plot.subscribe("plotselected", plotselected);
508 513
509 514 //resubscribe plothover
510 515 plot.subscribe("plothover", plothover);
511 516
512 517 // don't fire event on the overview to prevent eternal loop
513 518 overview.setSelection(ranges, true);
514 519
515 520 //resubscribe choiced
516 521 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, ranges]);
517 522 }
518 523
519 524 var previousPoint = null;
520 525
521 526 function plothover(o) {
522 527 var pos = o.pos;
523 528 var item = o.item;
524 529
525 530 //YUD.get("x").innerHTML = pos.x.toFixed(2);
526 531 //YUD.get("y").innerHTML = pos.y.toFixed(2);
527 532 if (item) {
528 533 if (previousPoint != item.datapoint) {
529 534 previousPoint = item.datapoint;
530 535
531 536 var tooltip = YUD.get("tooltip");
532 537 if(tooltip) {
533 538 tooltip.parentNode.removeChild(tooltip);
534 539 }
535 540 var x = item.datapoint.x.toFixed(2);
536 541 var y = item.datapoint.y.toFixed(2);
537 542
538 543 if (!item.series.label){
539 544 item.series.label = 'commits';
540 545 }
541 546 var d = new Date(x*1000);
542 547 var fd = d.toDateString()
543 548 var nr_commits = parseInt(y);
544 549
545 550 var cur_data = dataset[item.series.label].data[item.dataIndex];
546 551 var added = cur_data.added;
547 552 var changed = cur_data.changed;
548 553 var removed = cur_data.removed;
549 554
550 555 var nr_commits_suffix = " ${_('commits')} ";
551 556 var added_suffix = " ${_('files added')} ";
552 557 var changed_suffix = " ${_('files changed')} ";
553 558 var removed_suffix = " ${_('files removed')} ";
554 559
555 560
556 561 if(nr_commits == 1){nr_commits_suffix = " ${_('commit')} ";}
557 562 if(added==1){added_suffix=" ${_('file added')} ";}
558 563 if(changed==1){changed_suffix=" ${_('file changed')} ";}
559 564 if(removed==1){removed_suffix=" ${_('file removed')} ";}
560 565
561 566 showTooltip(item.pageX, item.pageY, item.series.label + " on " + fd
562 567 +'<br/>'+
563 568 nr_commits + nr_commits_suffix+'<br/>'+
564 569 added + added_suffix +'<br/>'+
565 570 changed + changed_suffix + '<br/>'+
566 571 removed + removed_suffix + '<br/>');
567 572 }
568 573 }
569 574 else {
570 575 var tooltip = YUD.get("tooltip");
571 576
572 577 if(tooltip) {
573 578 tooltip.parentNode.removeChild(tooltip);
574 579 }
575 580 previousPoint = null;
576 581 }
577 582 }
578 583
579 584 /**
580 585 * MAIN EXECUTION
581 586 */
582 587
583 588 var data = getDataAccordingToRanges(initial_ranges);
584 589 generateCheckboxes(data);
585 590
586 591 //main plot
587 592 var plot = YAHOO.widget.Flot(plotContainer,data,plot_options);
588 593
589 594 //overview
590 595 var overview = YAHOO.widget.Flot(overviewContainer, overview_dataset, overview_options);
591 596
592 597 //show initial selection on overview
593 598 overview.setSelection(initial_ranges);
594 599
595 600 plot.subscribe("plotselected", plotselected);
596 601
597 602 overview.subscribe("plotselected", function (ranges) {
598 603 plot.setSelection(ranges);
599 604 });
600 605
601 606 plot.subscribe("plothover", plothover);
602 607
603 608 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, initial_ranges]);
604 609 }
605 610 SummaryPlot(${c.ts_min},${c.ts_max},${c.commit_data|n},${c.overview_data|n});
606 611 </script>
607 612
608 613 </div>
609 614 </div>
610 615
611 616 <div class="box">
612 617 <div class="title">
613 618 <div class="breadcrumbs">${h.link_to(_('Shortlog'),h.url('shortlog_home',repo_name=c.repo_name))}</div>
614 619 </div>
615 620 <div class="table">
616 621 <div id="shortlog_data">
617 622 <%include file='../shortlog/shortlog_data.html'/>
618 623 </div>
619 624 ##%if c.repo_changesets:
620 625 ## ${h.link_to(_('show more'),h.url('changelog_home',repo_name=c.repo_name))}
621 626 ##%endif
622 627 </div>
623 628 </div>
624 629 <div class="box">
625 630 <div class="title">
626 631 <div class="breadcrumbs">${h.link_to(_('Tags'),h.url('tags_home',repo_name=c.repo_name))}</div>
627 632 </div>
628 633 <div class="table">
629 634 <%include file='../tags/tags_data.html'/>
630 635 %if c.repo_changesets:
631 636 ${h.link_to(_('show more'),h.url('tags_home',repo_name=c.repo_name))}
632 637 %endif
633 638 </div>
634 639 </div>
635 640 <div class="box">
636 641 <div class="title">
637 642 <div class="breadcrumbs">${h.link_to(_('Branches'),h.url('branches_home',repo_name=c.repo_name))}</div>
638 643 </div>
639 644 <div class="table">
640 645 <%include file='../branches/branches_data.html'/>
641 646 %if c.repo_changesets:
642 647 ${h.link_to(_('show more'),h.url('branches_home',repo_name=c.repo_name))}
643 648 %endif
644 649 </div>
645 650 </div>
646 651
647 </%def> No newline at end of file
652 </%def>
General Comments 0
You need to be logged in to leave comments. Login now