##// END OF EJS Templates
added quickstart page for new repos
marcink -
r1736:e2d76554 beta
parent child Browse files
Show More
@@ -1,59 +1,63 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.shortlog
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Shortlog 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 modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
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, see <http://www.gnu.org/licenses/>.
25 25
26 26 import logging
27 27
28 28 from pylons import tmpl_context as c, request, url
29 29
30 30 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
31 31 from rhodecode.lib.base import BaseRepoController, render
32 32 from rhodecode.lib.helpers import RepoPage
33 from pylons.controllers.util import redirect
33 34
34 35 log = logging.getLogger(__name__)
35 36
36 37
37 38 class ShortlogController(BaseRepoController):
38 39
39 40 @LoginRequired()
40 41 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
41 42 'repository.admin')
42 43 def __before__(self):
43 44 super(ShortlogController, self).__before__()
44 45
45 46 def index(self, repo_name):
46 47 p = int(request.params.get('page', 1))
47 48 size = int(request.params.get('size', 20))
48 49
49 50 def url_generator(**kw):
50 51 return url('shortlog_home', repo_name=repo_name, size=size, **kw)
51 52
52 53 c.repo_changesets = RepoPage(c.rhodecode_repo, page=p,
53 54 items_per_page=size, url=url_generator)
54 55
56 if not c.repo_changesets:
57 return redirect(url('summary_home', repo_name=repo_name))
58
55 59 c.shortlog_data = render('shortlog/shortlog_data.html')
56 60 if request.environ.get('HTTP_X_PARTIAL_XHR'):
57 61 return c.shortlog_data
58 62 r = render('shortlog/shortlog.html')
59 63 return r
@@ -1,231 +1,228 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 modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
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, see <http://www.gnu.org/licenses/>.
25 25
26 26 import traceback
27 27 import calendar
28 28 import logging
29 29 from time import mktime
30 30 from datetime import timedelta, date
31 31 from itertools import product
32 32 from urlparse import urlparse
33 33
34 34 from vcs.exceptions import ChangesetError, EmptyRepositoryError, \
35 35 NodeDoesNotExistError
36 36
37 37 from pylons import tmpl_context as c, request, url, config
38 38 from pylons.i18n.translation import _
39 39
40 40 from beaker.cache import cache_region, region_invalidate
41 41
42 42 from rhodecode.model.db import Statistics, CacheInvalidation
43 43 from rhodecode.lib import ALL_READMES, ALL_EXTS
44 44 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
45 45 from rhodecode.lib.base import BaseRepoController, render
46 46 from rhodecode.lib.utils import EmptyChangeset
47 47 from rhodecode.lib.markup_renderer import MarkupRenderer
48 48 from rhodecode.lib.celerylib import run_task
49 49 from rhodecode.lib.celerylib.tasks import get_commits_stats, \
50 50 LANGUAGES_EXTENSIONS_MAP
51 51 from rhodecode.lib.helpers import RepoPage
52 52 from rhodecode.lib.compat import json, OrderedDict
53 53
54 54 log = logging.getLogger(__name__)
55 55
56 56 README_FILES = [''.join([x[0][0], x[1][0]]) for x in
57 57 sorted(list(product(ALL_READMES, ALL_EXTS)),
58 58 key=lambda y:y[0][1] + y[1][1])]
59 59
60 60 class SummaryController(BaseRepoController):
61 61
62 62 @LoginRequired()
63 63 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
64 64 'repository.admin')
65 65 def __before__(self):
66 66 super(SummaryController, self).__before__()
67 67
68 68 def index(self, repo_name):
69
70 e = request.environ
71 69 c.dbrepo = dbrepo = c.rhodecode_db_repo
72
73 70 c.following = self.scm_model.is_following_repo(repo_name,
74 71 self.rhodecode_user.user_id)
75 72
76 73 def url_generator(**kw):
77 74 return url('shortlog_home', repo_name=repo_name, size=10, **kw)
78 75
79 76 c.repo_changesets = RepoPage(c.rhodecode_repo, page=1,
80 77 items_per_page=10, url=url_generator)
81 78
82 79 if self.rhodecode_user.username == 'default':
83 80 # for default(anonymous) user we don't need to pass credentials
84 81 username = ''
85 82 password = ''
86 83 else:
87 84 username = str(self.rhodecode_user.username)
88 85 password = '@'
89 86
90 87 parsed_url = urlparse(url.current(qualified=True))
91 88
92 89 default_clone_uri = '{scheme}://{user}{pass}{netloc}{path}'
93 90
94 91 uri_tmpl = config.get('clone_uri', default_clone_uri)
95 92 uri_tmpl = uri_tmpl.replace('{', '%(').replace('}', ')s')
96 93
97 94 uri = uri_tmpl % {'user': username,
98 95 'pass': password,
99 96 'scheme': parsed_url.scheme,
100 97 'netloc':parsed_url.netloc,
101 98 'path':parsed_url.path}
102 99
103 100 c.clone_repo_url = uri
104 101 c.repo_tags = OrderedDict()
105 102 for name, hash in c.rhodecode_repo.tags.items()[:10]:
106 103 try:
107 104 c.repo_tags[name] = c.rhodecode_repo.get_changeset(hash)
108 105 except ChangesetError:
109 106 c.repo_tags[name] = EmptyChangeset(hash)
110 107
111 108 c.repo_branches = OrderedDict()
112 109 for name, hash in c.rhodecode_repo.branches.items()[:10]:
113 110 try:
114 111 c.repo_branches[name] = c.rhodecode_repo.get_changeset(hash)
115 112 except ChangesetError:
116 113 c.repo_branches[name] = EmptyChangeset(hash)
117 114
118 115 td = date.today() + timedelta(days=1)
119 116 td_1m = td - timedelta(days=calendar.mdays[td.month])
120 117 td_1y = td - timedelta(days=365)
121 118
122 119 ts_min_m = mktime(td_1m.timetuple())
123 120 ts_min_y = mktime(td_1y.timetuple())
124 121 ts_max_y = mktime(td.timetuple())
125 122
126 123 if dbrepo.enable_statistics:
127 124 c.show_stats = True
128 125 c.no_data_msg = _('No data loaded yet')
129 126 run_task(get_commits_stats, c.dbrepo.repo_name, ts_min_y, ts_max_y)
130 127 else:
131 128 c.show_stats = False
132 129 c.no_data_msg = _('Statistics are disabled for this repository')
133 130 c.ts_min = ts_min_m
134 131 c.ts_max = ts_max_y
135 132
136 133 stats = self.sa.query(Statistics)\
137 134 .filter(Statistics.repository == dbrepo)\
138 135 .scalar()
139 136
140 137 c.stats_percentage = 0
141 138
142 139 if stats and stats.languages:
143 140 c.no_data = False is dbrepo.enable_statistics
144 141 lang_stats_d = json.loads(stats.languages)
145 142 c.commit_data = stats.commit_activity
146 143 c.overview_data = stats.commit_activity_combined
147 144
148 145 lang_stats = ((x, {"count": y,
149 146 "desc": LANGUAGES_EXTENSIONS_MAP.get(x)})
150 147 for x, y in lang_stats_d.items())
151 148
152 149 c.trending_languages = json.dumps(OrderedDict(
153 150 sorted(lang_stats, reverse=True,
154 151 key=lambda k: k[1])[:10]
155 152 )
156 153 )
157 154 last_rev = stats.stat_on_revision
158 155 c.repo_last_rev = c.rhodecode_repo.count() - 1 \
159 156 if c.rhodecode_repo.revisions else 0
160 157 if last_rev == 0 or c.repo_last_rev == 0:
161 158 pass
162 159 else:
163 160 c.stats_percentage = '%.2f' % ((float((last_rev)) /
164 161 c.repo_last_rev) * 100)
165 162 else:
166 163 c.commit_data = json.dumps({})
167 164 c.overview_data = json.dumps([[ts_min_y, 0], [ts_max_y, 10]])
168 165 c.trending_languages = json.dumps({})
169 166 c.no_data = True
170 167
171 168 c.enable_downloads = dbrepo.enable_downloads
172 169 if c.enable_downloads:
173 170 c.download_options = self._get_download_links(c.rhodecode_repo)
174 171
175 172 c.readme_data, c.readme_file = self.__get_readme_data(c.rhodecode_repo)
176 173 return render('summary/summary.html')
177 174
178 175 def __get_readme_data(self, repo):
179 176
180 177 @cache_region('long_term')
181 178 def _get_readme_from_cache(key):
182 179 readme_data = None
183 180 readme_file = None
184 181 log.debug('Fetching readme file')
185 182 try:
186 183 cs = repo.get_changeset('tip')
187 184 renderer = MarkupRenderer()
188 185 for f in README_FILES:
189 186 try:
190 187 readme = cs.get_node(f)
191 188 readme_file = f
192 189 readme_data = renderer.render(readme.content, f)
193 190 log.debug('Found readme %s' % readme_file)
194 191 break
195 192 except NodeDoesNotExistError:
196 193 continue
197 194 except ChangesetError:
198 195 pass
199 196 except EmptyRepositoryError:
200 197 pass
201 198 except Exception:
202 199 log.error(traceback.format_exc())
203 200
204 201 return readme_data, readme_file
205 202
206 203 key = repo.name + '_README'
207 204 inv = CacheInvalidation.invalidate(key)
208 205 if inv is not None:
209 206 region_invalidate(_get_readme_from_cache, None, key)
210 207 CacheInvalidation.set_valid(inv.cache_key)
211 208 return _get_readme_from_cache(key)
212 209
213 210 def _get_download_links(self, repo):
214 211
215 212 download_l = []
216 213
217 214 branches_group = ([], _("Branches"))
218 215 tags_group = ([], _("Tags"))
219 216
220 217 for name, chs in c.rhodecode_repo.branches.items():
221 218 #chs = chs.split(':')[-1]
222 219 branches_group[0].append((chs, name),)
223 220 download_l.append(branches_group)
224 221
225 222 for name, chs in c.rhodecode_repo.tags.items():
226 223 #chs = chs.split(':')[-1]
227 224 tags_group[0].append((chs, name),)
228 225 download_l.append(tags_group)
229 226
230 227 return download_l
231 228
@@ -1,62 +1,84 b''
1 1 ## -*- coding: utf-8 -*-
2 2 % if c.repo_changesets:
3 3 <table>
4 4 <tr>
5 5 <th class="left">${_('commit message')}</th>
6 6 <th class="left">${_('age')}</th>
7 7 <th class="left">${_('author')}</th>
8 8 <th class="left">${_('revision')}</th>
9 9 <th class="left">${_('branch')}</th>
10 10 <th class="left">${_('tags')}</th>
11 11 <th class="left">${_('links')}</th>
12 12
13 13 </tr>
14 14 %for cnt,cs in enumerate(c.repo_changesets):
15 15 <tr class="parity${cnt%2}">
16 16 <td>
17 17 ${h.link_to(h.truncate(cs.message,50),
18 18 h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id),
19 19 title=cs.message)}
20 20 </td>
21 21 <td><span class="tooltip" title="${cs.date}">
22 22 ${h.age(cs.date)}</span>
23 23 </td>
24 24 <td title="${cs.author}">${h.person(cs.author)}</td>
25 25 <td>r${cs.revision}:${h.short_id(cs.raw_id)}</td>
26 26 <td>
27 27 <span class="logtags">
28 28 <span class="branchtag">${cs.branch}</span>
29 29 </span>
30 30 </td>
31 31 <td>
32 32 <span class="logtags">
33 33 %for tag in cs.tags:
34 34 <span class="tagtag">${tag}</span>
35 35 %endfor
36 36 </span>
37 37 </td>
38 38 <td class="nowrap">
39 39 ${h.link_to(_('changeset'),h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id),class_="ui-button-small xsmall")}
40 40 <span style="color:#515151">|</span>
41 41 ${h.link_to(_('files'),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id),class_="ui-button-small xsmall")}
42 42 </td>
43 43 </tr>
44 44 %endfor
45 45
46 46 </table>
47 47
48 48 <script type="text/javascript">
49 49 YUE.onDOMReady(function(){
50 50 YUE.delegate("shortlog_data","click",function(e, matchedEl, container){
51 51 ypjax(e.target.href,"shortlog_data",function(){tooltip_activate();});
52 52 YUE.preventDefault(e);
53 53 },'.pager_link');
54 54 });
55 55 </script>
56 56
57 57 <div class="pagination-wh pagination-left">
58 58 ${c.repo_changesets.pager('$link_previous ~2~ $link_next')}
59 59 </div>
60 60 %else:
61 ${_('There are no changes yet')}
61
62 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
63 <h4>${_('Add or upload files directly via RhodeCode')}</h4>
64 <div style="margin: 20px 30px;">
65 <div id="add_node_id" class="add_node">
66 <a class="ui-button-small" href="${h.url('files_add_home',repo_name=c.repo_name,revision=0,f_path='')}">${_('add new file')}</a>
67 </div>
68 </div>
62 69 %endif
70
71
72 <h4>${_('Push new repo')}</h4>
73 <pre>
74 hg clone ${c.clone_repo_url}
75 hg add README # add first file
76 hg commit -m "Initial" # commit with message
77 hg push # push changes back
78 </pre>
79
80 <h4>${_('Existing repository?')}</h4>
81 <pre>
82 hg push ${c.clone_repo_url}
83 </pre>
84 %endif
@@ -1,683 +1,689 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.dbrepo.just_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 <%
21 21 summary = lambda n:{False:'summary-short'}.get(n)
22 22 %>
23 23 %if c.show_stats:
24 24 <div class="box box-left">
25 25 %else:
26 26 <div class="box">
27 27 %endif
28 28 <!-- box / title -->
29 29 <div class="title">
30 30 ${self.breadcrumbs()}
31 31 </div>
32 32 <!-- end box / title -->
33 33 <div class="form">
34 34 <div id="summary" class="fields">
35 35
36 36 <div class="field">
37 37 <div class="label-summary">
38 38 <label>${_('Name')}:</label>
39 39 </div>
40 40 <div class="input ${summary(c.show_stats)}">
41 41 <div style="float:right;padding:5px 0px 0px 5px">
42 42 %if c.rhodecode_user.username != 'default':
43 43 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.dbrepo.repo_name,api_key=c.rhodecode_user.api_key),class_='rss_icon')}
44 44 ${h.link_to(_('ATOM'),h.url('atom_feed_home',repo_name=c.dbrepo.repo_name,api_key=c.rhodecode_user.api_key),class_='atom_icon')}
45 45 %else:
46 46 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.dbrepo.repo_name),class_='rss_icon')}
47 47 ${h.link_to(_('ATOM'),h.url('atom_feed_home',repo_name=c.dbrepo.repo_name),class_='atom_icon')}
48 48 %endif
49 49 </div>
50 50 %if c.rhodecode_user.username != 'default':
51 51 %if c.following:
52 52 <span id="follow_toggle" class="following" title="${_('Stop following this repository')}"
53 53 onclick="javascript:toggleFollowingRepo(this,${c.dbrepo.repo_id},'${str(h.get_token())}')">
54 54 </span>
55 55 %else:
56 56 <span id="follow_toggle" class="follow" title="${_('Start following this repository')}"
57 57 onclick="javascript:toggleFollowingRepo(this,${c.dbrepo.repo_id},'${str(h.get_token())}')">
58 58 </span>
59 59 %endif
60 60 %endif:
61 61 ##REPO TYPE
62 62 %if c.dbrepo.repo_type =='hg':
63 63 <img style="margin-bottom:2px" class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="${h.url('/images/icons/hgicon.png')}"/>
64 64 %endif
65 65 %if c.dbrepo.repo_type =='git':
66 66 <img style="margin-bottom:2px" class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="${h.url('/images/icons/giticon.png')}"/>
67 67 %endif
68 68
69 69 ##PUBLIC/PRIVATE
70 70 %if c.dbrepo.private:
71 71 <img style="margin-bottom:2px" class="icon" title="${_('private repository')}" alt="${_('private repository')}" src="${h.url('/images/icons/lock.png')}"/>
72 72 %else:
73 73 <img style="margin-bottom:2px" class="icon" title="${_('public repository')}" alt="${_('public repository')}" src="${h.url('/images/icons/lock_open.png')}"/>
74 74 %endif
75 75
76 76 ##REPO NAME
77 77 <span class="repo_name">${h.repo_link(c.dbrepo.groups_and_repo)}</span>
78 78
79 79 ##FORK
80 80 %if c.dbrepo.fork:
81 81 <div style="margin-top:5px;clear:both"">
82 82 <a href="${h.url('summary_home',repo_name=c.dbrepo.fork.repo_name)}"><img class="icon" alt="${_('public')}" title="${_('Fork of')} ${c.dbrepo.fork.repo_name}" src="${h.url('/images/icons/arrow_divide.png')}"/>
83 83 ${_('Fork of')} ${c.dbrepo.fork.repo_name}
84 84 </a>
85 85 </div>
86 86 %endif
87 87 ##REMOTE
88 88 %if c.dbrepo.clone_uri:
89 89 <div style="margin-top:5px;clear:both">
90 90 <a href="${h.url(str(h.hide_credentials(c.dbrepo.clone_uri)))}"><img class="icon" alt="${_('remote clone')}" title="${_('Clone from')} ${h.hide_credentials(c.dbrepo.clone_uri)}" src="${h.url('/images/icons/connect.png')}"/>
91 91 ${_('Clone from')} ${h.hide_credentials(c.dbrepo.clone_uri)}
92 92 </a>
93 93 </div>
94 94 %endif
95 95 </div>
96 96 </div>
97 97
98 98 <div class="field">
99 99 <div class="label-summary">
100 100 <label>${_('Description')}:</label>
101 101 </div>
102 102 <div class="input ${summary(c.show_stats)} desc">${h.urlify_text(c.dbrepo.description)}</div>
103 103 </div>
104 104
105 105 <div class="field">
106 106 <div class="label-summary">
107 107 <label>${_('Contact')}:</label>
108 108 </div>
109 109 <div class="input ${summary(c.show_stats)}">
110 110 <div class="gravatar">
111 111 <img alt="gravatar" src="${h.gravatar_url(c.dbrepo.user.email)}"/>
112 112 </div>
113 113 ${_('Username')}: ${c.dbrepo.user.username}<br/>
114 114 ${_('Name')}: ${c.dbrepo.user.name} ${c.dbrepo.user.lastname}<br/>
115 115 ${_('Email')}: <a href="mailto:${c.dbrepo.user.email}">${c.dbrepo.user.email}</a>
116 116 </div>
117 117 </div>
118 118
119 119 <div class="field">
120 120 <div class="label-summary">
121 121 <label>${_('Last change')}:</label>
122 122 </div>
123 123 <div class="input ${summary(c.show_stats)}">
124 124 <b>${'r%s:%s' % (h.get_changeset_safe(c.rhodecode_repo,'tip').revision,
125 125 h.get_changeset_safe(c.rhodecode_repo,'tip').short_id)}</b> -
126 126 <span class="tooltip" title="${c.rhodecode_repo.last_change}">
127 127 ${h.age(c.rhodecode_repo.last_change)}</span>
128 128 ${_('by')} ${h.get_changeset_safe(c.rhodecode_repo,'tip').author}
129 129 </div>
130 130 </div>
131 131
132 132 <div class="field">
133 133 <div class="label-summary">
134 134 <label>${_('Clone url')}:</label>
135 135 </div>
136 136 <div class="input ${summary(c.show_stats)}">
137 137 <input type="text" id="clone_url" readonly="readonly" value="${c.rhodecode_repo.alias} clone ${c.clone_repo_url}" size="70"/>
138 138 </div>
139 139 </div>
140 140
141 141 <div class="field">
142 142 <div class="label-summary">
143 143 <label>${_('Trending files')}:</label>
144 144 </div>
145 145 <div class="input ${summary(c.show_stats)}">
146 146 %if c.show_stats:
147 147 <div id="lang_stats"></div>
148 148 %else:
149 149 ${_('Statistics are disabled for this repository')}
150 150 %if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
151 151 ${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name),class_="ui-button-small")}
152 152 %endif
153 153 %endif
154 154 </div>
155 155 </div>
156 156
157 157 <div class="field">
158 158 <div class="label-summary">
159 159 <label>${_('Download')}:</label>
160 160 </div>
161 161 <div class="input ${summary(c.show_stats)}">
162 162 %if len(c.rhodecode_repo.revisions) == 0:
163 163 ${_('There are no downloads yet')}
164 164 %elif c.enable_downloads is False:
165 165 ${_('Downloads are disabled for this repository')}
166 166 %if h.HasPermissionAll('hg.admin')('enable downloads on from summary'):
167 167 ${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name),class_="ui-button-small")}
168 168 %endif
169 169 %else:
170 170 ${h.select('download_options',c.rhodecode_repo.get_changeset().raw_id,c.download_options)}
171 171 %for cnt,archive in enumerate(c.rhodecode_repo._get_archives()):
172 172 %if cnt >=1:
173 173 |
174 174 %endif
175 175 <span class="tooltip" title="${_('Download %s as %s') %('tip',archive['type'])}"
176 176 id="${archive['type']+'_link'}">${h.link_to(archive['type'],
177 177 h.url('files_archive_home',repo_name=c.dbrepo.repo_name,
178 178 fname='tip'+archive['extension']),class_="archive_icon")}</span>
179 179 %endfor
180 180 <span style="vertical-align: bottom">
181 181 <input id="archive_subrepos" type="checkbox" name="subrepos"/> <span class="tooltip" title="${_('Check this to download archive with subrepos')}" >${_('with subrepos')}</span>
182 182 </span>
183 183 %endif
184 184 </div>
185 185 </div>
186 186 </div>
187 187 </div>
188 188 </div>
189 189
190 190 %if c.show_stats:
191 191 <div class="box box-right" style="min-height:455px">
192 192 <!-- box / title -->
193 193 <div class="title">
194 194 <h5>${_('Commit activity by day / author')}</h5>
195 195 </div>
196 196
197 197 <div class="graph">
198 198 <div style="padding:0 10px 10px 15px;font-size: 1.2em;">
199 199 %if c.no_data:
200 200 ${c.no_data_msg}
201 201 %if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
202 202 ${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name),class_="ui-button-small")}
203 203 %endif
204 204 %else:
205 205 ${_('Loaded in')} ${c.stats_percentage} %
206 206 %endif
207 207 </div>
208 208 <div id="commit_history" style="width:450px;height:300px;float:left"></div>
209 209 <div style="clear: both;height: 10px"></div>
210 210 <div id="overview" style="width:450px;height:100px;float:left"></div>
211 211
212 212 <div id="legend_data" style="clear:both;margin-top:10px;">
213 213 <div id="legend_container"></div>
214 214 <div id="legend_choices">
215 215 <table id="legend_choices_tables" class="noborder" style="font-size:smaller;color:#545454"></table>
216 216 </div>
217 217 </div>
218 218 </div>
219 219 </div>
220 220 %endif
221 221
222 222 <div class="box">
223 223 <div class="title">
224 <div class="breadcrumbs">${h.link_to(_('Shortlog'),h.url('shortlog_home',repo_name=c.repo_name))}</div>
224 <div class="breadcrumbs">
225 %if c.repo_changesets:
226 ${h.link_to(_('Shortlog'),h.url('shortlog_home',repo_name=c.repo_name))}
227 %else:
228 ${_('Quick start')}
229 %endif
230 </div>
225 231 </div>
226 232 <div class="table">
227 233 <div id="shortlog_data">
228 234 <%include file='../shortlog/shortlog_data.html'/>
229 235 </div>
230 236 </div>
231 237 </div>
232 238
233 239 %if c.readme_data:
234 240 <div class="box" style="background-color: #FAFAFA">
235 241 <div class="title">
236 242 <div class="breadcrumbs"><a href="${h.url('files_home',repo_name=c.repo_name,revision='tip',f_path=c.readme_file)}">${c.readme_file}</a></div>
237 243 </div>
238 244 <div class="readme">
239 245 <div class="readme_box">
240 246 ${c.readme_data|n}
241 247 </div>
242 248 </div>
243 249 </div>
244 250 %endif
245 251
246 252 <script type="text/javascript">
247 253 var clone_url = 'clone_url';
248 254 YUE.on(clone_url,'click',function(e){
249 255 if(YUD.hasClass(clone_url,'selected')){
250 256 return
251 257 }
252 258 else{
253 259 YUD.addClass(clone_url,'selected');
254 260 YUD.get(clone_url).select();
255 261 }
256 262 })
257 263
258 264 var tmpl_links = {};
259 265 %for cnt,archive in enumerate(c.rhodecode_repo._get_archives()):
260 266 tmpl_links['${archive['type']}'] = '${h.link_to(archive['type'],
261 267 h.url('files_archive_home',repo_name=c.dbrepo.repo_name,
262 268 fname='__CS__'+archive['extension'],subrepos='__SUB__'),class_="archive_icon")}';
263 269 %endfor
264 270
265 271 YUE.on(['download_options','archive_subrepos'],'change',function(e){
266 272 var sm = YUD.get('download_options');
267 273 var new_cs = sm.options[sm.selectedIndex];
268 274
269 275 for(k in tmpl_links){
270 276 var s = YUD.get(k+'_link');
271 277 title_tmpl = "${_('Download %s as %s') % ('__CS_NAME__','__CS_EXT__')}";
272 278 s.title = title_tmpl.replace('__CS_NAME__',new_cs.text);
273 279 s.title = s.title.replace('__CS_EXT__',k);
274 280 var url = tmpl_links[k].replace('__CS__',new_cs.value);
275 281 var subrepos = YUD.get('archive_subrepos').checked
276 282 url = url.replace('__SUB__',subrepos);
277 283 s.innerHTML = url
278 284 }
279 285 });
280 286 </script>
281 287 %if c.show_stats:
282 288 <script type="text/javascript">
283 289 var data = ${c.trending_languages|n};
284 290 var total = 0;
285 291 var no_data = true;
286 292 for (k in data){
287 293 total += data[k].count;
288 294 no_data = false;
289 295 }
290 296 var tbl = document.createElement('table');
291 297 tbl.setAttribute('class','trending_language_tbl');
292 298 var cnt = 0;
293 299 for (k in data){
294 300 cnt += 1;
295 301 var hide = cnt>2;
296 302 var tr = document.createElement('tr');
297 303 if (hide){
298 304 tr.setAttribute('style','display:none');
299 305 tr.setAttribute('class','stats_hidden');
300 306 }
301 307 var percentage = Math.round((data[k].count/total*100),2);
302 308 var value = data[k].count;
303 309 var td1 = document.createElement('td');
304 310 td1.width = 150;
305 311 var trending_language_label = document.createElement('div');
306 312 trending_language_label.innerHTML = data[k].desc+" ("+k+")";
307 313 td1.appendChild(trending_language_label);
308 314
309 315 var td2 = document.createElement('td');
310 316 td2.setAttribute('style','padding-right:14px !important');
311 317 var trending_language = document.createElement('div');
312 318 var nr_files = value+" ${_('files')}";
313 319
314 320 trending_language.title = k+" "+nr_files;
315 321
316 322 if (percentage>22){
317 323 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"% "+nr_files+ "</b>";
318 324 }
319 325 else{
320 326 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"%</b>";
321 327 }
322 328
323 329 trending_language.setAttribute("class", 'trending_language top-right-rounded-corner bottom-right-rounded-corner');
324 330 trending_language.style.width=percentage+"%";
325 331 td2.appendChild(trending_language);
326 332
327 333 tr.appendChild(td1);
328 334 tr.appendChild(td2);
329 335 tbl.appendChild(tr);
330 336 if(cnt == 3){
331 337 var show_more = document.createElement('tr');
332 338 var td = document.createElement('td');
333 339 lnk = document.createElement('a');
334 340
335 341 lnk.href='#';
336 342 lnk.innerHTML = "${_('show more')}";
337 343 lnk.id='code_stats_show_more';
338 344 td.appendChild(lnk);
339 345
340 346 show_more.appendChild(td);
341 347 show_more.appendChild(document.createElement('td'));
342 348 tbl.appendChild(show_more);
343 349 }
344 350
345 351 }
346 352
347 353 YUD.get('lang_stats').appendChild(tbl);
348 354 YUE.on('code_stats_show_more','click',function(){
349 355 l = YUD.getElementsByClassName('stats_hidden')
350 356 for (e in l){
351 357 YUD.setStyle(l[e],'display','');
352 358 };
353 359 YUD.setStyle(YUD.get('code_stats_show_more'),
354 360 'display','none');
355 361 });
356 362 </script>
357 363 <script type="text/javascript">
358 364 /**
359 365 * Plots summary graph
360 366 *
361 367 * @class SummaryPlot
362 368 * @param {from} initial from for detailed graph
363 369 * @param {to} initial to for detailed graph
364 370 * @param {dataset}
365 371 * @param {overview_dataset}
366 372 */
367 373 function SummaryPlot(from,to,dataset,overview_dataset) {
368 374 var initial_ranges = {
369 375 "xaxis":{
370 376 "from":from,
371 377 "to":to,
372 378 },
373 379 };
374 380 var dataset = dataset;
375 381 var overview_dataset = [overview_dataset];
376 382 var choiceContainer = YUD.get("legend_choices");
377 383 var choiceContainerTable = YUD.get("legend_choices_tables");
378 384 var plotContainer = YUD.get('commit_history');
379 385 var overviewContainer = YUD.get('overview');
380 386
381 387 var plot_options = {
382 388 bars: {show:true,align:'center',lineWidth:4},
383 389 legend: {show:true, container:"legend_container"},
384 390 points: {show:true,radius:0,fill:false},
385 391 yaxis: {tickDecimals:0,},
386 392 xaxis: {
387 393 mode: "time",
388 394 timeformat: "%d/%m",
389 395 min:from,
390 396 max:to,
391 397 },
392 398 grid: {
393 399 hoverable: true,
394 400 clickable: true,
395 401 autoHighlight:true,
396 402 color: "#999"
397 403 },
398 404 //selection: {mode: "x"}
399 405 };
400 406 var overview_options = {
401 407 legend:{show:false},
402 408 bars: {show:true,barWidth: 2,},
403 409 shadowSize: 0,
404 410 xaxis: {mode: "time", timeformat: "%d/%m/%y",},
405 411 yaxis: {ticks: 3, min: 0,tickDecimals:0,},
406 412 grid: {color: "#999",},
407 413 selection: {mode: "x"}
408 414 };
409 415
410 416 /**
411 417 *get dummy data needed in few places
412 418 */
413 419 function getDummyData(label){
414 420 return {"label":label,
415 421 "data":[{"time":0,
416 422 "commits":0,
417 423 "added":0,
418 424 "changed":0,
419 425 "removed":0,
420 426 }],
421 427 "schema":["commits"],
422 428 "color":'#ffffff',
423 429 }
424 430 }
425 431
426 432 /**
427 433 * generate checkboxes accordindly to data
428 434 * @param keys
429 435 * @returns
430 436 */
431 437 function generateCheckboxes(data) {
432 438 //append checkboxes
433 439 var i = 0;
434 440 choiceContainerTable.innerHTML = '';
435 441 for(var pos in data) {
436 442
437 443 data[pos].color = i;
438 444 i++;
439 445 if(data[pos].label != ''){
440 446 choiceContainerTable.innerHTML += '<tr><td>'+
441 447 '<input type="checkbox" name="' + data[pos].label +'" checked="checked" />'
442 448 +data[pos].label+
443 449 '</td></tr>';
444 450 }
445 451 }
446 452 }
447 453
448 454 /**
449 455 * ToolTip show
450 456 */
451 457 function showTooltip(x, y, contents) {
452 458 var div=document.getElementById('tooltip');
453 459 if(!div) {
454 460 div = document.createElement('div');
455 461 div.id="tooltip";
456 462 div.style.position="absolute";
457 463 div.style.border='1px solid #fdd';
458 464 div.style.padding='2px';
459 465 div.style.backgroundColor='#fee';
460 466 document.body.appendChild(div);
461 467 }
462 468 YUD.setStyle(div, 'opacity', 0);
463 469 div.innerHTML = contents;
464 470 div.style.top=(y + 5) + "px";
465 471 div.style.left=(x + 5) + "px";
466 472
467 473 var anim = new YAHOO.util.Anim(div, {opacity: {to: 0.8}}, 0.2);
468 474 anim.animate();
469 475 }
470 476
471 477 /**
472 478 * This function will detect if selected period has some changesets
473 479 for this user if it does this data is then pushed for displaying
474 480 Additionally it will only display users that are selected by the checkbox
475 481 */
476 482 function getDataAccordingToRanges(ranges) {
477 483
478 484 var data = [];
479 485 var new_dataset = {};
480 486 var keys = [];
481 487 var max_commits = 0;
482 488 for(var key in dataset){
483 489
484 490 for(var ds in dataset[key].data){
485 491 commit_data = dataset[key].data[ds];
486 492 if (commit_data.time >= ranges.xaxis.from && commit_data.time <= ranges.xaxis.to){
487 493
488 494 if(new_dataset[key] === undefined){
489 495 new_dataset[key] = {data:[],schema:["commits"],label:key};
490 496 }
491 497 new_dataset[key].data.push(commit_data);
492 498 }
493 499 }
494 500 if (new_dataset[key] !== undefined){
495 501 data.push(new_dataset[key]);
496 502 }
497 503 }
498 504
499 505 if (data.length > 0){
500 506 return data;
501 507 }
502 508 else{
503 509 //just return dummy data for graph to plot itself
504 510 return [getDummyData('')];
505 511 }
506 512 }
507 513
508 514 /**
509 515 * redraw using new checkbox data
510 516 */
511 517 function plotchoiced(e,args){
512 518 var cur_data = args[0];
513 519 var cur_ranges = args[1];
514 520
515 521 var new_data = [];
516 522 var inputs = choiceContainer.getElementsByTagName("input");
517 523
518 524 //show only checked labels
519 525 for(var i=0; i<inputs.length; i++) {
520 526 var checkbox_key = inputs[i].name;
521 527
522 528 if(inputs[i].checked){
523 529 for(var d in cur_data){
524 530 if(cur_data[d].label == checkbox_key){
525 531 new_data.push(cur_data[d]);
526 532 }
527 533 }
528 534 }
529 535 else{
530 536 //push dummy data to not hide the label
531 537 new_data.push(getDummyData(checkbox_key));
532 538 }
533 539 }
534 540
535 541 var new_options = YAHOO.lang.merge(plot_options, {
536 542 xaxis: {
537 543 min: cur_ranges.xaxis.from,
538 544 max: cur_ranges.xaxis.to,
539 545 mode:"time",
540 546 timeformat: "%d/%m",
541 547 },
542 548 });
543 549 if (!new_data){
544 550 new_data = [[0,1]];
545 551 }
546 552 // do the zooming
547 553 plot = YAHOO.widget.Flot(plotContainer, new_data, new_options);
548 554
549 555 plot.subscribe("plotselected", plotselected);
550 556
551 557 //resubscribe plothover
552 558 plot.subscribe("plothover", plothover);
553 559
554 560 // don't fire event on the overview to prevent eternal loop
555 561 overview.setSelection(cur_ranges, true);
556 562
557 563 }
558 564
559 565 /**
560 566 * plot only selected items from overview
561 567 * @param ranges
562 568 * @returns
563 569 */
564 570 function plotselected(ranges,cur_data) {
565 571 //updates the data for new plot
566 572 var data = getDataAccordingToRanges(ranges);
567 573 generateCheckboxes(data);
568 574
569 575 var new_options = YAHOO.lang.merge(plot_options, {
570 576 xaxis: {
571 577 min: ranges.xaxis.from,
572 578 max: ranges.xaxis.to,
573 579 mode:"time",
574 580 timeformat: "%d/%m",
575 581 },
576 582 });
577 583 // do the zooming
578 584 plot = YAHOO.widget.Flot(plotContainer, data, new_options);
579 585
580 586 plot.subscribe("plotselected", plotselected);
581 587
582 588 //resubscribe plothover
583 589 plot.subscribe("plothover", plothover);
584 590
585 591 // don't fire event on the overview to prevent eternal loop
586 592 overview.setSelection(ranges, true);
587 593
588 594 //resubscribe choiced
589 595 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, ranges]);
590 596 }
591 597
592 598 var previousPoint = null;
593 599
594 600 function plothover(o) {
595 601 var pos = o.pos;
596 602 var item = o.item;
597 603
598 604 //YUD.get("x").innerHTML = pos.x.toFixed(2);
599 605 //YUD.get("y").innerHTML = pos.y.toFixed(2);
600 606 if (item) {
601 607 if (previousPoint != item.datapoint) {
602 608 previousPoint = item.datapoint;
603 609
604 610 var tooltip = YUD.get("tooltip");
605 611 if(tooltip) {
606 612 tooltip.parentNode.removeChild(tooltip);
607 613 }
608 614 var x = item.datapoint.x.toFixed(2);
609 615 var y = item.datapoint.y.toFixed(2);
610 616
611 617 if (!item.series.label){
612 618 item.series.label = 'commits';
613 619 }
614 620 var d = new Date(x*1000);
615 621 var fd = d.toDateString()
616 622 var nr_commits = parseInt(y);
617 623
618 624 var cur_data = dataset[item.series.label].data[item.dataIndex];
619 625 var added = cur_data.added;
620 626 var changed = cur_data.changed;
621 627 var removed = cur_data.removed;
622 628
623 629 var nr_commits_suffix = " ${_('commits')} ";
624 630 var added_suffix = " ${_('files added')} ";
625 631 var changed_suffix = " ${_('files changed')} ";
626 632 var removed_suffix = " ${_('files removed')} ";
627 633
628 634
629 635 if(nr_commits == 1){nr_commits_suffix = " ${_('commit')} ";}
630 636 if(added==1){added_suffix=" ${_('file added')} ";}
631 637 if(changed==1){changed_suffix=" ${_('file changed')} ";}
632 638 if(removed==1){removed_suffix=" ${_('file removed')} ";}
633 639
634 640 showTooltip(item.pageX, item.pageY, item.series.label + " on " + fd
635 641 +'<br/>'+
636 642 nr_commits + nr_commits_suffix+'<br/>'+
637 643 added + added_suffix +'<br/>'+
638 644 changed + changed_suffix + '<br/>'+
639 645 removed + removed_suffix + '<br/>');
640 646 }
641 647 }
642 648 else {
643 649 var tooltip = YUD.get("tooltip");
644 650
645 651 if(tooltip) {
646 652 tooltip.parentNode.removeChild(tooltip);
647 653 }
648 654 previousPoint = null;
649 655 }
650 656 }
651 657
652 658 /**
653 659 * MAIN EXECUTION
654 660 */
655 661
656 662 var data = getDataAccordingToRanges(initial_ranges);
657 663 generateCheckboxes(data);
658 664
659 665 //main plot
660 666 var plot = YAHOO.widget.Flot(plotContainer,data,plot_options);
661 667
662 668 //overview
663 669 var overview = YAHOO.widget.Flot(overviewContainer,
664 670 overview_dataset, overview_options);
665 671
666 672 //show initial selection on overview
667 673 overview.setSelection(initial_ranges);
668 674
669 675 plot.subscribe("plotselected", plotselected);
670 676 plot.subscribe("plothover", plothover)
671 677
672 678 overview.subscribe("plotselected", function (ranges) {
673 679 plot.setSelection(ranges);
674 680 });
675 681
676 682 // user choices on overview
677 683 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, initial_ranges]);
678 684 }
679 685 SummaryPlot(${c.ts_min},${c.ts_max},${c.commit_data|n},${c.overview_data|n});
680 686 </script>
681 687 %endif
682 688
683 689 </%def>
General Comments 0
You need to be logged in to leave comments. Login now