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