##// END OF EJS Templates
fixes #76 added confirmation dialog for user removal....
marcink -
r739:554ed649 beta
parent child Browse files
Show More
@@ -1,105 +1,105 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # ldap authentication lib
3 # ldap authentication lib
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 Nov 17, 2010
21 Created on Nov 17, 2010
22
22
23 @author: marcink
23 @author: marcink
24 """
24 """
25
25
26 from rhodecode.lib.exceptions import *
26 from rhodecode.lib.exceptions import *
27 import logging
27 import logging
28
28
29 log = logging.getLogger(__name__)
29 log = logging.getLogger(__name__)
30
30
31 try:
31 try:
32 import ldap
32 import ldap
33 except ImportError:
33 except ImportError:
34 pass
34 pass
35
35
36 class AuthLdap(object):
36 class AuthLdap(object):
37
37
38 def __init__(self, server, base_dn, port=389, bind_dn='', bind_pass='',
38 def __init__(self, server, base_dn, port=389, bind_dn='', bind_pass='',
39 use_ldaps=False, ldap_version=3):
39 use_ldaps=False, ldap_version=3):
40 self.ldap_version = ldap_version
40 self.ldap_version = ldap_version
41 if use_ldaps:
41 if use_ldaps:
42 port = port or 689
42 port = port or 689
43 self.LDAP_USE_LDAPS = use_ldaps
43 self.LDAP_USE_LDAPS = use_ldaps
44 self.LDAP_SERVER_ADDRESS = server
44 self.LDAP_SERVER_ADDRESS = server
45 self.LDAP_SERVER_PORT = port
45 self.LDAP_SERVER_PORT = port
46
46
47 #USE FOR READ ONLY BIND TO LDAP SERVER
47 #USE FOR READ ONLY BIND TO LDAP SERVER
48 self.LDAP_BIND_DN = bind_dn
48 self.LDAP_BIND_DN = bind_dn
49 self.LDAP_BIND_PASS = bind_pass
49 self.LDAP_BIND_PASS = bind_pass
50
50
51 ldap_server_type = 'ldap'
51 ldap_server_type = 'ldap'
52 if self.LDAP_USE_LDAPS:ldap_server_type = ldap_server_type + 's'
52 if self.LDAP_USE_LDAPS:ldap_server_type = ldap_server_type + 's'
53 self.LDAP_SERVER = "%s://%s:%s" % (ldap_server_type,
53 self.LDAP_SERVER = "%s://%s:%s" % (ldap_server_type,
54 self.LDAP_SERVER_ADDRESS,
54 self.LDAP_SERVER_ADDRESS,
55 self.LDAP_SERVER_PORT)
55 self.LDAP_SERVER_PORT)
56
56
57 self.BASE_DN = base_dn
57 self.BASE_DN = base_dn
58 self.AUTH_DN = "uid=%s,%s"
58 self.AUTH_DN = "uid=%s,%s"
59
59
60 def authenticate_ldap(self, username, password):
60 def authenticate_ldap(self, username, password):
61 """Authenticate a user via LDAP and return his/her LDAP properties.
61 """Authenticate a user via LDAP and return his/her LDAP properties.
62
62
63 Raises AuthenticationError if the credentials are rejected, or
63 Raises AuthenticationError if the credentials are rejected, or
64 EnvironmentError if the LDAP server can't be reached.
64 EnvironmentError if the LDAP server can't be reached.
65
65
66 :param username: username
66 :param username: username
67 :param password: password
67 :param password: password
68 """
68 """
69
69
70 from rhodecode.lib.helpers import chop_at
70 from rhodecode.lib.helpers import chop_at
71
71
72 uid = chop_at(username, "@%s" % self.LDAP_SERVER_ADDRESS)
72 uid = chop_at(username, "@%s" % self.LDAP_SERVER_ADDRESS)
73 dn = self.AUTH_DN % (uid, self.BASE_DN)
73 dn = self.AUTH_DN % (uid, self.BASE_DN)
74 log.debug("Authenticating %r at %s", dn, self.LDAP_SERVER)
74 log.debug("Authenticating %r at %s", dn, self.LDAP_SERVER)
75 if "," in username:
75 if "," in username:
76 raise LdapUsernameError("invalid character in username: ,")
76 raise LdapUsernameError("invalid character in username: ,")
77 try:
77 try:
78 ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, '/etc/openldap/cacerts')
78 ldap.set_option(ldap.OPT_X_TLS_CACERTDIR, '/etc/openldap/cacerts')
79 ldap.set_option(ldap.OPT_NETWORK_TIMEOUT, 10)
79 ldap.set_option(ldap.OPT_NETWORK_TIMEOUT, 10)
80 server = ldap.initialize(self.LDAP_SERVER)
80 server = ldap.initialize(self.LDAP_SERVER)
81 if self.ldap_version == 2:
81 if self.ldap_version == 2:
82 server.protocol = ldap.VERSION2
82 server.protocol = ldap.VERSION2
83 else:
83 else:
84 server.protocol = ldap.VERSION3
84 server.protocol = ldap.VERSION3
85
85
86 if self.LDAP_BIND_DN and self.LDAP_BIND_PASS:
86 if self.LDAP_BIND_DN and self.LDAP_BIND_PASS:
87 server.simple_bind_s(self.AUTH_DN % (self.LDAP_BIND_DN,
87 server.simple_bind_s(self.AUTH_DN % (self.LDAP_BIND_DN,
88 self.BASE_DN),
88 self.BASE_DN),
89 self.LDAP_BIND_PASS)
89 self.LDAP_BIND_PASS)
90
90
91 server.simple_bind_s(dn, password)
91 server.simple_bind_s(dn, password)
92 properties = server.search_s(dn, ldap.SCOPE_SUBTREE)
92 properties = server.search_s(dn, ldap.SCOPE_SUBTREE)
93 if not properties:
93 if not properties:
94 raise ldap.NO_SUCH_OBJECT()
94 raise ldap.NO_SUCH_OBJECT()
95 except ldap.NO_SUCH_OBJECT, e:
95 except ldap.NO_SUCH_OBJECT, e:
96 log.debug("LDAP says no such user '%s' (%s)", uid, username)
96 log.debug("LDAP says no such user '%s' (%s)", uid, username)
97 raise LdapUsernameError()
97 raise LdapUsernameError()
98 except ldap.INVALID_CREDENTIALS, e:
98 except ldap.INVALID_CREDENTIALS, e:
99 log.debug("LDAP rejected password for user '%s' (%s)", uid, username)
99 log.debug("LDAP rejected password for user '%s' (%s)", uid, username)
100 raise LdapPasswordError()
100 raise LdapPasswordError()
101 except ldap.SERVER_DOWN, e:
101 except ldap.SERVER_DOWN, e:
102 raise LdapConnectionError("LDAP can't access authentication server")
102 raise LdapConnectionError("LDAP can't access authentication server")
103
103
104 return properties[0]
104 return properties[0]
105
105
@@ -1,62 +1,63 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Users administration')} - ${c.rhodecode_name}
5 ${_('Users administration')} - ${c.rhodecode_name}
6 </%def>
6 </%def>
7
7
8 <%def name="breadcrumbs_links()">
8 <%def name="breadcrumbs_links()">
9 ${h.link_to(_('Admin'),h.url('admin_home'))} &raquo; ${_('Users')}
9 ${h.link_to(_('Admin'),h.url('admin_home'))} &raquo; ${_('Users')}
10 </%def>
10 </%def>
11
11
12 <%def name="page_nav()">
12 <%def name="page_nav()">
13 ${self.menu('admin')}
13 ${self.menu('admin')}
14 </%def>
14 </%def>
15
15
16 <%def name="main()">
16 <%def name="main()">
17 <div class="box">
17 <div class="box">
18 <!-- box / title -->
18 <!-- box / title -->
19 <div class="title">
19 <div class="title">
20 ${self.breadcrumbs()}
20 ${self.breadcrumbs()}
21 <ul class="links">
21 <ul class="links">
22 <li>
22 <li>
23 <span>${h.link_to(u'ADD NEW USER',h.url('new_user'))}</span>
23 <span>${h.link_to(u'ADD NEW USER',h.url('new_user'))}</span>
24 </li>
24 </li>
25
25
26 </ul>
26 </ul>
27 </div>
27 </div>
28 <!-- end box / title -->
28 <!-- end box / title -->
29 <div class="table">
29 <div class="table">
30 <table class="table_disp">
30 <table class="table_disp">
31 <tr class="header">
31 <tr class="header">
32 <th></th>
32 <th></th>
33 <th class="left">${_('username')}</th>
33 <th class="left">${_('username')}</th>
34 <th class="left">${_('name')}</th>
34 <th class="left">${_('name')}</th>
35 <th class="left">${_('lastname')}</th>
35 <th class="left">${_('lastname')}</th>
36 <th class="left">${_('active')}</th>
36 <th class="left">${_('active')}</th>
37 <th class="left">${_('admin')}</th>
37 <th class="left">${_('admin')}</th>
38 <th class="left">${_('ldap')}</th>
38 <th class="left">${_('ldap')}</th>
39 <th class="left">${_('action')}</th>
39 <th class="left">${_('action')}</th>
40 </tr>
40 </tr>
41 %for cnt,user in enumerate(c.users_list):
41 %for cnt,user in enumerate(c.users_list):
42 %if user.name !='default':
42 %if user.name !='default':
43 <tr class="parity${cnt%2}">
43 <tr class="parity${cnt%2}">
44 <td><div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(user.email,24)}"/> </div></td>
44 <td><div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(user.email,24)}"/> </div></td>
45 <td>${h.link_to(user.username,h.url('edit_user', id=user.user_id))}</td>
45 <td>${h.link_to(user.username,h.url('edit_user', id=user.user_id))}</td>
46 <td>${user.name}</td>
46 <td>${user.name}</td>
47 <td>${user.lastname}</td>
47 <td>${user.lastname}</td>
48 <td>${h.bool2icon(user.active)}</td>
48 <td>${h.bool2icon(user.active)}</td>
49 <td>${h.bool2icon(user.admin)}</td>
49 <td>${h.bool2icon(user.admin)}</td>
50 <td>${h.bool2icon(user.is_ldap)}</td>
50 <td>${h.bool2icon(user.is_ldap)}</td>
51 <td>
51 <td>
52 ${h.form(url('user', id=user.user_id),method='delete')}
52 ${h.form(url('user', id=user.user_id),method='delete')}
53 ${h.submit('remove','delete',id="remove_user_%s" % user.user_id,class_="delete_icon action_button")}
53 ${h.submit('remove_','delete',id="remove_user_%s" % user.user_id,
54 class_="delete_icon action_button",onclick="return confirm('Confirm to delete this user');")}
54 ${h.end_form()}
55 ${h.end_form()}
55 </td>
56 </td>
56 </tr>
57 </tr>
57 %endif
58 %endif
58 %endfor
59 %endfor
59 </table>
60 </table>
60 </div>
61 </div>
61 </div>
62 </div>
62 </%def>
63 </%def>
@@ -1,606 +1,606 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
58
59 %if c.following:
59 %if c.following:
60 <span id="follow_toggle" class="following" title="${_('Stop following this repository')}"
60 <span id="follow_toggle" class="following" title="${_('Stop following this repository')}"
61 onclick="javascript:toggleFollowingRepo(${c.repo_info.dbrepo.repo_id},'${str(h.get_token())}')">
61 onclick="javascript:toggleFollowingRepo(${c.repo_info.dbrepo.repo_id},'${str(h.get_token())}')">
62 </span>
62 </span>
63 %else:
63 %else:
64 <span id="follow_toggle" class="follow" title="${_('Start following this repository')}"
64 <span id="follow_toggle" class="follow" title="${_('Start following this repository')}"
65 onclick="javascript:toggleFollowingRepo(${c.repo_info.dbrepo.repo_id},'${str(h.get_token())}')">
65 onclick="javascript:toggleFollowingRepo(${c.repo_info.dbrepo.repo_id},'${str(h.get_token())}')">
66 </span>
66 </span>
67 %endif
67 %endif
68 <br/>
68 <br/>
69 %if c.repo_info.dbrepo.fork:
69 %if c.repo_info.dbrepo.fork:
70 <span style="margin-top:5px">
70 <span style="margin-top:5px">
71 <a href="${h.url('summary_home',repo_name=c.repo_info.dbrepo.fork.repo_name)}">
71 <a href="${h.url('summary_home',repo_name=c.repo_info.dbrepo.fork.repo_name)}">
72 <img class="icon" alt="${_('public')}"
72 <img class="icon" alt="${_('public')}"
73 title="${_('Fork of')} ${c.repo_info.dbrepo.fork.repo_name}"
73 title="${_('Fork of')} ${c.repo_info.dbrepo.fork.repo_name}"
74 src="/images/icons/arrow_divide.png"/>
74 src="/images/icons/arrow_divide.png"/>
75 ${_('Fork of')} ${c.repo_info.dbrepo.fork.repo_name}
75 ${_('Fork of')} ${c.repo_info.dbrepo.fork.repo_name}
76 </a>
76 </a>
77 </span>
77 </span>
78 %endif
78 %endif
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>${_('Description')}:</label>
85 <label>${_('Description')}:</label>
86 </div>
86 </div>
87 <div class="input-short">
87 <div class="input-short">
88 ${c.repo_info.dbrepo.description}
88 ${c.repo_info.dbrepo.description}
89 </div>
89 </div>
90 </div>
90 </div>
91
91
92
92
93 <div class="field">
93 <div class="field">
94 <div class="label">
94 <div class="label">
95 <label>${_('Contact')}:</label>
95 <label>${_('Contact')}:</label>
96 </div>
96 </div>
97 <div class="input-short">
97 <div class="input-short">
98 <div class="gravatar">
98 <div class="gravatar">
99 <img alt="gravatar" src="${h.gravatar_url(c.repo_info.dbrepo.user.email)}"/>
99 <img alt="gravatar" src="${h.gravatar_url(c.repo_info.dbrepo.user.email)}"/>
100 </div>
100 </div>
101 ${_('Username')}: ${c.repo_info.dbrepo.user.username}<br/>
101 ${_('Username')}: ${c.repo_info.dbrepo.user.username}<br/>
102 ${_('Name')}: ${c.repo_info.dbrepo.user.name} ${c.repo_info.dbrepo.user.lastname}<br/>
102 ${_('Name')}: ${c.repo_info.dbrepo.user.name} ${c.repo_info.dbrepo.user.lastname}<br/>
103 ${_('Email')}: <a href="mailto:${c.repo_info.dbrepo.user.email}">${c.repo_info.dbrepo.user.email}</a>
103 ${_('Email')}: <a href="mailto:${c.repo_info.dbrepo.user.email}">${c.repo_info.dbrepo.user.email}</a>
104 </div>
104 </div>
105 </div>
105 </div>
106
106
107 <div class="field">
107 <div class="field">
108 <div class="label">
108 <div class="label">
109 <label>${_('Last change')}:</label>
109 <label>${_('Last change')}:</label>
110 </div>
110 </div>
111 <div class="input-short">
111 <div class="input-short">
112 ${h.age(c.repo_info.last_change)} - ${c.repo_info.last_change}
112 ${h.age(c.repo_info.last_change)} - ${c.repo_info.last_change}
113 ${_('by')} ${h.get_changeset_safe(c.repo_info,'tip').author}
113 ${_('by')} ${h.get_changeset_safe(c.repo_info,'tip').author}
114
114
115 </div>
115 </div>
116 </div>
116 </div>
117
117
118 <div class="field">
118 <div class="field">
119 <div class="label">
119 <div class="label">
120 <label>${_('Clone url')}:</label>
120 <label>${_('Clone url')}:</label>
121 </div>
121 </div>
122 <div class="input-short">
122 <div class="input-short">
123 <input type="text" id="clone_url" readonly="readonly" value="hg clone ${c.clone_repo_url}" size="70"/>
123 <input type="text" id="clone_url" readonly="readonly" value="hg clone ${c.clone_repo_url}" size="70"/>
124 </div>
124 </div>
125 </div>
125 </div>
126
126
127 <div class="field">
127 <div class="field">
128 <div class="label">
128 <div class="label">
129 <label>${_('Trending languages')}:</label>
129 <label>${_('Trending languages')}:</label>
130 </div>
130 </div>
131 <div class="input-short">
131 <div class="input-short">
132 <div id="lang_stats">
132 <div id="lang_stats">
133
133
134 </div>
134 </div>
135 <script type="text/javascript">
135 <script type="text/javascript">
136 var data = ${c.trending_languages|n};
136 var data = ${c.trending_languages|n};
137 var total = 0;
137 var total = 0;
138 var no_data = true;
138 var no_data = true;
139 for (k in data){
139 for (k in data){
140 total += data[k];
140 total += data[k];
141 no_data = false;
141 no_data = false;
142 }
142 }
143 var tbl = document.createElement('table');
143 var tbl = document.createElement('table');
144 tbl.setAttribute('class','trending_language_tbl');
144 tbl.setAttribute('class','trending_language_tbl');
145 for (k in data){
145 for (k in data){
146 var tr = document.createElement('tr');
146 var tr = document.createElement('tr');
147 var percentage = Math.round((data[k]/total*100),2);
147 var percentage = Math.round((data[k]/total*100),2);
148 var value = data[k];
148 var value = data[k];
149 var td1 = document.createElement('td');
149 var td1 = document.createElement('td');
150 td1.width=150;
150 td1.width=150;
151 var trending_language_label = document.createElement('div');
151 var trending_language_label = document.createElement('div');
152 trending_language_label.innerHTML = k;
152 trending_language_label.innerHTML = k;
153 td1.appendChild(trending_language_label);
153 td1.appendChild(trending_language_label);
154
154
155 var td2 = document.createElement('td');
155 var td2 = document.createElement('td');
156 td2.setAttribute('style','padding-right:14px !important');
156 td2.setAttribute('style','padding-right:14px !important');
157 var trending_language = document.createElement('div');
157 var trending_language = document.createElement('div');
158 trending_language.title = k;
158 trending_language.title = k;
159 trending_language.innerHTML = "<b>"+percentage+"% "+value+" ${_('files')}</b>";
159 trending_language.innerHTML = "<b>"+percentage+"% "+value+" ${_('files')}</b>";
160 trending_language.setAttribute("class", 'trending_language top-right-rounded-corner bottom-right-rounded-corner');
160 trending_language.setAttribute("class", 'trending_language top-right-rounded-corner bottom-right-rounded-corner');
161 trending_language.style.width=percentage+"%";
161 trending_language.style.width=percentage+"%";
162 td2.appendChild(trending_language);
162 td2.appendChild(trending_language);
163
163
164 tr.appendChild(td1);
164 tr.appendChild(td1);
165 tr.appendChild(td2);
165 tr.appendChild(td2);
166 tbl.appendChild(tr);
166 tbl.appendChild(tr);
167
167
168 }
168 }
169 if(no_data){
169 if(no_data){
170 var tr = document.createElement('tr');
170 var tr = document.createElement('tr');
171 var td1 = document.createElement('td');
171 var td1 = document.createElement('td');
172 td1.innerHTML = "${_('No data loaded yet')}";
172 td1.innerHTML = "${_('No data loaded yet')}";
173 tr.appendChild(td1);
173 tr.appendChild(td1);
174 tbl.appendChild(tr);
174 tbl.appendChild(tr);
175 }
175 }
176 YAHOO.util.Dom.get('lang_stats').appendChild(tbl);
176 YAHOO.util.Dom.get('lang_stats').appendChild(tbl);
177 </script>
177 </script>
178
178
179 </div>
179 </div>
180 </div>
180 </div>
181
181
182 <div class="field">
182 <div class="field">
183 <div class="label">
183 <div class="label">
184 <label>${_('Download')}:</label>
184 <label>${_('Download')}:</label>
185 </div>
185 </div>
186 <div class="input-short">
186 <div class="input-short">
187 %for cnt,archive in enumerate(c.repo_info._get_archives()):
187 %for cnt,archive in enumerate(c.repo_info._get_archives()):
188 %if cnt >=1:
188 %if cnt >=1:
189 |
189 |
190 %endif
190 %endif
191 ${h.link_to(c.repo_info.name+'.'+archive['type'],
191 ${h.link_to(c.repo_info.name+'.'+archive['type'],
192 h.url('files_archive_home',repo_name=c.repo_info.name,
192 h.url('files_archive_home',repo_name=c.repo_info.name,
193 revision='tip',fileformat=archive['extension']),class_="archive_icon")}
193 revision='tip',fileformat=archive['extension']),class_="archive_icon")}
194 %endfor
194 %endfor
195 </div>
195 </div>
196 </div>
196 </div>
197
197
198 <div class="field">
198 <div class="field">
199 <div class="label">
199 <div class="label">
200 <label>${_('Feeds')}:</label>
200 <label>${_('Feeds')}:</label>
201 </div>
201 </div>
202 <div class="input-short">
202 <div class="input-short">
203 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.repo_info.name),class_='rss_icon')}
203 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.repo_info.name),class_='rss_icon')}
204 ${h.link_to(_('Atom'),h.url('atom_feed_home',repo_name=c.repo_info.name),class_='atom_icon')}
204 ${h.link_to(_('Atom'),h.url('atom_feed_home',repo_name=c.repo_info.name),class_='atom_icon')}
205 </div>
205 </div>
206 </div>
206 </div>
207 </div>
207 </div>
208 </div>
208 </div>
209 </div>
209 </div>
210
210
211 <div class="box box-right" style="min-height:455px">
211 <div class="box box-right" style="min-height:455px">
212 <!-- box / title -->
212 <!-- box / title -->
213 <div class="title">
213 <div class="title">
214 <h5>${_('Commit activity by day / author')}</h5>
214 <h5>${_('Commit activity by day / author')}</h5>
215 </div>
215 </div>
216
216
217 <div class="table">
217 <div class="table">
218 <div id="commit_history" style="width:460px;height:300px;float:left"></div>
218 <div id="commit_history" style="width:460px;height:300px;float:left"></div>
219 <div style="clear: both;height: 10px"></div>
219 <div style="clear: both;height: 10px"></div>
220 <div id="overview" style="width:460px;height:100px;float:left"></div>
220 <div id="overview" style="width:460px;height:100px;float:left"></div>
221
221
222 <div id="legend_data" style="clear:both;margin-top:10px;">
222 <div id="legend_data" style="clear:both;margin-top:10px;">
223 <div id="legend_container"></div>
223 <div id="legend_container"></div>
224 <div id="legend_choices">
224 <div id="legend_choices">
225 <table id="legend_choices_tables" style="font-size:smaller;color:#545454"></table>
225 <table id="legend_choices_tables" style="font-size:smaller;color:#545454"></table>
226 </div>
226 </div>
227 </div>
227 </div>
228 <script type="text/javascript">
228 <script type="text/javascript">
229 /**
229 /**
230 * Plots summary graph
230 * Plots summary graph
231 *
231 *
232 * @class SummaryPlot
232 * @class SummaryPlot
233 * @param {from} initial from for detailed graph
233 * @param {from} initial from for detailed graph
234 * @param {to} initial to for detailed graph
234 * @param {to} initial to for detailed graph
235 * @param {dataset}
235 * @param {dataset}
236 * @param {overview_dataset}
236 * @param {overview_dataset}
237 */
237 */
238 function SummaryPlot(from,to,dataset,overview_dataset) {
238 function SummaryPlot(from,to,dataset,overview_dataset) {
239 var initial_ranges = {
239 var initial_ranges = {
240 "xaxis":{
240 "xaxis":{
241 "from":from,
241 "from":from,
242 "to":to,
242 "to":to,
243 },
243 },
244 };
244 };
245 var dataset = dataset;
245 var dataset = dataset;
246 var overview_dataset = [overview_dataset];
246 var overview_dataset = [overview_dataset];
247 var choiceContainer = YAHOO.util.Dom.get("legend_choices");
247 var choiceContainer = YAHOO.util.Dom.get("legend_choices");
248 var choiceContainerTable = YAHOO.util.Dom.get("legend_choices_tables");
248 var choiceContainerTable = YAHOO.util.Dom.get("legend_choices_tables");
249 var plotContainer = YAHOO.util.Dom.get('commit_history');
249 var plotContainer = YAHOO.util.Dom.get('commit_history');
250 var overviewContainer = YAHOO.util.Dom.get('overview');
250 var overviewContainer = YAHOO.util.Dom.get('overview');
251
251
252 var plot_options = {
252 var plot_options = {
253 bars: {show:true,align:'center',lineWidth:4},
253 bars: {show:true,align:'center',lineWidth:4},
254 legend: {show:true, container:"legend_container"},
254 legend: {show:true, container:"legend_container"},
255 points: {show:true,radius:0,fill:false},
255 points: {show:true,radius:0,fill:false},
256 yaxis: {tickDecimals:0,},
256 yaxis: {tickDecimals:0,},
257 xaxis: {
257 xaxis: {
258 mode: "time",
258 mode: "time",
259 timeformat: "%d/%m",
259 timeformat: "%d/%m",
260 min:from,
260 min:from,
261 max:to,
261 max:to,
262 },
262 },
263 grid: {
263 grid: {
264 hoverable: true,
264 hoverable: true,
265 clickable: true,
265 clickable: true,
266 autoHighlight:true,
266 autoHighlight:true,
267 color: "#999"
267 color: "#999"
268 },
268 },
269 //selection: {mode: "x"}
269 //selection: {mode: "x"}
270 };
270 };
271 var overview_options = {
271 var overview_options = {
272 legend:{show:false},
272 legend:{show:false},
273 bars: {show:true,barWidth: 2,},
273 bars: {show:true,barWidth: 2,},
274 shadowSize: 0,
274 shadowSize: 0,
275 xaxis: {mode: "time", timeformat: "%d/%m/%y",},
275 xaxis: {mode: "time", timeformat: "%d/%m/%y",},
276 yaxis: {ticks: 3, min: 0,},
276 yaxis: {ticks: 3, min: 0,},
277 grid: {color: "#999",},
277 grid: {color: "#999",},
278 selection: {mode: "x"}
278 selection: {mode: "x"}
279 };
279 };
280
280
281 /**
281 /**
282 *get dummy data needed in few places
282 *get dummy data needed in few places
283 */
283 */
284 function getDummyData(label){
284 function getDummyData(label){
285 return {"label":label,
285 return {"label":label,
286 "data":[{"time":0,
286 "data":[{"time":0,
287 "commits":0,
287 "commits":0,
288 "added":0,
288 "added":0,
289 "changed":0,
289 "changed":0,
290 "removed":0,
290 "removed":0,
291 }],
291 }],
292 "schema":["commits"],
292 "schema":["commits"],
293 "color":'#ffffff',
293 "color":'#ffffff',
294 }
294 }
295 }
295 }
296
296
297 /**
297 /**
298 * generate checkboxes accordindly to data
298 * generate checkboxes accordindly to data
299 * @param keys
299 * @param keys
300 * @returns
300 * @returns
301 */
301 */
302 function generateCheckboxes(data) {
302 function generateCheckboxes(data) {
303 //append checkboxes
303 //append checkboxes
304 var i = 0;
304 var i = 0;
305 choiceContainerTable.innerHTML = '';
305 choiceContainerTable.innerHTML = '';
306 for(var pos in data) {
306 for(var pos in data) {
307
307
308 data[pos].color = i;
308 data[pos].color = i;
309 i++;
309 i++;
310 if(data[pos].label != ''){
310 if(data[pos].label != ''){
311 choiceContainerTable.innerHTML += '<tr><td>'+
311 choiceContainerTable.innerHTML += '<tr><td>'+
312 '<input type="checkbox" name="' + data[pos].label +'" checked="checked" />'
312 '<input type="checkbox" name="' + data[pos].label +'" checked="checked" />'
313 +data[pos].label+
313 +data[pos].label+
314 '</td></tr>';
314 '</td></tr>';
315 }
315 }
316 }
316 }
317 }
317 }
318
318
319 /**
319 /**
320 * ToolTip show
320 * ToolTip show
321 */
321 */
322 function showTooltip(x, y, contents) {
322 function showTooltip(x, y, contents) {
323 var div=document.getElementById('tooltip');
323 var div=document.getElementById('tooltip');
324 if(!div) {
324 if(!div) {
325 div = document.createElement('div');
325 div = document.createElement('div');
326 div.id="tooltip";
326 div.id="tooltip";
327 div.style.position="absolute";
327 div.style.position="absolute";
328 div.style.border='1px solid #fdd';
328 div.style.border='1px solid #fdd';
329 div.style.padding='2px';
329 div.style.padding='2px';
330 div.style.backgroundColor='#fee';
330 div.style.backgroundColor='#fee';
331 document.body.appendChild(div);
331 document.body.appendChild(div);
332 }
332 }
333 YAHOO.util.Dom.setStyle(div, 'opacity', 0);
333 YAHOO.util.Dom.setStyle(div, 'opacity', 0);
334 div.innerHTML = contents;
334 div.innerHTML = contents;
335 div.style.top=(y + 5) + "px";
335 div.style.top=(y + 5) + "px";
336 div.style.left=(x + 5) + "px";
336 div.style.left=(x + 5) + "px";
337
337
338 var anim = new YAHOO.util.Anim(div, {opacity: {to: 0.8}}, 0.2);
338 var anim = new YAHOO.util.Anim(div, {opacity: {to: 0.8}}, 0.2);
339 anim.animate();
339 anim.animate();
340 }
340 }
341
341
342 /**
342 /**
343 * This function will detect if selected period has some changesets
343 * This function will detect if selected period has some changesets
344 for this user if it does this data is then pushed for displaying
344 for this user if it does this data is then pushed for displaying
345 Additionally it will only display users that are selected by the checkbox
345 Additionally it will only display users that are selected by the checkbox
346 */
346 */
347 function getDataAccordingToRanges(ranges) {
347 function getDataAccordingToRanges(ranges) {
348
348
349 var data = [];
349 var data = [];
350 var keys = [];
350 var keys = [];
351 for(var key in dataset){
351 for(var key in dataset){
352 var push = false;
352 var push = false;
353
353
354 //method1 slow !!
354 //method1 slow !!
355 //*
355 //*
356 for(var ds in dataset[key].data){
356 for(var ds in dataset[key].data){
357 commit_data = dataset[key].data[ds];
357 commit_data = dataset[key].data[ds];
358 if (commit_data.time >= ranges.xaxis.from && commit_data.time <= ranges.xaxis.to){
358 if (commit_data.time >= ranges.xaxis.from && commit_data.time <= ranges.xaxis.to){
359 push = true;
359 push = true;
360 break;
360 break;
361 }
361 }
362 }
362 }
363 //*/
363 //*/
364
364
365 /*//method2 sorted commit data !!!
365 /*//method2 sorted commit data !!!
366
366
367 var first_commit = dataset[key].data[0].time;
367 var first_commit = dataset[key].data[0].time;
368 var last_commit = dataset[key].data[dataset[key].data.length-1].time;
368 var last_commit = dataset[key].data[dataset[key].data.length-1].time;
369
369
370 if (first_commit >= ranges.xaxis.from && last_commit <= ranges.xaxis.to){
370 if (first_commit >= ranges.xaxis.from && last_commit <= ranges.xaxis.to){
371 push = true;
371 push = true;
372 }
372 }
373 //*/
373 //*/
374
374
375 if(push){
375 if(push){
376 data.push(dataset[key]);
376 data.push(dataset[key]);
377 }
377 }
378 }
378 }
379 if(data.length >= 1){
379 if(data.length >= 1){
380 return data;
380 return data;
381 }
381 }
382 else{
382 else{
383 //just return dummy data for graph to plot itself
383 //just return dummy data for graph to plot itself
384 return [getDummyData('')];
384 return [getDummyData('')];
385 }
385 }
386
386
387 }
387 }
388
388
389 /**
389 /**
390 * redraw using new checkbox data
390 * redraw using new checkbox data
391 */
391 */
392 function plotchoiced(e,args){
392 function plotchoiced(e,args){
393 var cur_data = args[0];
393 var cur_data = args[0];
394 var cur_ranges = args[1];
394 var cur_ranges = args[1];
395
395
396 var new_data = [];
396 var new_data = [];
397 var inputs = choiceContainer.getElementsByTagName("input");
397 var inputs = choiceContainer.getElementsByTagName("input");
398
398
399 //show only checked labels
399 //show only checked labels
400 for(var i=0; i<inputs.length; i++) {
400 for(var i=0; i<inputs.length; i++) {
401 var checkbox_key = inputs[i].name;
401 var checkbox_key = inputs[i].name;
402
402
403 if(inputs[i].checked){
403 if(inputs[i].checked){
404 for(var d in cur_data){
404 for(var d in cur_data){
405 if(cur_data[d].label == checkbox_key){
405 if(cur_data[d].label == checkbox_key){
406 new_data.push(cur_data[d]);
406 new_data.push(cur_data[d]);
407 }
407 }
408 }
408 }
409 }
409 }
410 else{
410 else{
411 //push dummy data to not hide the label
411 //push dummy data to not hide the label
412 new_data.push(getDummyData(checkbox_key));
412 new_data.push(getDummyData(checkbox_key));
413 }
413 }
414 }
414 }
415
415
416 var new_options = YAHOO.lang.merge(plot_options, {
416 var new_options = YAHOO.lang.merge(plot_options, {
417 xaxis: {
417 xaxis: {
418 min: cur_ranges.xaxis.from,
418 min: cur_ranges.xaxis.from,
419 max: cur_ranges.xaxis.to,
419 max: cur_ranges.xaxis.to,
420 mode:"time",
420 mode:"time",
421 timeformat: "%d/%m",
421 timeformat: "%d/%m",
422 },
422 },
423 });
423 });
424 if (!new_data){
424 if (!new_data){
425 new_data = [[0,1]];
425 new_data = [[0,1]];
426 }
426 }
427 // do the zooming
427 // do the zooming
428 plot = YAHOO.widget.Flot(plotContainer, new_data, new_options);
428 plot = YAHOO.widget.Flot(plotContainer, new_data, new_options);
429
429
430 plot.subscribe("plotselected", plotselected);
430 plot.subscribe("plotselected", plotselected);
431
431
432 //resubscribe plothover
432 //resubscribe plothover
433 plot.subscribe("plothover", plothover);
433 plot.subscribe("plothover", plothover);
434
434
435 // don't fire event on the overview to prevent eternal loop
435 // don't fire event on the overview to prevent eternal loop
436 overview.setSelection(cur_ranges, true);
436 overview.setSelection(cur_ranges, true);
437
437
438 }
438 }
439
439
440 /**
440 /**
441 * plot only selected items from overview
441 * plot only selected items from overview
442 * @param ranges
442 * @param ranges
443 * @returns
443 * @returns
444 */
444 */
445 function plotselected(ranges,cur_data) {
445 function plotselected(ranges,cur_data) {
446 //updates the data for new plot
446 //updates the data for new plot
447 data = getDataAccordingToRanges(ranges);
447 data = getDataAccordingToRanges(ranges);
448 generateCheckboxes(data);
448 generateCheckboxes(data);
449
449
450 var new_options = YAHOO.lang.merge(plot_options, {
450 var new_options = YAHOO.lang.merge(plot_options, {
451 xaxis: {
451 xaxis: {
452 min: ranges.xaxis.from,
452 min: ranges.xaxis.from,
453 max: ranges.xaxis.to,
453 max: ranges.xaxis.to,
454 mode:"time",
454 mode:"time",
455 timeformat: "%d/%m",
455 timeformat: "%d/%m",
456 },
456 },
457 yaxis: {
457 yaxis: {
458 min: ranges.yaxis.from,
458 min: ranges.yaxis.from,
459 max: ranges.yaxis.to,
459 max: ranges.yaxis.to,
460 },
460 },
461
461
462 });
462 });
463 // do the zooming
463 // do the zooming
464 plot = YAHOO.widget.Flot(plotContainer, data, new_options);
464 plot = YAHOO.widget.Flot(plotContainer, data, new_options);
465
465
466 plot.subscribe("plotselected", plotselected);
466 plot.subscribe("plotselected", plotselected);
467
467
468 //resubscribe plothover
468 //resubscribe plothover
469 plot.subscribe("plothover", plothover);
469 plot.subscribe("plothover", plothover);
470
470
471 // don't fire event on the overview to prevent eternal loop
471 // don't fire event on the overview to prevent eternal loop
472 overview.setSelection(ranges, true);
472 overview.setSelection(ranges, true);
473
473
474 //resubscribe choiced
474 //resubscribe choiced
475 YAHOO.util.Event.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, ranges]);
475 YAHOO.util.Event.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, ranges]);
476 }
476 }
477
477
478 var previousPoint = null;
478 var previousPoint = null;
479
479
480 function plothover(o) {
480 function plothover(o) {
481 var pos = o.pos;
481 var pos = o.pos;
482 var item = o.item;
482 var item = o.item;
483
483
484 //YAHOO.util.Dom.get("x").innerHTML = pos.x.toFixed(2);
484 //YAHOO.util.Dom.get("x").innerHTML = pos.x.toFixed(2);
485 //YAHOO.util.Dom.get("y").innerHTML = pos.y.toFixed(2);
485 //YAHOO.util.Dom.get("y").innerHTML = pos.y.toFixed(2);
486 if (item) {
486 if (item) {
487 if (previousPoint != item.datapoint) {
487 if (previousPoint != item.datapoint) {
488 previousPoint = item.datapoint;
488 previousPoint = item.datapoint;
489
489
490 var tooltip = YAHOO.util.Dom.get("tooltip");
490 var tooltip = YAHOO.util.Dom.get("tooltip");
491 if(tooltip) {
491 if(tooltip) {
492 tooltip.parentNode.removeChild(tooltip);
492 tooltip.parentNode.removeChild(tooltip);
493 }
493 }
494 var x = item.datapoint.x.toFixed(2);
494 var x = item.datapoint.x.toFixed(2);
495 var y = item.datapoint.y.toFixed(2);
495 var y = item.datapoint.y.toFixed(2);
496
496
497 if (!item.series.label){
497 if (!item.series.label){
498 item.series.label = 'commits';
498 item.series.label = 'commits';
499 }
499 }
500 var d = new Date(x*1000);
500 var d = new Date(x*1000);
501 var fd = d.toDateString()
501 var fd = d.toDateString()
502 var nr_commits = parseInt(y);
502 var nr_commits = parseInt(y);
503
503
504 var cur_data = dataset[item.series.label].data[item.dataIndex];
504 var cur_data = dataset[item.series.label].data[item.dataIndex];
505 var added = cur_data.added;
505 var added = cur_data.added;
506 var changed = cur_data.changed;
506 var changed = cur_data.changed;
507 var removed = cur_data.removed;
507 var removed = cur_data.removed;
508
508
509 var nr_commits_suffix = " ${_('commits')} ";
509 var nr_commits_suffix = " ${_('commits')} ";
510 var added_suffix = " ${_('files added')} ";
510 var added_suffix = " ${_('files added')} ";
511 var changed_suffix = " ${_('files changed')} ";
511 var changed_suffix = " ${_('files changed')} ";
512 var removed_suffix = " ${_('files removed')} ";
512 var removed_suffix = " ${_('files removed')} ";
513
513
514
514
515 if(nr_commits == 1){nr_commits_suffix = " ${_('commit')} ";}
515 if(nr_commits == 1){nr_commits_suffix = " ${_('commit')} ";}
516 if(added==1){added_suffix=" ${_('file added')} ";}
516 if(added==1){added_suffix=" ${_('file added')} ";}
517 if(changed==1){changed_suffix=" ${_('file changed')} ";}
517 if(changed==1){changed_suffix=" ${_('file changed')} ";}
518 if(removed==1){removed_suffix=" ${_('file removed')} ";}
518 if(removed==1){removed_suffix=" ${_('file removed')} ";}
519
519
520 showTooltip(item.pageX, item.pageY, item.series.label + " on " + fd
520 showTooltip(item.pageX, item.pageY, item.series.label + " on " + fd
521 +'<br/>'+
521 +'<br/>'+
522 nr_commits + nr_commits_suffix+'<br/>'+
522 nr_commits + nr_commits_suffix+'<br/>'+
523 added + added_suffix +'<br/>'+
523 added + added_suffix +'<br/>'+
524 changed + changed_suffix + '<br/>'+
524 changed + changed_suffix + '<br/>'+
525 removed + removed_suffix + '<br/>');
525 removed + removed_suffix + '<br/>');
526 }
526 }
527 }
527 }
528 else {
528 else {
529 var tooltip = YAHOO.util.Dom.get("tooltip");
529 var tooltip = YAHOO.util.Dom.get("tooltip");
530
530
531 if(tooltip) {
531 if(tooltip) {
532 tooltip.parentNode.removeChild(tooltip);
532 tooltip.parentNode.removeChild(tooltip);
533 }
533 }
534 previousPoint = null;
534 previousPoint = null;
535 }
535 }
536 }
536 }
537
537
538 /**
538 /**
539 * MAIN EXECUTION
539 * MAIN EXECUTION
540 */
540 */
541
541
542 var data = getDataAccordingToRanges(initial_ranges);
542 var data = getDataAccordingToRanges(initial_ranges);
543 generateCheckboxes(data);
543 generateCheckboxes(data);
544
544
545 //main plot
545 //main plot
546 var plot = YAHOO.widget.Flot(plotContainer,data,plot_options);
546 var plot = YAHOO.widget.Flot(plotContainer,data,plot_options);
547
547
548 //overview
548 //overview
549 var overview = YAHOO.widget.Flot(overviewContainer, overview_dataset, overview_options);
549 var overview = YAHOO.widget.Flot(overviewContainer, overview_dataset, overview_options);
550
550
551 //show initial selection on overview
551 //show initial selection on overview
552 overview.setSelection(initial_ranges);
552 overview.setSelection(initial_ranges);
553
553
554 plot.subscribe("plotselected", plotselected);
554 plot.subscribe("plotselected", plotselected);
555
555
556 overview.subscribe("plotselected", function (ranges) {
556 overview.subscribe("plotselected", function (ranges) {
557 plot.setSelection(ranges);
557 plot.setSelection(ranges);
558 });
558 });
559
559
560 plot.subscribe("plothover", plothover);
560 plot.subscribe("plothover", plothover);
561
561
562 YAHOO.util.Event.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, initial_ranges]);
562 YAHOO.util.Event.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, initial_ranges]);
563 }
563 }
564 SummaryPlot(${c.ts_min},${c.ts_max},${c.commit_data|n},${c.overview_data|n});
564 SummaryPlot(${c.ts_min},${c.ts_max},${c.commit_data|n},${c.overview_data|n});
565 </script>
565 </script>
566
566
567 </div>
567 </div>
568 </div>
568 </div>
569
569
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 changes'),h.url('shortlog_home',repo_name=c.repo_name))}</div>
572 <div class="breadcrumbs">${h.link_to(_('Shortlog'),h.url('shortlog_home',repo_name=c.repo_name))}</div>
573 </div>
573 </div>
574 <div class="table">
574 <div class="table">
575 <div id="shortlog_data">
575 <div id="shortlog_data">
576 <%include file='../shortlog/shortlog_data.html'/>
576 <%include file='../shortlog/shortlog_data.html'/>
577 </div>
577 </div>
578 ##%if c.repo_changesets:
578 ##%if c.repo_changesets:
579 ## ${h.link_to(_('show more'),h.url('changelog_home',repo_name=c.repo_name))}
579 ## ${h.link_to(_('show more'),h.url('changelog_home',repo_name=c.repo_name))}
580 ##%endif
580 ##%endif
581 </div>
581 </div>
582 </div>
582 </div>
583 <div class="box">
583 <div class="box">
584 <div class="title">
584 <div class="title">
585 <div class="breadcrumbs">${h.link_to(_('Last ten tags'),h.url('tags_home',repo_name=c.repo_name))}</div>
585 <div class="breadcrumbs">${h.link_to(_('Last ten tags'),h.url('tags_home',repo_name=c.repo_name))}</div>
586 </div>
586 </div>
587 <div class="table">
587 <div class="table">
588 <%include file='../tags/tags_data.html'/>
588 <%include file='../tags/tags_data.html'/>
589 %if c.repo_changesets:
589 %if c.repo_changesets:
590 ${h.link_to(_('show more'),h.url('tags_home',repo_name=c.repo_name))}
590 ${h.link_to(_('show more'),h.url('tags_home',repo_name=c.repo_name))}
591 %endif
591 %endif
592 </div>
592 </div>
593 </div>
593 </div>
594 <div class="box">
594 <div class="box">
595 <div class="title">
595 <div class="title">
596 <div class="breadcrumbs">${h.link_to(_('Last ten branches'),h.url('branches_home',repo_name=c.repo_name))}</div>
596 <div class="breadcrumbs">${h.link_to(_('Last ten branches'),h.url('branches_home',repo_name=c.repo_name))}</div>
597 </div>
597 </div>
598 <div class="table">
598 <div class="table">
599 <%include file='../branches/branches_data.html'/>
599 <%include file='../branches/branches_data.html'/>
600 %if c.repo_changesets:
600 %if c.repo_changesets:
601 ${h.link_to(_('show more'),h.url('branches_home',repo_name=c.repo_name))}
601 ${h.link_to(_('show more'),h.url('branches_home',repo_name=c.repo_name))}
602 %endif
602 %endif
603 </div>
603 </div>
604 </div>
604 </div>
605
605
606 </%def> No newline at end of file
606 </%def>
General Comments 0
You need to be logged in to leave comments. Login now