##// END OF EJS Templates
Fixed sumamry page description bug...
marcink -
r690:4685f3ea beta
parent child Browse files
Show More
@@ -1,168 +1,177 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # Model for RhodeCode
3 # Model for RhodeCode
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on April 9, 2010
21 Created on April 9, 2010
22 Model for RhodeCode
22 Model for RhodeCode
23 @author: marcink
23 @author: marcink
24 """
24 """
25 from beaker.cache import cache_region, region_invalidate
25 from beaker.cache import cache_region, region_invalidate
26 from mercurial import ui
26 from mercurial import ui
27 from rhodecode.lib import helpers as h
27 from rhodecode.lib import helpers as h
28 from rhodecode.lib.auth import HasRepoPermissionAny
28 from rhodecode.lib.auth import HasRepoPermissionAny
29 from rhodecode.lib.utils import get_repos
29 from rhodecode.lib.utils import get_repos
30 from rhodecode.model import meta
30 from rhodecode.model import meta
31 from rhodecode.model.caching_query import FromCache
32 from rhodecode.model.db import Repository, User, RhodeCodeUi
31 from rhodecode.model.db import Repository, User, RhodeCodeUi
33 from sqlalchemy.orm import joinedload
32 from sqlalchemy.orm import joinedload
34 from vcs import get_repo as vcs_get_repo, get_backend
33 from vcs import get_backend
35 from vcs.backends.hg import MercurialRepository
34 from vcs.utils.helpers import get_scm
36 from vcs.exceptions import RepositoryError, VCSError
35 from vcs.exceptions import RepositoryError, VCSError
37 from vcs.utils.lazy import LazyProperty
36 from vcs.utils.lazy import LazyProperty
38 import logging
37 import logging
39 import os
38 import os
40 import time
39 import time
41
40
42 log = logging.getLogger(__name__)
41 log = logging.getLogger(__name__)
43
42
44 class HgModel(object):
43 class HgModel(object):
45 """
44 """
46 Mercurial Model
45 Mercurial Model
47 """
46 """
48
47
49 def __init__(self, sa=None):
48 def __init__(self, sa=None):
50 if not sa:
49 if not sa:
51 self.sa = meta.Session()
50 self.sa = meta.Session()
52 else:
51 else:
53 self.sa = sa
52 self.sa = sa
54
53
55
54
56 @LazyProperty
55 @LazyProperty
57 def repos_path(self):
56 def repos_path(self):
58 """
57 """
59 Get's the repositories root path from database
58 Get's the repositories root path from database
60 """
59 """
61 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
60 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
62
61
63 return q.ui_value
62 return q.ui_value
64
63
65 def repo_scan(self, repos_path, baseui, initial=False):
64 def repo_scan(self, repos_path, baseui, initial=False):
66 """
65 """
67 Listing of repositories in given path. This path should not be a
66 Listing of repositories in given path. This path should not be a
68 repository itself. Return a dictionary of repository objects
67 repository itself. Return a dictionary of repository objects
69
68
70 :param repos_path: path to directory containing repositories
69 :param repos_path: path to directory containing repositories
71 :param baseui
70 :param baseui
72 :param initial: initial scan
71 :param initial: initial scan
73 """
72 """
74 log.info('scanning for repositories in %s', repos_path)
73 log.info('scanning for repositories in %s', repos_path)
75
74
76 if not isinstance(baseui, ui.ui):
75 if not isinstance(baseui, ui.ui):
77 baseui = ui.ui()
76 baseui = ui.ui()
78 repos_list = {}
77 repos_list = {}
78
79 for name, path in get_repos(repos_path):
79 for name, path in get_repos(repos_path):
80 try:
80 try:
81 if repos_list.has_key(name):
81 if repos_list.has_key(name):
82 raise RepositoryError('Duplicate repository name %s '
82 raise RepositoryError('Duplicate repository name %s '
83 'found in %s' % (name, path))
83 'found in %s' % (name, path))
84 else:
84 else:
85
85
86 klass = get_backend(path[0])
86 klass = get_backend(path[0])
87
87
88 if path[0] == 'hg':
88 if path[0] == 'hg':
89 repos_list[name] = klass(path[1], baseui=baseui)
89 repos_list[name] = klass(path[1], baseui=baseui)
90
90
91 if path[0] == 'git':
91 if path[0] == 'git':
92 repos_list[name] = klass(path[1])
92 repos_list[name] = klass(path[1])
93 except OSError:
93 except OSError:
94 continue
94 continue
95
95
96 return repos_list
96 return repos_list
97
97
98 def get_repos(self, all_repos=None):
98 def get_repos(self, all_repos=None):
99 """
99 """
100 Get all repos from db and for each such repo make backend and
100 Get all repos from db and for each repo create it's backend instance.
101 fetch dependent data from db
101 and fill that backed with information from database
102
103 :param all_repos: give specific repositories list, good for filtering
102 """
104 """
103 if not all_repos:
105 if not all_repos:
104 all_repos = self.sa.query(Repository).all()
106 all_repos = self.sa.query(Repository).all()
105
107
106 for r in all_repos:
108 for r in all_repos:
107
109
108 repo = self.get(r.repo_name)
110 repo = self.get(r.repo_name)
109
111
110 if repo is not None:
112 if repo is not None:
111 last_change = repo.last_change
113 last_change = repo.last_change
112 tip = h.get_changeset_safe(repo, 'tip')
114 tip = h.get_changeset_safe(repo, 'tip')
113
115
114 tmp_d = {}
116 tmp_d = {}
115 tmp_d['name'] = repo.name
117 tmp_d['name'] = repo.name
116 tmp_d['name_sort'] = tmp_d['name'].lower()
118 tmp_d['name_sort'] = tmp_d['name'].lower()
117 tmp_d['description'] = repo.dbrepo.description
119 tmp_d['description'] = repo.dbrepo.description
118 tmp_d['description_sort'] = tmp_d['description']
120 tmp_d['description_sort'] = tmp_d['description']
119 tmp_d['last_change'] = last_change
121 tmp_d['last_change'] = last_change
120 tmp_d['last_change_sort'] = time.mktime(last_change.timetuple())
122 tmp_d['last_change_sort'] = time.mktime(last_change.timetuple())
121 tmp_d['tip'] = tip.raw_id
123 tmp_d['tip'] = tip.raw_id
122 tmp_d['tip_sort'] = tip.revision
124 tmp_d['tip_sort'] = tip.revision
123 tmp_d['rev'] = tip.revision
125 tmp_d['rev'] = tip.revision
124 tmp_d['contact'] = repo.dbrepo.user.full_contact
126 tmp_d['contact'] = repo.dbrepo.user.full_contact
125 tmp_d['contact_sort'] = tmp_d['contact']
127 tmp_d['contact_sort'] = tmp_d['contact']
126 tmp_d['repo_archives'] = list(repo._get_archives())
128 tmp_d['repo_archives'] = list(repo._get_archives())
127 tmp_d['last_msg'] = tip.message
129 tmp_d['last_msg'] = tip.message
128 tmp_d['repo'] = repo
130 tmp_d['repo'] = repo
129 yield tmp_d
131 yield tmp_d
130
132
131 def get_repo(self, repo_name):
133 def get_repo(self, repo_name):
132 return self.get(repo_name)
134 return self.get(repo_name)
133
135
134 def get(self, repo_name):
136 def get(self, repo_name):
135 """
137 """
136 Get's repository from given name, creates BackendInstance and
138 Get's repository from given name, creates BackendInstance and
137 propagates it's data from database with all additional information
139 propagates it's data from database with all additional information
138 :param repo_name:
140 :param repo_name:
139 """
141 """
140 if not HasRepoPermissionAny('repository.read', 'repository.write',
142 if not HasRepoPermissionAny('repository.read', 'repository.write',
141 'repository.admin')(repo_name, 'get repo check'):
143 'repository.admin')(repo_name, 'get repo check'):
142 return
144 return
143
145
144 @cache_region('long_term', 'get_repo_cached_%s' % repo_name)
146 @cache_region('long_term', 'get_repo_cached_%s' % repo_name)
145 def _get_repo(repo_name):
147 def _get_repo(repo_name):
146
148
147 repo = vcs_get_repo(os.path.join(self.repos_path, repo_name),
149 repo_path = os.path.join(self.repos_path, repo_name)
148 alias=None, create=False)
150 alias = get_scm(repo_path)[0]
151
152 log.debug('Creating instance of %s repository', alias)
153 backend = get_backend(alias)
149
154
150 #skip hidden web repository
155 if alias == 'hg':
151 if isinstance(repo, MercurialRepository) and repo._get_hidden():
156 repo = backend(repo_path, create=False, baseui=None)
152 return
157 #skip hidden web repository
158 if repo._get_hidden():
159 return
160 else:
161 repo = backend(repo_path, create=False)
153
162
154 dbrepo = self.sa.query(Repository)\
163 dbrepo = self.sa.query(Repository)\
155 .options(joinedload(Repository.fork))\
164 .options(joinedload(Repository.fork))\
156 .options(joinedload(Repository.user))\
165 .options(joinedload(Repository.user))\
157 .filter(Repository.repo_name == repo_name)\
166 .filter(Repository.repo_name == repo_name)\
158 .scalar()
167 .scalar()
159 repo.dbrepo = dbrepo
168 repo.dbrepo = dbrepo
160 return repo
169 return repo
161
170
162 invalidate = False
171 invalidate = False
163 if invalidate:
172 if invalidate:
164 log.info('INVALIDATING CACHE FOR %s', repo_name)
173 log.info('INVALIDATING CACHE FOR %s', repo_name)
165 region_invalidate(_get_repo, None, repo_name)
174 region_invalidate(_get_repo, None, repo_name)
166
175
167 return _get_repo(repo_name)
176 return _get_repo(repo_name)
168
177
@@ -1,593 +1,593 b''
1 <%inherit file="/base/base.html"/>
1 <%inherit file="/base/base.html"/>
2
2
3 <%def name="title()">
3 <%def name="title()">
4 ${c.repo_name} ${_('Summary')} - ${c.rhodecode_name}
4 ${c.repo_name} ${_('Summary')} - ${c.rhodecode_name}
5 </%def>
5 </%def>
6
6
7 <%def name="breadcrumbs_links()">
7 <%def name="breadcrumbs_links()">
8 ${h.link_to(u'Home',h.url('/'))}
8 ${h.link_to(u'Home',h.url('/'))}
9 &raquo;
9 &raquo;
10 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
10 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
11 &raquo;
11 &raquo;
12 ${_('summary')}
12 ${_('summary')}
13 </%def>
13 </%def>
14
14
15 <%def name="page_nav()">
15 <%def name="page_nav()">
16 ${self.menu('summary')}
16 ${self.menu('summary')}
17 </%def>
17 </%def>
18
18
19 <%def name="main()">
19 <%def name="main()">
20 <script type="text/javascript">
20 <script type="text/javascript">
21 var E = YAHOO.util.Event;
21 var E = YAHOO.util.Event;
22 var D = YAHOO.util.Dom;
22 var D = YAHOO.util.Dom;
23
23
24 E.onDOMReady(function(e){
24 E.onDOMReady(function(e){
25 id = 'clone_url';
25 id = 'clone_url';
26 E.addListener(id,'click',function(e){
26 E.addListener(id,'click',function(e){
27 D.get('clone_url').select();
27 D.get('clone_url').select();
28 })
28 })
29 })
29 })
30 </script>
30 </script>
31 <div class="box box-left">
31 <div class="box box-left">
32 <!-- box / title -->
32 <!-- box / title -->
33 <div class="title">
33 <div class="title">
34 ${self.breadcrumbs()}
34 ${self.breadcrumbs()}
35 </div>
35 </div>
36 <!-- end box / title -->
36 <!-- end box / title -->
37 <div class="form">
37 <div class="form">
38 <div class="fields">
38 <div class="fields">
39
39
40 <div class="field">
40 <div class="field">
41 <div class="label">
41 <div class="label">
42 <label>${_('Name')}:</label>
42 <label>${_('Name')}:</label>
43 </div>
43 </div>
44 <div class="input-short">
44 <div class="input-short">
45 %if c.repo_info.dbrepo.repo_type =='hg':
45 %if c.repo_info.dbrepo.repo_type =='hg':
46 <img style="margin-bottom:2px" class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="/images/icons/hgicon.png"/>
46 <img style="margin-bottom:2px" class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="/images/icons/hgicon.png"/>
47 %endif
47 %endif
48 %if c.repo_info.dbrepo.repo_type =='git':
48 %if c.repo_info.dbrepo.repo_type =='git':
49 <img style="margin-bottom:2px" class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="/images/icons/giticon.png"/>
49 <img style="margin-bottom:2px" class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="/images/icons/giticon.png"/>
50 %endif
50 %endif
51
51
52 %if c.repo_info.dbrepo.private:
52 %if c.repo_info.dbrepo.private:
53 <img style="margin-bottom:2px" class="icon" title="${_('private repository')}" alt="${_('private repository')}" src="/images/icons/lock.png"/>
53 <img style="margin-bottom:2px" class="icon" title="${_('private repository')}" alt="${_('private repository')}" src="/images/icons/lock.png"/>
54 %else:
54 %else:
55 <img style="margin-bottom:2px" class="icon" title="${_('public repository')}" alt="${_('public repository')}" src="/images/icons/lock_open.png"/>
55 <img style="margin-bottom:2px" class="icon" title="${_('public repository')}" alt="${_('public repository')}" src="/images/icons/lock_open.png"/>
56 %endif
56 %endif
57 <span style="font-size: 1.6em;font-weight: bold;vertical-align: baseline;">${c.repo_info.name}</span>
57 <span style="font-size: 1.6em;font-weight: bold;vertical-align: baseline;">${c.repo_info.name}</span>
58 <br/>
58 <br/>
59 %if c.repo_info.dbrepo.fork:
59 %if c.repo_info.dbrepo.fork:
60 <span style="margin-top:5px">
60 <span style="margin-top:5px">
61 <a href="${h.url('summary_home',repo_name=c.repo_info.dbrepo.fork.repo_name)}">
61 <a href="${h.url('summary_home',repo_name=c.repo_info.dbrepo.fork.repo_name)}">
62 <img class="icon" alt="${_('public')}"
62 <img class="icon" alt="${_('public')}"
63 title="${_('Fork of')} ${c.repo_info.dbrepo.fork.repo_name}"
63 title="${_('Fork of')} ${c.repo_info.dbrepo.fork.repo_name}"
64 src="/images/icons/arrow_divide.png"/>
64 src="/images/icons/arrow_divide.png"/>
65 ${_('Fork of')} ${c.repo_info.dbrepo.fork.repo_name}
65 ${_('Fork of')} ${c.repo_info.dbrepo.fork.repo_name}
66 </a>
66 </a>
67 </span>
67 </span>
68 %endif
68 %endif
69 </div>
69 </div>
70 </div>
70 </div>
71
71
72
72
73 <div class="field">
73 <div class="field">
74 <div class="label">
74 <div class="label">
75 <label>${_('Description')}:</label>
75 <label>${_('Description')}:</label>
76 </div>
76 </div>
77 <div class="input-short">
77 <div class="input-short">
78 ${c.repo_info.description}
78 ${c.repo_info.dbrepo.description}
79 </div>
79 </div>
80 </div>
80 </div>
81
81
82
82
83 <div class="field">
83 <div class="field">
84 <div class="label">
84 <div class="label">
85 <label>${_('Contact')}:</label>
85 <label>${_('Contact')}:</label>
86 </div>
86 </div>
87 <div class="input-short">
87 <div class="input-short">
88 <div class="gravatar">
88 <div class="gravatar">
89 <img alt="gravatar" src="${h.gravatar_url(c.repo_info.dbrepo.user.email)}"/>
89 <img alt="gravatar" src="${h.gravatar_url(c.repo_info.dbrepo.user.email)}"/>
90 </div>
90 </div>
91 ${_('Username')}: ${c.repo_info.dbrepo.user.username}<br/>
91 ${_('Username')}: ${c.repo_info.dbrepo.user.username}<br/>
92 ${_('Name')}: ${c.repo_info.dbrepo.user.name} ${c.repo_info.dbrepo.user.lastname}<br/>
92 ${_('Name')}: ${c.repo_info.dbrepo.user.name} ${c.repo_info.dbrepo.user.lastname}<br/>
93 ${_('Email')}: <a href="mailto:${c.repo_info.dbrepo.user.email}">${c.repo_info.dbrepo.user.email}</a>
93 ${_('Email')}: <a href="mailto:${c.repo_info.dbrepo.user.email}">${c.repo_info.dbrepo.user.email}</a>
94 </div>
94 </div>
95 </div>
95 </div>
96
96
97 <div class="field">
97 <div class="field">
98 <div class="label">
98 <div class="label">
99 <label>${_('Last change')}:</label>
99 <label>${_('Last change')}:</label>
100 </div>
100 </div>
101 <div class="input-short">
101 <div class="input-short">
102 ${h.age(c.repo_info.last_change)} - ${c.repo_info.last_change}
102 ${h.age(c.repo_info.last_change)} - ${c.repo_info.last_change}
103 ${_('by')} ${h.get_changeset_safe(c.repo_info,'tip').author}
103 ${_('by')} ${h.get_changeset_safe(c.repo_info,'tip').author}
104
104
105 </div>
105 </div>
106 </div>
106 </div>
107
107
108 <div class="field">
108 <div class="field">
109 <div class="label">
109 <div class="label">
110 <label>${_('Clone url')}:</label>
110 <label>${_('Clone url')}:</label>
111 </div>
111 </div>
112 <div class="input-short">
112 <div class="input-short">
113 <input type="text" id="clone_url" readonly="readonly" value="hg clone ${c.clone_repo_url}" size="70"/>
113 <input type="text" id="clone_url" readonly="readonly" value="hg clone ${c.clone_repo_url}" size="70"/>
114 </div>
114 </div>
115 </div>
115 </div>
116
116
117 <div class="field">
117 <div class="field">
118 <div class="label">
118 <div class="label">
119 <label>${_('Trending languages')}:</label>
119 <label>${_('Trending languages')}:</label>
120 </div>
120 </div>
121 <div class="input-short">
121 <div class="input-short">
122 <div id="lang_stats">
122 <div id="lang_stats">
123
123
124 </div>
124 </div>
125 <script type="text/javascript">
125 <script type="text/javascript">
126 var data = ${c.trending_languages|n};
126 var data = ${c.trending_languages|n};
127 var total = 0;
127 var total = 0;
128 var no_data = true;
128 var no_data = true;
129 for (k in data){
129 for (k in data){
130 total += data[k];
130 total += data[k];
131 no_data = false;
131 no_data = false;
132 }
132 }
133 var tbl = document.createElement('table');
133 var tbl = document.createElement('table');
134 tbl.setAttribute('class','trending_language_tbl');
134 tbl.setAttribute('class','trending_language_tbl');
135 for (k in data){
135 for (k in data){
136 var tr = document.createElement('tr');
136 var tr = document.createElement('tr');
137 var percentage = Math.round((data[k]/total*100),2);
137 var percentage = Math.round((data[k]/total*100),2);
138 var value = data[k];
138 var value = data[k];
139 var td1 = document.createElement('td');
139 var td1 = document.createElement('td');
140 td1.width=150;
140 td1.width=150;
141 var trending_language_label = document.createElement('div');
141 var trending_language_label = document.createElement('div');
142 trending_language_label.innerHTML = k;
142 trending_language_label.innerHTML = k;
143 td1.appendChild(trending_language_label);
143 td1.appendChild(trending_language_label);
144
144
145 var td2 = document.createElement('td');
145 var td2 = document.createElement('td');
146 var trending_language = document.createElement('div');
146 var trending_language = document.createElement('div');
147 trending_language.title = k;
147 trending_language.title = k;
148 trending_language.innerHTML = "<b>"+percentage+"% "+value+" ${_('files')}</b>";
148 trending_language.innerHTML = "<b>"+percentage+"% "+value+" ${_('files')}</b>";
149 trending_language.setAttribute("class", 'trending_language top-right-rounded-corner bottom-right-rounded-corner');
149 trending_language.setAttribute("class", 'trending_language top-right-rounded-corner bottom-right-rounded-corner');
150 trending_language.style.width=percentage+"%";
150 trending_language.style.width=percentage+"%";
151 td2.appendChild(trending_language);
151 td2.appendChild(trending_language);
152
152
153 tr.appendChild(td1);
153 tr.appendChild(td1);
154 tr.appendChild(td2);
154 tr.appendChild(td2);
155 tbl.appendChild(tr);
155 tbl.appendChild(tr);
156
156
157 }
157 }
158 if(no_data){
158 if(no_data){
159 var tr = document.createElement('tr');
159 var tr = document.createElement('tr');
160 var td1 = document.createElement('td');
160 var td1 = document.createElement('td');
161 td1.innerHTML = "${_('No data loaded yet')}";
161 td1.innerHTML = "${_('No data loaded yet')}";
162 tr.appendChild(td1);
162 tr.appendChild(td1);
163 tbl.appendChild(tr);
163 tbl.appendChild(tr);
164 }
164 }
165 YAHOO.util.Dom.get('lang_stats').appendChild(tbl);
165 YAHOO.util.Dom.get('lang_stats').appendChild(tbl);
166 </script>
166 </script>
167
167
168 </div>
168 </div>
169 </div>
169 </div>
170
170
171 <div class="field">
171 <div class="field">
172 <div class="label">
172 <div class="label">
173 <label>${_('Download')}:</label>
173 <label>${_('Download')}:</label>
174 </div>
174 </div>
175 <div class="input-short">
175 <div class="input-short">
176 %for cnt,archive in enumerate(c.repo_info._get_archives()):
176 %for cnt,archive in enumerate(c.repo_info._get_archives()):
177 %if cnt >=1:
177 %if cnt >=1:
178 |
178 |
179 %endif
179 %endif
180 ${h.link_to(c.repo_info.name+'.'+archive['type'],
180 ${h.link_to(c.repo_info.name+'.'+archive['type'],
181 h.url('files_archive_home',repo_name=c.repo_info.name,
181 h.url('files_archive_home',repo_name=c.repo_info.name,
182 revision='tip',fileformat=archive['extension']),class_="archive_icon")}
182 revision='tip',fileformat=archive['extension']),class_="archive_icon")}
183 %endfor
183 %endfor
184 </div>
184 </div>
185 </div>
185 </div>
186
186
187 <div class="field">
187 <div class="field">
188 <div class="label">
188 <div class="label">
189 <label>${_('Feeds')}:</label>
189 <label>${_('Feeds')}:</label>
190 </div>
190 </div>
191 <div class="input-short">
191 <div class="input-short">
192 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.repo_info.name),class_='rss_icon')}
192 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.repo_info.name),class_='rss_icon')}
193 ${h.link_to(_('Atom'),h.url('atom_feed_home',repo_name=c.repo_info.name),class_='atom_icon')}
193 ${h.link_to(_('Atom'),h.url('atom_feed_home',repo_name=c.repo_info.name),class_='atom_icon')}
194 </div>
194 </div>
195 </div>
195 </div>
196 </div>
196 </div>
197 </div>
197 </div>
198 </div>
198 </div>
199
199
200 <div class="box box-right" style="min-height:455px">
200 <div class="box box-right" style="min-height:455px">
201 <!-- box / title -->
201 <!-- box / title -->
202 <div class="title">
202 <div class="title">
203 <h5>${_('Commit activity by day / author')}</h5>
203 <h5>${_('Commit activity by day / author')}</h5>
204 </div>
204 </div>
205
205
206 <div class="table">
206 <div class="table">
207 <div id="commit_history" style="width:460px;height:300px;float:left"></div>
207 <div id="commit_history" style="width:460px;height:300px;float:left"></div>
208 <div style="clear: both;height: 10px"></div>
208 <div style="clear: both;height: 10px"></div>
209 <div id="overview" style="width:460px;height:100px;float:left"></div>
209 <div id="overview" style="width:460px;height:100px;float:left"></div>
210
210
211 <div id="legend_data" style="clear:both;margin-top:10px;">
211 <div id="legend_data" style="clear:both;margin-top:10px;">
212 <div id="legend_container"></div>
212 <div id="legend_container"></div>
213 <div id="legend_choices">
213 <div id="legend_choices">
214 <table id="legend_choices_tables" style="font-size:smaller;color:#545454"></table>
214 <table id="legend_choices_tables" style="font-size:smaller;color:#545454"></table>
215 </div>
215 </div>
216 </div>
216 </div>
217 <script type="text/javascript">
217 <script type="text/javascript">
218 /**
218 /**
219 * Plots summary graph
219 * Plots summary graph
220 *
220 *
221 * @class SummaryPlot
221 * @class SummaryPlot
222 * @param {from} initial from for detailed graph
222 * @param {from} initial from for detailed graph
223 * @param {to} initial to for detailed graph
223 * @param {to} initial to for detailed graph
224 * @param {dataset}
224 * @param {dataset}
225 * @param {overview_dataset}
225 * @param {overview_dataset}
226 */
226 */
227 function SummaryPlot(from,to,dataset,overview_dataset) {
227 function SummaryPlot(from,to,dataset,overview_dataset) {
228 var initial_ranges = {
228 var initial_ranges = {
229 "xaxis":{
229 "xaxis":{
230 "from":from,
230 "from":from,
231 "to":to,
231 "to":to,
232 },
232 },
233 };
233 };
234 var dataset = dataset;
234 var dataset = dataset;
235 var overview_dataset = [overview_dataset];
235 var overview_dataset = [overview_dataset];
236 var choiceContainer = YAHOO.util.Dom.get("legend_choices");
236 var choiceContainer = YAHOO.util.Dom.get("legend_choices");
237 var choiceContainerTable = YAHOO.util.Dom.get("legend_choices_tables");
237 var choiceContainerTable = YAHOO.util.Dom.get("legend_choices_tables");
238 var plotContainer = YAHOO.util.Dom.get('commit_history');
238 var plotContainer = YAHOO.util.Dom.get('commit_history');
239 var overviewContainer = YAHOO.util.Dom.get('overview');
239 var overviewContainer = YAHOO.util.Dom.get('overview');
240
240
241 var plot_options = {
241 var plot_options = {
242 bars: {show:true,align:'center',lineWidth:4},
242 bars: {show:true,align:'center',lineWidth:4},
243 legend: {show:true, container:"legend_container"},
243 legend: {show:true, container:"legend_container"},
244 points: {show:true,radius:0,fill:false},
244 points: {show:true,radius:0,fill:false},
245 yaxis: {tickDecimals:0,},
245 yaxis: {tickDecimals:0,},
246 xaxis: {
246 xaxis: {
247 mode: "time",
247 mode: "time",
248 timeformat: "%d/%m",
248 timeformat: "%d/%m",
249 min:from,
249 min:from,
250 max:to,
250 max:to,
251 },
251 },
252 grid: {
252 grid: {
253 hoverable: true,
253 hoverable: true,
254 clickable: true,
254 clickable: true,
255 autoHighlight:true,
255 autoHighlight:true,
256 color: "#999"
256 color: "#999"
257 },
257 },
258 //selection: {mode: "x"}
258 //selection: {mode: "x"}
259 };
259 };
260 var overview_options = {
260 var overview_options = {
261 legend:{show:false},
261 legend:{show:false},
262 bars: {show:true,barWidth: 2,},
262 bars: {show:true,barWidth: 2,},
263 shadowSize: 0,
263 shadowSize: 0,
264 xaxis: {mode: "time", timeformat: "%d/%m/%y",},
264 xaxis: {mode: "time", timeformat: "%d/%m/%y",},
265 yaxis: {ticks: 3, min: 0,},
265 yaxis: {ticks: 3, min: 0,},
266 grid: {color: "#999",},
266 grid: {color: "#999",},
267 selection: {mode: "x"}
267 selection: {mode: "x"}
268 };
268 };
269
269
270 /**
270 /**
271 *get dummy data needed in few places
271 *get dummy data needed in few places
272 */
272 */
273 function getDummyData(label){
273 function getDummyData(label){
274 return {"label":label,
274 return {"label":label,
275 "data":[{"time":0,
275 "data":[{"time":0,
276 "commits":0,
276 "commits":0,
277 "added":0,
277 "added":0,
278 "changed":0,
278 "changed":0,
279 "removed":0,
279 "removed":0,
280 }],
280 }],
281 "schema":["commits"],
281 "schema":["commits"],
282 "color":'#ffffff',
282 "color":'#ffffff',
283 }
283 }
284 }
284 }
285
285
286 /**
286 /**
287 * generate checkboxes accordindly to data
287 * generate checkboxes accordindly to data
288 * @param keys
288 * @param keys
289 * @returns
289 * @returns
290 */
290 */
291 function generateCheckboxes(data) {
291 function generateCheckboxes(data) {
292 //append checkboxes
292 //append checkboxes
293 var i = 0;
293 var i = 0;
294 choiceContainerTable.innerHTML = '';
294 choiceContainerTable.innerHTML = '';
295 for(var pos in data) {
295 for(var pos in data) {
296
296
297 data[pos].color = i;
297 data[pos].color = i;
298 i++;
298 i++;
299 if(data[pos].label != ''){
299 if(data[pos].label != ''){
300 choiceContainerTable.innerHTML += '<tr><td>'+
300 choiceContainerTable.innerHTML += '<tr><td>'+
301 '<input type="checkbox" name="' + data[pos].label +'" checked="checked" />'
301 '<input type="checkbox" name="' + data[pos].label +'" checked="checked" />'
302 +data[pos].label+
302 +data[pos].label+
303 '</td></tr>';
303 '</td></tr>';
304 }
304 }
305 }
305 }
306 }
306 }
307
307
308 /**
308 /**
309 * ToolTip show
309 * ToolTip show
310 */
310 */
311 function showTooltip(x, y, contents) {
311 function showTooltip(x, y, contents) {
312 var div=document.getElementById('tooltip');
312 var div=document.getElementById('tooltip');
313 if(!div) {
313 if(!div) {
314 div = document.createElement('div');
314 div = document.createElement('div');
315 div.id="tooltip";
315 div.id="tooltip";
316 div.style.position="absolute";
316 div.style.position="absolute";
317 div.style.border='1px solid #fdd';
317 div.style.border='1px solid #fdd';
318 div.style.padding='2px';
318 div.style.padding='2px';
319 div.style.backgroundColor='#fee';
319 div.style.backgroundColor='#fee';
320 document.body.appendChild(div);
320 document.body.appendChild(div);
321 }
321 }
322 YAHOO.util.Dom.setStyle(div, 'opacity', 0);
322 YAHOO.util.Dom.setStyle(div, 'opacity', 0);
323 div.innerHTML = contents;
323 div.innerHTML = contents;
324 div.style.top=(y + 5) + "px";
324 div.style.top=(y + 5) + "px";
325 div.style.left=(x + 5) + "px";
325 div.style.left=(x + 5) + "px";
326
326
327 var anim = new YAHOO.util.Anim(div, {opacity: {to: 0.8}}, 0.2);
327 var anim = new YAHOO.util.Anim(div, {opacity: {to: 0.8}}, 0.2);
328 anim.animate();
328 anim.animate();
329 }
329 }
330
330
331 /**
331 /**
332 * This function will detect if selected period has some changesets for this user
332 * This function will detect if selected period has some changesets for this user
333 if it does this data is then pushed for displaying
333 if it does this data is then pushed for displaying
334 Additionally it will only display users that are selected by the checkbox
334 Additionally it will only display users that are selected by the checkbox
335 */
335 */
336 function getDataAccordingToRanges(ranges) {
336 function getDataAccordingToRanges(ranges) {
337
337
338 var data = [];
338 var data = [];
339 var keys = [];
339 var keys = [];
340 for(var key in dataset){
340 for(var key in dataset){
341 var push = false;
341 var push = false;
342 //method1 slow !!
342 //method1 slow !!
343 ///*
343 ///*
344 for(var ds in dataset[key].data){
344 for(var ds in dataset[key].data){
345 commit_data = dataset[key].data[ds];
345 commit_data = dataset[key].data[ds];
346 //console.log(key);
346 //console.log(key);
347 //console.log(new Date(commit_data.time*1000));
347 //console.log(new Date(commit_data.time*1000));
348 //console.log(new Date(ranges.xaxis.from*1000));
348 //console.log(new Date(ranges.xaxis.from*1000));
349 //console.log(new Date(ranges.xaxis.to*1000));
349 //console.log(new Date(ranges.xaxis.to*1000));
350 if (commit_data.time >= ranges.xaxis.from && commit_data.time <= ranges.xaxis.to){
350 if (commit_data.time >= ranges.xaxis.from && commit_data.time <= ranges.xaxis.to){
351 push = true;
351 push = true;
352 break;
352 break;
353 }
353 }
354 }
354 }
355 //*/
355 //*/
356 /*//method2 sorted commit data !!!
356 /*//method2 sorted commit data !!!
357 var first_commit = dataset[key].data[0].time;
357 var first_commit = dataset[key].data[0].time;
358 var last_commit = dataset[key].data[dataset[key].data.length-1].time;
358 var last_commit = dataset[key].data[dataset[key].data.length-1].time;
359
359
360 console.log(first_commit);
360 console.log(first_commit);
361 console.log(last_commit);
361 console.log(last_commit);
362
362
363 if (first_commit >= ranges.xaxis.from && last_commit <= ranges.xaxis.to){
363 if (first_commit >= ranges.xaxis.from && last_commit <= ranges.xaxis.to){
364 push = true;
364 push = true;
365 }
365 }
366 */
366 */
367 if(push){
367 if(push){
368 data.push(dataset[key]);
368 data.push(dataset[key]);
369 }
369 }
370 }
370 }
371 if(data.length >= 1){
371 if(data.length >= 1){
372 return data;
372 return data;
373 }
373 }
374 else{
374 else{
375 //just return dummy data for graph to plot itself
375 //just return dummy data for graph to plot itself
376 return [getDummyData('')];
376 return [getDummyData('')];
377 }
377 }
378
378
379 }
379 }
380
380
381 /**
381 /**
382 * redraw using new checkbox data
382 * redraw using new checkbox data
383 */
383 */
384 function plotchoiced(e,args){
384 function plotchoiced(e,args){
385 var cur_data = args[0];
385 var cur_data = args[0];
386 var cur_ranges = args[1];
386 var cur_ranges = args[1];
387
387
388 var new_data = [];
388 var new_data = [];
389 var inputs = choiceContainer.getElementsByTagName("input");
389 var inputs = choiceContainer.getElementsByTagName("input");
390
390
391 //show only checked labels
391 //show only checked labels
392 for(var i=0; i<inputs.length; i++) {
392 for(var i=0; i<inputs.length; i++) {
393 var checkbox_key = inputs[i].name;
393 var checkbox_key = inputs[i].name;
394
394
395 if(inputs[i].checked){
395 if(inputs[i].checked){
396 for(var d in cur_data){
396 for(var d in cur_data){
397 if(cur_data[d].label == checkbox_key){
397 if(cur_data[d].label == checkbox_key){
398 new_data.push(cur_data[d]);
398 new_data.push(cur_data[d]);
399 }
399 }
400 }
400 }
401 }
401 }
402 else{
402 else{
403 //push dummy data to not hide the label
403 //push dummy data to not hide the label
404 new_data.push(getDummyData(checkbox_key));
404 new_data.push(getDummyData(checkbox_key));
405 }
405 }
406 }
406 }
407
407
408 var new_options = YAHOO.lang.merge(plot_options, {
408 var new_options = YAHOO.lang.merge(plot_options, {
409 xaxis: {
409 xaxis: {
410 min: cur_ranges.xaxis.from,
410 min: cur_ranges.xaxis.from,
411 max: cur_ranges.xaxis.to,
411 max: cur_ranges.xaxis.to,
412 mode:"time",
412 mode:"time",
413 timeformat: "%d/%m",
413 timeformat: "%d/%m",
414 }
414 }
415 });
415 });
416 if (!new_data){
416 if (!new_data){
417 new_data = [[0,1]];
417 new_data = [[0,1]];
418 }
418 }
419 // do the zooming
419 // do the zooming
420 plot = YAHOO.widget.Flot(plotContainer, new_data, new_options);
420 plot = YAHOO.widget.Flot(plotContainer, new_data, new_options);
421
421
422 plot.subscribe("plotselected", plotselected);
422 plot.subscribe("plotselected", plotselected);
423
423
424 //resubscribe plothover
424 //resubscribe plothover
425 plot.subscribe("plothover", plothover);
425 plot.subscribe("plothover", plothover);
426
426
427 // don't fire event on the overview to prevent eternal loop
427 // don't fire event on the overview to prevent eternal loop
428 overview.setSelection(cur_ranges, true);
428 overview.setSelection(cur_ranges, true);
429
429
430 }
430 }
431
431
432 /**
432 /**
433 * plot only selected items from overview
433 * plot only selected items from overview
434 * @param ranges
434 * @param ranges
435 * @returns
435 * @returns
436 */
436 */
437 function plotselected(ranges,cur_data) {
437 function plotselected(ranges,cur_data) {
438 //updates the data for new plot
438 //updates the data for new plot
439 data = getDataAccordingToRanges(ranges);
439 data = getDataAccordingToRanges(ranges);
440 generateCheckboxes(data);
440 generateCheckboxes(data);
441
441
442 var new_options = YAHOO.lang.merge(plot_options, {
442 var new_options = YAHOO.lang.merge(plot_options, {
443 xaxis: {
443 xaxis: {
444 min: ranges.xaxis.from,
444 min: ranges.xaxis.from,
445 max: ranges.xaxis.to,
445 max: ranges.xaxis.to,
446 mode:"time",
446 mode:"time",
447 timeformat: "%d/%m",
447 timeformat: "%d/%m",
448 }
448 }
449 });
449 });
450 // do the zooming
450 // do the zooming
451 plot = YAHOO.widget.Flot(plotContainer, data, new_options);
451 plot = YAHOO.widget.Flot(plotContainer, data, new_options);
452
452
453 plot.subscribe("plotselected", plotselected);
453 plot.subscribe("plotselected", plotselected);
454
454
455 //resubscribe plothover
455 //resubscribe plothover
456 plot.subscribe("plothover", plothover);
456 plot.subscribe("plothover", plothover);
457
457
458 // don't fire event on the overview to prevent eternal loop
458 // don't fire event on the overview to prevent eternal loop
459 overview.setSelection(ranges, true);
459 overview.setSelection(ranges, true);
460
460
461 //resubscribe choiced
461 //resubscribe choiced
462 YAHOO.util.Event.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, ranges]);
462 YAHOO.util.Event.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, ranges]);
463 }
463 }
464
464
465 var previousPoint = null;
465 var previousPoint = null;
466
466
467 function plothover(o) {
467 function plothover(o) {
468 var pos = o.pos;
468 var pos = o.pos;
469 var item = o.item;
469 var item = o.item;
470
470
471 //YAHOO.util.Dom.get("x").innerHTML = pos.x.toFixed(2);
471 //YAHOO.util.Dom.get("x").innerHTML = pos.x.toFixed(2);
472 //YAHOO.util.Dom.get("y").innerHTML = pos.y.toFixed(2);
472 //YAHOO.util.Dom.get("y").innerHTML = pos.y.toFixed(2);
473 if (item) {
473 if (item) {
474 if (previousPoint != item.datapoint) {
474 if (previousPoint != item.datapoint) {
475 previousPoint = item.datapoint;
475 previousPoint = item.datapoint;
476
476
477 var tooltip = YAHOO.util.Dom.get("tooltip");
477 var tooltip = YAHOO.util.Dom.get("tooltip");
478 if(tooltip) {
478 if(tooltip) {
479 tooltip.parentNode.removeChild(tooltip);
479 tooltip.parentNode.removeChild(tooltip);
480 }
480 }
481 var x = item.datapoint.x.toFixed(2);
481 var x = item.datapoint.x.toFixed(2);
482 var y = item.datapoint.y.toFixed(2);
482 var y = item.datapoint.y.toFixed(2);
483
483
484 if (!item.series.label){
484 if (!item.series.label){
485 item.series.label = 'commits';
485 item.series.label = 'commits';
486 }
486 }
487 var d = new Date(x*1000);
487 var d = new Date(x*1000);
488 var fd = d.toDateString()
488 var fd = d.toDateString()
489 var nr_commits = parseInt(y);
489 var nr_commits = parseInt(y);
490
490
491 var cur_data = dataset[item.series.label].data[item.dataIndex];
491 var cur_data = dataset[item.series.label].data[item.dataIndex];
492 var added = cur_data.added;
492 var added = cur_data.added;
493 var changed = cur_data.changed;
493 var changed = cur_data.changed;
494 var removed = cur_data.removed;
494 var removed = cur_data.removed;
495
495
496 var nr_commits_suffix = " ${_('commits')} ";
496 var nr_commits_suffix = " ${_('commits')} ";
497 var added_suffix = " ${_('files added')} ";
497 var added_suffix = " ${_('files added')} ";
498 var changed_suffix = " ${_('files changed')} ";
498 var changed_suffix = " ${_('files changed')} ";
499 var removed_suffix = " ${_('files removed')} ";
499 var removed_suffix = " ${_('files removed')} ";
500
500
501
501
502 if(nr_commits == 1){nr_commits_suffix = " ${_('commit')} ";}
502 if(nr_commits == 1){nr_commits_suffix = " ${_('commit')} ";}
503 if(added==1){added_suffix=" ${_('file added')} ";}
503 if(added==1){added_suffix=" ${_('file added')} ";}
504 if(changed==1){changed_suffix=" ${_('file changed')} ";}
504 if(changed==1){changed_suffix=" ${_('file changed')} ";}
505 if(removed==1){removed_suffix=" ${_('file removed')} ";}
505 if(removed==1){removed_suffix=" ${_('file removed')} ";}
506
506
507 showTooltip(item.pageX, item.pageY, item.series.label + " on " + fd
507 showTooltip(item.pageX, item.pageY, item.series.label + " on " + fd
508 +'<br/>'+
508 +'<br/>'+
509 nr_commits + nr_commits_suffix+'<br/>'+
509 nr_commits + nr_commits_suffix+'<br/>'+
510 added + added_suffix +'<br/>'+
510 added + added_suffix +'<br/>'+
511 changed + changed_suffix + '<br/>'+
511 changed + changed_suffix + '<br/>'+
512 removed + removed_suffix + '<br/>');
512 removed + removed_suffix + '<br/>');
513 }
513 }
514 }
514 }
515 else {
515 else {
516 var tooltip = YAHOO.util.Dom.get("tooltip");
516 var tooltip = YAHOO.util.Dom.get("tooltip");
517
517
518 if(tooltip) {
518 if(tooltip) {
519 tooltip.parentNode.removeChild(tooltip);
519 tooltip.parentNode.removeChild(tooltip);
520 }
520 }
521 previousPoint = null;
521 previousPoint = null;
522 }
522 }
523 }
523 }
524
524
525 /**
525 /**
526 * MAIN EXECUTION
526 * MAIN EXECUTION
527 */
527 */
528
528
529 var data = getDataAccordingToRanges(initial_ranges);
529 var data = getDataAccordingToRanges(initial_ranges);
530 generateCheckboxes(data);
530 generateCheckboxes(data);
531
531
532 //main plot
532 //main plot
533 var plot = YAHOO.widget.Flot(plotContainer,data,plot_options);
533 var plot = YAHOO.widget.Flot(plotContainer,data,plot_options);
534
534
535 //overview
535 //overview
536 var overview = YAHOO.widget.Flot(overviewContainer, overview_dataset, overview_options);
536 var overview = YAHOO.widget.Flot(overviewContainer, overview_dataset, overview_options);
537
537
538 //show initial selection on overview
538 //show initial selection on overview
539 overview.setSelection(initial_ranges);
539 overview.setSelection(initial_ranges);
540
540
541 plot.subscribe("plotselected", plotselected);
541 plot.subscribe("plotselected", plotselected);
542
542
543 overview.subscribe("plotselected", function (ranges) {
543 overview.subscribe("plotselected", function (ranges) {
544 plot.setSelection(ranges);
544 plot.setSelection(ranges);
545 });
545 });
546
546
547 plot.subscribe("plothover", plothover);
547 plot.subscribe("plothover", plothover);
548
548
549 YAHOO.util.Event.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, initial_ranges]);
549 YAHOO.util.Event.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, initial_ranges]);
550 }
550 }
551 SummaryPlot(${c.ts_min},${c.ts_max},${c.commit_data|n},${c.overview_data|n});
551 SummaryPlot(${c.ts_min},${c.ts_max},${c.commit_data|n},${c.overview_data|n});
552 </script>
552 </script>
553
553
554 </div>
554 </div>
555 </div>
555 </div>
556
556
557 <div class="box">
557 <div class="box">
558 <div class="title">
558 <div class="title">
559 <div class="breadcrumbs">${h.link_to(_('Last ten changes'),h.url('changelog_home',repo_name=c.repo_name))}</div>
559 <div class="breadcrumbs">${h.link_to(_('Last ten changes'),h.url('changelog_home',repo_name=c.repo_name))}</div>
560 </div>
560 </div>
561 <div class="table">
561 <div class="table">
562 <div id="shortlog_data">
562 <div id="shortlog_data">
563 <%include file='../shortlog/shortlog_data.html'/>
563 <%include file='../shortlog/shortlog_data.html'/>
564 </div>
564 </div>
565 ##%if c.repo_changesets:
565 ##%if c.repo_changesets:
566 ## ${h.link_to(_('show more'),h.url('changelog_home',repo_name=c.repo_name))}
566 ## ${h.link_to(_('show more'),h.url('changelog_home',repo_name=c.repo_name))}
567 ##%endif
567 ##%endif
568 </div>
568 </div>
569 </div>
569 </div>
570 <div class="box">
570 <div class="box">
571 <div class="title">
571 <div class="title">
572 <div class="breadcrumbs">${h.link_to(_('Last ten tags'),h.url('tags_home',repo_name=c.repo_name))}</div>
572 <div class="breadcrumbs">${h.link_to(_('Last ten tags'),h.url('tags_home',repo_name=c.repo_name))}</div>
573 </div>
573 </div>
574 <div class="table">
574 <div class="table">
575 <%include file='../tags/tags_data.html'/>
575 <%include file='../tags/tags_data.html'/>
576 %if c.repo_changesets:
576 %if c.repo_changesets:
577 ${h.link_to(_('show more'),h.url('tags_home',repo_name=c.repo_name))}
577 ${h.link_to(_('show more'),h.url('tags_home',repo_name=c.repo_name))}
578 %endif
578 %endif
579 </div>
579 </div>
580 </div>
580 </div>
581 <div class="box">
581 <div class="box">
582 <div class="title">
582 <div class="title">
583 <div class="breadcrumbs">${h.link_to(_('Last ten branches'),h.url('branches_home',repo_name=c.repo_name))}</div>
583 <div class="breadcrumbs">${h.link_to(_('Last ten branches'),h.url('branches_home',repo_name=c.repo_name))}</div>
584 </div>
584 </div>
585 <div class="table">
585 <div class="table">
586 <%include file='../branches/branches_data.html'/>
586 <%include file='../branches/branches_data.html'/>
587 %if c.repo_changesets:
587 %if c.repo_changesets:
588 ${h.link_to(_('show more'),h.url('branches_home',repo_name=c.repo_name))}
588 ${h.link_to(_('show more'),h.url('branches_home',repo_name=c.repo_name))}
589 %endif
589 %endif
590 </div>
590 </div>
591 </div>
591 </div>
592
592
593 </%def> No newline at end of file
593 </%def>
General Comments 0
You need to be logged in to leave comments. Login now