##// END OF EJS Templates
removed decodes, thus it should be implemented on vcs side...
*** failed to import extension hggit: No module named hggit -
r428:dee0e7eb default
parent child Browse files
Show More
@@ -1,142 +1,142 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # summary controller for pylons
3 # summary controller for pylons
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 18, 2010
21 Created on April 18, 2010
22 summary controller for pylons
22 summary controller for pylons
23 @author: marcink
23 @author: marcink
24 """
24 """
25 from datetime import datetime, timedelta
25 from datetime import datetime, timedelta
26 from pylons import tmpl_context as c, request
26 from pylons import tmpl_context as c, request
27 from pylons_app.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
27 from pylons_app.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
28 from pylons_app.lib.base import BaseController, render
28 from pylons_app.lib.base import BaseController, render
29 from pylons_app.lib.helpers import person
29 from pylons_app.lib.helpers import person
30 from pylons_app.lib.utils import OrderedDict
30 from pylons_app.lib.utils import OrderedDict
31 from pylons_app.model.hg_model import HgModel
31 from pylons_app.model.hg_model import HgModel
32 from time import mktime
32 from time import mktime
33 from webhelpers.paginate import Page
33 from webhelpers.paginate import Page
34 import calendar
34 import calendar
35 import logging
35 import logging
36
36
37 log = logging.getLogger(__name__)
37 log = logging.getLogger(__name__)
38
38
39 class SummaryController(BaseController):
39 class SummaryController(BaseController):
40
40
41 @LoginRequired()
41 @LoginRequired()
42 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
42 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
43 'repository.admin')
43 'repository.admin')
44 def __before__(self):
44 def __before__(self):
45 super(SummaryController, self).__before__()
45 super(SummaryController, self).__before__()
46
46
47 def index(self):
47 def index(self):
48 hg_model = HgModel()
48 hg_model = HgModel()
49 c.repo_info = hg_model.get_repo(c.repo_name)
49 c.repo_info = hg_model.get_repo(c.repo_name)
50 c.repo_changesets = Page(list(c.repo_info[:10]), page=1, items_per_page=20)
50 c.repo_changesets = Page(list(c.repo_info[:10]), page=1, items_per_page=20)
51 e = request.environ
51 e = request.environ
52 uri = u'%(protocol)s://%(user)s@%(host)s/%(repo_name)s' % {
52 uri = u'%(protocol)s://%(user)s@%(host)s/%(repo_name)s' % {
53 'protocol': e.get('wsgi.url_scheme'),
53 'protocol': e.get('wsgi.url_scheme'),
54 'user':str(c.hg_app_user.username),
54 'user':str(c.hg_app_user.username),
55 'host':e.get('HTTP_HOST'),
55 'host':e.get('HTTP_HOST'),
56 'repo_name':c.repo_name, }
56 'repo_name':c.repo_name, }
57 c.clone_repo_url = uri
57 c.clone_repo_url = uri
58 c.repo_tags = OrderedDict()
58 c.repo_tags = OrderedDict()
59 for name, hash in c.repo_info.tags.items()[:10]:
59 for name, hash in c.repo_info.tags.items()[:10]:
60 c.repo_tags[name] = c.repo_info.get_changeset(hash)
60 c.repo_tags[name] = c.repo_info.get_changeset(hash)
61
61
62 c.repo_branches = OrderedDict()
62 c.repo_branches = OrderedDict()
63 for name, hash in c.repo_info.branches.items()[:10]:
63 for name, hash in c.repo_info.branches.items()[:10]:
64 c.repo_branches[name] = c.repo_info.get_changeset(hash)
64 c.repo_branches[name] = c.repo_info.get_changeset(hash)
65
65
66 c.commit_data = self.__get_commit_stats(c.repo_info)
66 c.commit_data = self.__get_commit_stats(c.repo_info)
67
67
68 return render('summary/summary.html')
68 return render('summary/summary.html')
69
69
70
70
71
71
72 def __get_commit_stats(self, repo):
72 def __get_commit_stats(self, repo):
73 aggregate = OrderedDict()
73 aggregate = OrderedDict()
74
74
75 #graph range
75 #graph range
76 td = datetime.today() + timedelta(days=1)
76 td = datetime.today() + timedelta(days=1)
77 y = td.year
77 y = td.year
78 m = td.month
78 m = td.month
79 d = td.day
79 d = td.day
80 c.ts_min = mktime((y, (td - timedelta(days=calendar.mdays[m] - 1)).month,
80 c.ts_min = mktime((y, (td - timedelta(days=calendar.mdays[m] - 1)).month,
81 d, 0, 0, 0, 0, 0, 0,))
81 d, 0, 0, 0, 0, 0, 0,))
82 c.ts_max = mktime((y, m, d, 0, 0, 0, 0, 0, 0,))
82 c.ts_max = mktime((y, m, d, 0, 0, 0, 0, 0, 0,))
83
83
84
84
85 def author_key_cleaner(k):
85 def author_key_cleaner(k):
86 k = person(k)
86 k = person(k)
87 k = k.replace('"', "'") #for js data compatibilty
87 k = k.replace('"', "'") #for js data compatibilty
88 return k
88 return k
89
89
90 for cs in repo:
90 for cs in repo:
91 k = '%s-%s-%s' % (cs.date.timetuple()[0], cs.date.timetuple()[1],
91 k = '%s-%s-%s' % (cs.date.timetuple()[0], cs.date.timetuple()[1],
92 cs.date.timetuple()[2])
92 cs.date.timetuple()[2])
93 timetupple = [int(x) for x in k.split('-')]
93 timetupple = [int(x) for x in k.split('-')]
94 timetupple.extend([0 for _ in xrange(6)])
94 timetupple.extend([0 for _ in xrange(6)])
95 k = mktime(timetupple)
95 k = mktime(timetupple)
96 if aggregate.has_key(author_key_cleaner(cs.author)):
96 if aggregate.has_key(author_key_cleaner(cs.author)):
97 if aggregate[author_key_cleaner(cs.author)].has_key(k):
97 if aggregate[author_key_cleaner(cs.author)].has_key(k):
98 aggregate[author_key_cleaner(cs.author)][k]["commits"] += 1
98 aggregate[author_key_cleaner(cs.author)][k]["commits"] += 1
99 aggregate[author_key_cleaner(cs.author)][k]["added"] += len(cs.added)
99 aggregate[author_key_cleaner(cs.author)][k]["added"] += len(cs.added)
100 aggregate[author_key_cleaner(cs.author)][k]["changed"] += len(cs.changed)
100 aggregate[author_key_cleaner(cs.author)][k]["changed"] += len(cs.changed)
101 aggregate[author_key_cleaner(cs.author)][k]["removed"] += len(cs.removed)
101 aggregate[author_key_cleaner(cs.author)][k]["removed"] += len(cs.removed)
102
102
103 else:
103 else:
104 #aggregate[author_key_cleaner(cs.author)].update(dates_range)
104 #aggregate[author_key_cleaner(cs.author)].update(dates_range)
105 if k >= c.ts_min and k <= c.ts_max:
105 if k >= c.ts_min and k <= c.ts_max:
106 aggregate[author_key_cleaner(cs.author)][k] = {}
106 aggregate[author_key_cleaner(cs.author)][k] = {}
107 aggregate[author_key_cleaner(cs.author)][k]["commits"] = 1
107 aggregate[author_key_cleaner(cs.author)][k]["commits"] = 1
108 aggregate[author_key_cleaner(cs.author)][k]["added"] = len(cs.added)
108 aggregate[author_key_cleaner(cs.author)][k]["added"] = len(cs.added)
109 aggregate[author_key_cleaner(cs.author)][k]["changed"] = len(cs.changed)
109 aggregate[author_key_cleaner(cs.author)][k]["changed"] = len(cs.changed)
110 aggregate[author_key_cleaner(cs.author)][k]["removed"] = len(cs.removed)
110 aggregate[author_key_cleaner(cs.author)][k]["removed"] = len(cs.removed)
111
111
112 else:
112 else:
113 if k >= c.ts_min and k <= c.ts_max:
113 if k >= c.ts_min and k <= c.ts_max:
114 aggregate[author_key_cleaner(cs.author)] = OrderedDict()
114 aggregate[author_key_cleaner(cs.author)] = OrderedDict()
115 #aggregate[author_key_cleaner(cs.author)].update(dates_range)
115 #aggregate[author_key_cleaner(cs.author)].update(dates_range)
116 aggregate[author_key_cleaner(cs.author)][k] = {}
116 aggregate[author_key_cleaner(cs.author)][k] = {}
117 aggregate[author_key_cleaner(cs.author)][k]["commits"] = 1
117 aggregate[author_key_cleaner(cs.author)][k]["commits"] = 1
118 aggregate[author_key_cleaner(cs.author)][k]["added"] = len(cs.added)
118 aggregate[author_key_cleaner(cs.author)][k]["added"] = len(cs.added)
119 aggregate[author_key_cleaner(cs.author)][k]["changed"] = len(cs.changed)
119 aggregate[author_key_cleaner(cs.author)][k]["changed"] = len(cs.changed)
120 aggregate[author_key_cleaner(cs.author)][k]["removed"] = len(cs.removed)
120 aggregate[author_key_cleaner(cs.author)][k]["removed"] = len(cs.removed)
121
121
122 d = ''
122 d = ''
123 tmpl0 = u""""%s":%s"""
123 tmpl0 = u""""%s":%s"""
124 tmpl1 = u"""{label:"%s",data:%s,schema:["commits"]},"""
124 tmpl1 = u"""{label:"%s",data:%s,schema:["commits"]},"""
125 for author in aggregate:
125 for author in aggregate:
126
126
127 d += tmpl0 % (author.decode('utf8'),
127 d += tmpl0 % (author,
128 tmpl1 \
128 tmpl1 \
129 % (author.decode('utf8'),
129 % (author,
130 [{"time":x,
130 [{"time":x,
131 "commits":aggregate[author][x]['commits'],
131 "commits":aggregate[author][x]['commits'],
132 "added":aggregate[author][x]['added'],
132 "added":aggregate[author][x]['added'],
133 "changed":aggregate[author][x]['changed'],
133 "changed":aggregate[author][x]['changed'],
134 "removed":aggregate[author][x]['removed'],
134 "removed":aggregate[author][x]['removed'],
135 } for x in aggregate[author]]))
135 } for x in aggregate[author]]))
136 if d == '':
136 if d == '':
137 d = '"%s":{label:"%s",data:[[0,1],]}' \
137 d = '"%s":{label:"%s",data:[[0,1],]}' \
138 % (author_key_cleaner(repo.contact),
138 % (author_key_cleaner(repo.contact),
139 author_key_cleaner(repo.contact))
139 author_key_cleaner(repo.contact))
140 return d
140 return d
141
141
142
142
@@ -1,323 +1,325 b''
1 """Helper functions
1 """Helper functions
2
2
3 Consists of functions to typically be used within templates, but also
3 Consists of functions to typically be used within templates, but also
4 available to Controllers. This module is available to both as 'h'.
4 available to Controllers. This module is available to both as 'h'.
5 """
5 """
6 from pygments.formatters import HtmlFormatter
6 from pygments.formatters import HtmlFormatter
7 from pygments import highlight as code_highlight
7 from pygments import highlight as code_highlight
8 from pylons import url, app_globals as g
8 from pylons import url, app_globals as g
9 from pylons.i18n.translation import _, ungettext
9 from pylons.i18n.translation import _, ungettext
10 from vcs.utils.annotate import annotate_highlight
10 from vcs.utils.annotate import annotate_highlight
11 from webhelpers.html import literal, HTML, escape
11 from webhelpers.html import literal, HTML, escape
12 from webhelpers.html.tools import *
12 from webhelpers.html.tools import *
13 from webhelpers.html.builder import make_tag
13 from webhelpers.html.builder import make_tag
14 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
14 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
15 end_form, file, form, hidden, image, javascript_link, link_to, link_to_if, \
15 end_form, file, form, hidden, image, javascript_link, link_to, link_to_if, \
16 link_to_unless, ol, required_legend, select, stylesheet_link, submit, text, \
16 link_to_unless, ol, required_legend, select, stylesheet_link, submit, text, \
17 password, textarea, title, ul, xml_declaration, radio
17 password, textarea, title, ul, xml_declaration, radio
18 from webhelpers.html.tools import auto_link, button_to, highlight, js_obfuscate, \
18 from webhelpers.html.tools import auto_link, button_to, highlight, js_obfuscate, \
19 mail_to, strip_links, strip_tags, tag_re
19 mail_to, strip_links, strip_tags, tag_re
20 from webhelpers.number import format_byte_size, format_bit_size
20 from webhelpers.number import format_byte_size, format_bit_size
21 from webhelpers.pylonslib import Flash as _Flash
21 from webhelpers.pylonslib import Flash as _Flash
22 from webhelpers.pylonslib.secure_form import secure_form
22 from webhelpers.pylonslib.secure_form import secure_form
23 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
23 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
24 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
24 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
25 replace_whitespace, urlify, truncate, wrap_paragraphs
25 replace_whitespace, urlify, truncate, wrap_paragraphs
26
26
27
27
28 #Custom helpers here :)
28 #Custom helpers here :)
29 class _Link(object):
29 class _Link(object):
30 '''
30 '''
31 Make a url based on label and url with help of url_for
31 Make a url based on label and url with help of url_for
32 @param label:name of link if not defined url is used
32 @param label:name of link if not defined url is used
33 @param url: the url for link
33 @param url: the url for link
34 '''
34 '''
35
35
36 def __call__(self, label='', *url_, **urlargs):
36 def __call__(self, label='', *url_, **urlargs):
37 if label is None or '':
37 if label is None or '':
38 label = url
38 label = url
39 link_fn = link_to(label, url(*url_, **urlargs))
39 link_fn = link_to(label, url(*url_, **urlargs))
40 return link_fn
40 return link_fn
41
41
42 link = _Link()
42 link = _Link()
43
43
44 class _GetError(object):
44 class _GetError(object):
45
45
46 def __call__(self, field_name, form_errors):
46 def __call__(self, field_name, form_errors):
47 tmpl = """<span class="error_msg">%s</span>"""
47 tmpl = """<span class="error_msg">%s</span>"""
48 if form_errors and form_errors.has_key(field_name):
48 if form_errors and form_errors.has_key(field_name):
49 return literal(tmpl % form_errors.get(field_name))
49 return literal(tmpl % form_errors.get(field_name))
50
50
51 get_error = _GetError()
51 get_error = _GetError()
52
52
53 def recursive_replace(str, replace=' '):
53 def recursive_replace(str, replace=' '):
54 """
54 """
55 Recursive replace of given sign to just one instance
55 Recursive replace of given sign to just one instance
56 @param str: given string
56 @param str: given string
57 @param replace:char to find and replace multiple instances
57 @param replace:char to find and replace multiple instances
58
58
59 Examples::
59 Examples::
60 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
60 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
61 'Mighty-Mighty-Bo-sstones'
61 'Mighty-Mighty-Bo-sstones'
62 """
62 """
63
63
64 if str.find(replace * 2) == -1:
64 if str.find(replace * 2) == -1:
65 return str
65 return str
66 else:
66 else:
67 str = str.replace(replace * 2, replace)
67 str = str.replace(replace * 2, replace)
68 return recursive_replace(str, replace)
68 return recursive_replace(str, replace)
69
69
70 class _ToolTip(object):
70 class _ToolTip(object):
71
71
72 def __call__(self, tooltip_title, trim_at=50):
72 def __call__(self, tooltip_title, trim_at=50):
73 """
73 """
74 Special function just to wrap our text into nice formatted autowrapped
74 Special function just to wrap our text into nice formatted autowrapped
75 text
75 text
76 @param tooltip_title:
76 @param tooltip_title:
77 """
77 """
78
78
79 return literal(wrap_paragraphs(tooltip_title, trim_at)\
79 return literal(wrap_paragraphs(tooltip_title, trim_at)\
80 .replace('\n', '<br/>'))
80 .replace('\n', '<br/>'))
81
81
82 def activate(self):
82 def activate(self):
83 """
83 """
84 Adds tooltip mechanism to the given Html all tooltips have to have
84 Adds tooltip mechanism to the given Html all tooltips have to have
85 set class tooltip and set attribute tooltip_title.
85 set class tooltip and set attribute tooltip_title.
86 Then a tooltip will be generated based on that
86 Then a tooltip will be generated based on that
87 All with yui js tooltip
87 All with yui js tooltip
88 """
88 """
89
89
90 js = '''
90 js = '''
91 YAHOO.util.Event.onDOMReady(function(){
91 YAHOO.util.Event.onDOMReady(function(){
92 function toolTipsId(){
92 function toolTipsId(){
93 var ids = [];
93 var ids = [];
94 var tts = YAHOO.util.Dom.getElementsByClassName('tooltip');
94 var tts = YAHOO.util.Dom.getElementsByClassName('tooltip');
95
95
96 for (var i = 0; i < tts.length; i++) {
96 for (var i = 0; i < tts.length; i++) {
97 //if element doesn not have and id autgenerate one for tooltip
97 //if element doesn not have and id autgenerate one for tooltip
98
98
99 if (!tts[i].id){
99 if (!tts[i].id){
100 tts[i].id='tt'+i*100;
100 tts[i].id='tt'+i*100;
101 }
101 }
102 ids.push(tts[i].id);
102 ids.push(tts[i].id);
103 }
103 }
104 return ids
104 return ids
105 };
105 };
106 var myToolTips = new YAHOO.widget.Tooltip("tooltip", {
106 var myToolTips = new YAHOO.widget.Tooltip("tooltip", {
107 context: toolTipsId(),
107 context: toolTipsId(),
108 monitorresize:false,
108 monitorresize:false,
109 xyoffset :[0,0],
109 xyoffset :[0,0],
110 autodismissdelay:300000,
110 autodismissdelay:300000,
111 hidedelay:5,
111 hidedelay:5,
112 showdelay:20,
112 showdelay:20,
113 });
113 });
114
114
115 //Mouse Over event disabled for new repositories since they dont
115 //Mouse Over event disabled for new repositories since they dont
116 //have last commit message
116 //have last commit message
117 myToolTips.contextMouseOverEvent.subscribe(
117 myToolTips.contextMouseOverEvent.subscribe(
118 function(type, args) {
118 function(type, args) {
119 var context = args[0];
119 var context = args[0];
120 var txt = context.getAttribute('tooltip_title');
120 var txt = context.getAttribute('tooltip_title');
121 if(txt){
121 if(txt){
122 return true;
122 return true;
123 }
123 }
124 else{
124 else{
125 return false;
125 return false;
126 }
126 }
127 });
127 });
128
128
129
129
130 // Set the text for the tooltip just before we display it. Lazy method
130 // Set the text for the tooltip just before we display it. Lazy method
131 myToolTips.contextTriggerEvent.subscribe(
131 myToolTips.contextTriggerEvent.subscribe(
132 function(type, args) {
132 function(type, args) {
133
133
134
134
135 var context = args[0];
135 var context = args[0];
136
136
137 var txt = context.getAttribute('tooltip_title');
137 var txt = context.getAttribute('tooltip_title');
138 this.cfg.setProperty("text", txt);
138 this.cfg.setProperty("text", txt);
139
139
140
140
141 // positioning of tooltip
141 // positioning of tooltip
142 var tt_w = this.element.clientWidth;
142 var tt_w = this.element.clientWidth;
143 var tt_h = this.element.clientHeight;
143 var tt_h = this.element.clientHeight;
144
144
145 var context_w = context.offsetWidth;
145 var context_w = context.offsetWidth;
146 var context_h = context.offsetHeight;
146 var context_h = context.offsetHeight;
147
147
148 var pos_x = YAHOO.util.Dom.getX(context);
148 var pos_x = YAHOO.util.Dom.getX(context);
149 var pos_y = YAHOO.util.Dom.getY(context);
149 var pos_y = YAHOO.util.Dom.getY(context);
150
150
151 var display_strategy = 'top';
151 var display_strategy = 'top';
152 var xy_pos = [0,0];
152 var xy_pos = [0,0];
153 switch (display_strategy){
153 switch (display_strategy){
154
154
155 case 'top':
155 case 'top':
156 var cur_x = (pos_x+context_w/2)-(tt_w/2);
156 var cur_x = (pos_x+context_w/2)-(tt_w/2);
157 var cur_y = pos_y-tt_h-4;
157 var cur_y = pos_y-tt_h-4;
158 xy_pos = [cur_x,cur_y];
158 xy_pos = [cur_x,cur_y];
159 break;
159 break;
160 case 'bottom':
160 case 'bottom':
161 var cur_x = (pos_x+context_w/2)-(tt_w/2);
161 var cur_x = (pos_x+context_w/2)-(tt_w/2);
162 var cur_y = pos_y+context_h+4;
162 var cur_y = pos_y+context_h+4;
163 xy_pos = [cur_x,cur_y];
163 xy_pos = [cur_x,cur_y];
164 break;
164 break;
165 case 'left':
165 case 'left':
166 var cur_x = (pos_x-tt_w-4);
166 var cur_x = (pos_x-tt_w-4);
167 var cur_y = pos_y-((tt_h/2)-context_h/2);
167 var cur_y = pos_y-((tt_h/2)-context_h/2);
168 xy_pos = [cur_x,cur_y];
168 xy_pos = [cur_x,cur_y];
169 break;
169 break;
170 case 'right':
170 case 'right':
171 var cur_x = (pos_x+context_w+4);
171 var cur_x = (pos_x+context_w+4);
172 var cur_y = pos_y-((tt_h/2)-context_h/2);
172 var cur_y = pos_y-((tt_h/2)-context_h/2);
173 xy_pos = [cur_x,cur_y];
173 xy_pos = [cur_x,cur_y];
174 break;
174 break;
175 default:
175 default:
176 var cur_x = (pos_x+context_w/2)-(tt_w/2);
176 var cur_x = (pos_x+context_w/2)-(tt_w/2);
177 var cur_y = pos_y-tt_h-4;
177 var cur_y = pos_y-tt_h-4;
178 xy_pos = [cur_x,cur_y];
178 xy_pos = [cur_x,cur_y];
179 break;
179 break;
180
180
181 }
181 }
182
182
183 this.cfg.setProperty("xy",xy_pos);
183 this.cfg.setProperty("xy",xy_pos);
184
184
185 });
185 });
186
186
187 //Mouse out
187 //Mouse out
188 myToolTips.contextMouseOutEvent.subscribe(
188 myToolTips.contextMouseOutEvent.subscribe(
189 function(type, args) {
189 function(type, args) {
190 var context = args[0];
190 var context = args[0];
191
191
192 });
192 });
193 });
193 });
194 '''
194 '''
195 return literal(js)
195 return literal(js)
196
196
197 tooltip = _ToolTip()
197 tooltip = _ToolTip()
198
198
199 class _FilesBreadCrumbs(object):
199 class _FilesBreadCrumbs(object):
200
200
201 def __call__(self, repo_name, rev, paths):
201 def __call__(self, repo_name, rev, paths):
202 url_l = [link_to(repo_name, url('files_home',
202 url_l = [link_to(repo_name, url('files_home',
203 repo_name=repo_name,
203 repo_name=repo_name,
204 revision=rev, f_path=''))]
204 revision=rev, f_path=''))]
205 paths_l = paths.split('/')
205 paths_l = paths.split('/')
206
206
207 for cnt, p in enumerate(paths_l, 1):
207 for cnt, p in enumerate(paths_l, 1):
208 if p != '':
208 if p != '':
209 url_l.append(link_to(p, url('files_home',
209 url_l.append(link_to(p, url('files_home',
210 repo_name=repo_name,
210 repo_name=repo_name,
211 revision=rev,
211 revision=rev,
212 f_path='/'.join(paths_l[:cnt]))))
212 f_path='/'.join(paths_l[:cnt]))))
213
213
214 return literal(' / '.join(url_l))
214 return literal(' / '.join(url_l))
215
215
216 files_breadcrumbs = _FilesBreadCrumbs()
216 files_breadcrumbs = _FilesBreadCrumbs()
217
217
218 def pygmentize(filenode, **kwargs):
218 def pygmentize(filenode, **kwargs):
219 """
219 """
220 pygmentize function using pygments
220 pygmentize function using pygments
221 @param filenode:
221 @param filenode:
222 """
222 """
223 return literal(code_highlight(filenode.content, filenode.lexer, HtmlFormatter(**kwargs)))
223 return literal(code_highlight(filenode.content,
224 filenode.lexer, HtmlFormatter(**kwargs)))
224
225
225 def pygmentize_annotation(filenode, **kwargs):
226 def pygmentize_annotation(filenode, **kwargs):
226 """
227 """
227 pygmentize function for annotation
228 pygmentize function for annotation
228 @param filenode:
229 @param filenode:
229 """
230 """
230
231
231 color_dict = g.changeset_annotation_colors
232 color_dict = g.changeset_annotation_colors
232 def gen_color():
233 def gen_color():
233 import random
234 import random
234 return [str(random.randrange(10, 235)) for _ in xrange(3)]
235 return [str(random.randrange(10, 235)) for _ in xrange(3)]
235 def get_color_string(cs):
236 def get_color_string(cs):
236 if color_dict.has_key(cs):
237 if color_dict.has_key(cs):
237 col = color_dict[cs]
238 col = color_dict[cs]
238 else:
239 else:
239 color_dict[cs] = gen_color()
240 color_dict[cs] = gen_color()
240 col = color_dict[cs]
241 col = color_dict[cs]
241 return "color: rgb(%s) ! important;" % (', '.join(col))
242 return "color: rgb(%s) ! important;" % (', '.join(col))
242
243
243 def url_func(changeset):
244 def url_func(changeset):
244 tooltip_html = "<div style='font-size:0.8em'><b>Author:</b> %s<br/><b>Date:</b> %s</b><br/><b>Message:</b> %s<br/></div>"
245 tooltip_html = "<div style='font-size:0.8em'><b>Author:</b>"+\
246 " %s<br/><b>Date:</b> %s</b><br/><b>Message:</b> %s<br/></div>"
245
247
246 tooltip_html = tooltip_html % (changeset.author,
248 tooltip_html = tooltip_html % (changeset.author,
247 changeset.date,
249 changeset.date,
248 tooltip(changeset.message))
250 tooltip(changeset.message))
249 lnk_format = 'r%s:%s' % (changeset.revision,
251 lnk_format = 'r%s:%s' % (changeset.revision,
250 changeset.raw_id)
252 changeset.raw_id)
251 uri = link_to(
253 uri = link_to(
252 lnk_format,
254 lnk_format,
253 url('changeset_home', repo_name='test',
255 url('changeset_home', repo_name=changeset.repository.name,
254 revision=changeset.raw_id),
256 revision=changeset.raw_id),
255 style=get_color_string(changeset.raw_id),
257 style=get_color_string(changeset.raw_id),
256 class_='tooltip',
258 class_='tooltip',
257 tooltip_title=tooltip_html
259 tooltip_title=tooltip_html
258 )
260 )
259
261
260 uri += '\n'
262 uri += '\n'
261 return uri
263 return uri
262 return literal(annotate_highlight(filenode, url_func, **kwargs))
264 return literal(annotate_highlight(filenode, url_func, **kwargs))
263
265
264 def repo_name_slug(value):
266 def repo_name_slug(value):
265 """
267 """
266 Return slug of name of repository
268 Return slug of name of repository
267 """
269 """
268 slug = urlify(value)
270 slug = urlify(value)
269 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|:""":
271 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|:""":
270 slug = slug.replace(c, '-')
272 slug = slug.replace(c, '-')
271 slug = recursive_replace(slug, '-')
273 slug = recursive_replace(slug, '-')
272 return slug
274 return slug
273
275
274 flash = _Flash()
276 flash = _Flash()
275
277
276
278
277 #===============================================================================
279 #===============================================================================
278 # MERCURIAL FILTERS available via h.
280 # MERCURIAL FILTERS available via h.
279 #===============================================================================
281 #===============================================================================
280 from mercurial import util
282 from mercurial import util
281 from mercurial.templatefilters import age as _age, person as _person
283 from mercurial.templatefilters import age as _age, person as _person
282
284
283 age = lambda x:_age(x)
285 age = lambda x:_age(x)
284 capitalize = lambda x: x.capitalize()
286 capitalize = lambda x: x.capitalize()
285 date = lambda x: util.datestr(x)
287 date = lambda x: util.datestr(x)
286 email = util.email
288 email = util.email
287 email_or_none = lambda x: util.email(x) if util.email(x) != x else None
289 email_or_none = lambda x: util.email(x) if util.email(x) != x else None
288 person = lambda x: _person(x)
290 person = lambda x: _person(x)
289 hgdate = lambda x: "%d %d" % x
291 hgdate = lambda x: "%d %d" % x
290 isodate = lambda x: util.datestr(x, '%Y-%m-%d %H:%M %1%2')
292 isodate = lambda x: util.datestr(x, '%Y-%m-%d %H:%M %1%2')
291 isodatesec = lambda x: util.datestr(x, '%Y-%m-%d %H:%M:%S %1%2')
293 isodatesec = lambda x: util.datestr(x, '%Y-%m-%d %H:%M:%S %1%2')
292 localdate = lambda x: (x[0], util.makedate()[1])
294 localdate = lambda x: (x[0], util.makedate()[1])
293 rfc822date = lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S %1%2")
295 rfc822date = lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S %1%2")
294 rfc3339date = lambda x: util.datestr(x, "%Y-%m-%dT%H:%M:%S%1:%2")
296 rfc3339date = lambda x: util.datestr(x, "%Y-%m-%dT%H:%M:%S%1:%2")
295 time_ago = lambda x: util.datestr(_age(x), "%a, %d %b %Y %H:%M:%S %1%2")
297 time_ago = lambda x: util.datestr(_age(x), "%a, %d %b %Y %H:%M:%S %1%2")
296
298
297
299
298 #===============================================================================
300 #===============================================================================
299 # PERMS
301 # PERMS
300 #===============================================================================
302 #===============================================================================
301 from pylons_app.lib.auth import HasPermissionAny, HasPermissionAll, \
303 from pylons_app.lib.auth import HasPermissionAny, HasPermissionAll, \
302 HasRepoPermissionAny, HasRepoPermissionAll
304 HasRepoPermissionAny, HasRepoPermissionAll
303
305
304 #===============================================================================
306 #===============================================================================
305 # GRAVATAR URL
307 # GRAVATAR URL
306 #===============================================================================
308 #===============================================================================
307 import hashlib
309 import hashlib
308 import urllib
310 import urllib
309 from pylons import request
311 from pylons import request
310
312
311 def gravatar_url(email_address, size=30):
313 def gravatar_url(email_address, size=30):
312 ssl_enabled = 'https' == request.environ.get('HTTP_X_URL_SCHEME')
314 ssl_enabled = 'https' == request.environ.get('HTTP_X_URL_SCHEME')
313 default = 'identicon'
315 default = 'identicon'
314 baseurl_nossl = "http://www.gravatar.com/avatar/"
316 baseurl_nossl = "http://www.gravatar.com/avatar/"
315 baseurl_ssl = "https://secure.gravatar.com/avatar/"
317 baseurl_ssl = "https://secure.gravatar.com/avatar/"
316 baseurl = baseurl_ssl if ssl_enabled else baseurl_nossl
318 baseurl = baseurl_ssl if ssl_enabled else baseurl_nossl
317
319
318
320
319 # construct the url
321 # construct the url
320 gravatar_url = baseurl + hashlib.md5(email_address.lower()).hexdigest() + "?"
322 gravatar_url = baseurl + hashlib.md5(email_address.lower()).hexdigest() + "?"
321 gravatar_url += urllib.urlencode({'d':default, 's':str(size)})
323 gravatar_url += urllib.urlencode({'d':default, 's':str(size)})
322
324
323 return gravatar_url
325 return gravatar_url
@@ -1,117 +1,117 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2
2
3 <%inherit file="/base/base.html"/>
3 <%inherit file="/base/base.html"/>
4
4
5 <%def name="title()">
5 <%def name="title()">
6 ${_('Changelog - %s') % c.repo_name}
6 ${_('Changelog - %s') % c.repo_name}
7 </%def>
7 </%def>
8
8
9 <%def name="breadcrumbs_links()">
9 <%def name="breadcrumbs_links()">
10 ${h.link_to(u'Home',h.url('/'))}
10 ${h.link_to(u'Home',h.url('/'))}
11 &raquo;
11 &raquo;
12 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
12 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
13 &raquo;
13 &raquo;
14 ${_('Changelog')} - ${_('showing ')} ${c.size if c.size <= c.total_cs else c.total_cs} ${_('out of')} ${c.total_cs} ${_('revisions')}
14 ${_('Changelog')} - ${_('showing ')} ${c.size if c.size <= c.total_cs else c.total_cs} ${_('out of')} ${c.total_cs} ${_('revisions')}
15 </%def>
15 </%def>
16
16
17 <%def name="page_nav()">
17 <%def name="page_nav()">
18 ${self.menu('changelog')}
18 ${self.menu('changelog')}
19 </%def>
19 </%def>
20
20
21 <%def name="main()">
21 <%def name="main()">
22 <div class="box">
22 <div class="box">
23 <!-- box / title -->
23 <!-- box / title -->
24 <div class="title">
24 <div class="title">
25 ${self.breadcrumbs()}
25 ${self.breadcrumbs()}
26 </div>
26 </div>
27 <div class="table">
27 <div class="table">
28 % if c.pagination:
28 % if c.pagination:
29 <div id="graph">
29 <div id="graph">
30 <div id="graph_nodes">
30 <div id="graph_nodes">
31 <canvas id="graph_canvas"></canvas>
31 <canvas id="graph_canvas"></canvas>
32 </div>
32 </div>
33 <div id="graph_content">
33 <div id="graph_content">
34 <div class="container_header">
34 <div class="container_header">
35
35
36 ${h.form(h.url.current(),method='get')}
36 ${h.form(h.url.current(),method='get')}
37 <div class="info_box">
37 <div class="info_box">
38 <span>${_('Show')}:</span>
38 <span>${_('Show')}:</span>
39 ${h.text('size',size=1,value=c.size)}
39 ${h.text('size',size=1,value=c.size)}
40 <span>${_('revisions')}</span>
40 <span>${_('revisions')}</span>
41 ${h.submit('set',_('set'))}
41 ${h.submit('set',_('set'))}
42 </div>
42 </div>
43 ${h.end_form()}
43 ${h.end_form()}
44
44
45 </div>
45 </div>
46 %for cnt,cs in enumerate(c.pagination):
46 %for cnt,cs in enumerate(c.pagination):
47 <div id="chg_${cnt+1}" class="container">
47 <div id="chg_${cnt+1}" class="container">
48 <div class="left">
48 <div class="left">
49 <div class="date">${_('commit')} ${cs.revision}: ${cs.raw_id}@${cs.date}</div>
49 <div class="date">${_('commit')} ${cs.revision}: ${cs.raw_id}@${cs.date}</div>
50 <span class="logtags">
50 <span class="logtags">
51 <span class="branchtag">${cs.branch}</span>
51 <span class="branchtag">${cs.branch}</span>
52 %for tag in cs.tags:
52 %for tag in cs.tags:
53 <span class="tagtag">${tag}</span>
53 <span class="tagtag">${tag}</span>
54 %endfor
54 %endfor
55 </span>
55 </span>
56 <div class="author">
56 <div class="author">
57 <div class="gravatar">
57 <div class="gravatar">
58 <img alt="gravatar" src="${h.gravatar_url(h.email(cs.author),20)}"/>
58 <img alt="gravatar" src="${h.gravatar_url(h.email(cs.author),20)}"/>
59 </div>
59 </div>
60 <span>${h.person(cs.author)}</span><br/>
60 <span>${h.person(cs.author)}</span><br/>
61 <span><a href="mailto:${h.email_or_none(cs.author)}">${h.email_or_none(cs.author)}</a></span><br/>
61 <span><a href="mailto:${h.email_or_none(cs.author)}">${h.email_or_none(cs.author)}</a></span><br/>
62 </div>
62 </div>
63 <div class="message">
63 <div class="message">
64 ${h.link_to(h.wrap_paragraphs(cs.message.decode('utf-8','replace')),
64 ${h.link_to(h.wrap_paragraphs(cs.message),
65 h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}
65 h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}
66 </div>
66 </div>
67 </div>
67 </div>
68 <div class="right">
68 <div class="right">
69 <div class="changes">
69 <div class="changes">
70 <span class="removed" title="${_('removed')}">${len(cs.removed)}</span>
70 <span class="removed" title="${_('removed')}">${len(cs.removed)}</span>
71 <span class="changed" title="${_('changed')}">${len(cs.changed)}</span>
71 <span class="changed" title="${_('changed')}">${len(cs.changed)}</span>
72 <span class="added" title="${_('added')}">${len(cs.added)}</span>
72 <span class="added" title="${_('added')}">${len(cs.added)}</span>
73 </div>
73 </div>
74 %if len(cs.parents)>1:
74 %if len(cs.parents)>1:
75 <div class="merge">
75 <div class="merge">
76 ${_('merge')}<img alt="merge" src="/images/icons/arrow_join.png"/>
76 ${_('merge')}<img alt="merge" src="/images/icons/arrow_join.png"/>
77 </div>
77 </div>
78 %endif
78 %endif
79 %for p_cs in reversed(cs.parents):
79 %for p_cs in reversed(cs.parents):
80 <div class="parent">${_('Parent')} ${p_cs.revision}: ${h.link_to(p_cs.raw_id,
80 <div class="parent">${_('Parent')} ${p_cs.revision}: ${h.link_to(p_cs.raw_id,
81 h.url('changeset_home',repo_name=c.repo_name,revision=p_cs.raw_id),title=p_cs.message.decode('utf-8','replace'))}
81 h.url('changeset_home',repo_name=c.repo_name,revision=p_cs.raw_id),title=p_cs.message)}
82 </div>
82 </div>
83 %endfor
83 %endfor
84 </div>
84 </div>
85 </div>
85 </div>
86
86
87 %endfor
87 %endfor
88 <div class="pagination-wh pagination-left">
88 <div class="pagination-wh pagination-left">
89 ${c.pagination.pager('$link_previous ~2~ $link_next')}
89 ${c.pagination.pager('$link_previous ~2~ $link_next')}
90 </div>
90 </div>
91 </div>
91 </div>
92 </div>
92 </div>
93
93
94 <script type="text/javascript" src="/js/graph.js"></script>
94 <script type="text/javascript" src="/js/graph.js"></script>
95 <script type="text/javascript">
95 <script type="text/javascript">
96 YAHOO.util.Event.onDOMReady(function(){
96 YAHOO.util.Event.onDOMReady(function(){
97 function set_canvas() {
97 function set_canvas() {
98 var c = document.getElementById('graph_nodes');
98 var c = document.getElementById('graph_nodes');
99 var t = document.getElementById('graph_content');
99 var t = document.getElementById('graph_content');
100 canvas = document.getElementById('graph_canvas');
100 canvas = document.getElementById('graph_canvas');
101 var div_h = t.clientHeight;
101 var div_h = t.clientHeight;
102 c.style.height=div_h+'px';
102 c.style.height=div_h+'px';
103 canvas.setAttribute('height',div_h);
103 canvas.setAttribute('height',div_h);
104 canvas.setAttribute('width',160);
104 canvas.setAttribute('width',160);
105 };
105 };
106 set_canvas();
106 set_canvas();
107 var jsdata = ${c.jsdata|n};
107 var jsdata = ${c.jsdata|n};
108 var r = new BranchRenderer();
108 var r = new BranchRenderer();
109 r.render(jsdata);
109 r.render(jsdata);
110 });
110 });
111 </script>
111 </script>
112 %else:
112 %else:
113 ${_('There are no changes yet')}
113 ${_('There are no changes yet')}
114 %endif
114 %endif
115 </div>
115 </div>
116 </div>
116 </div>
117 </%def> No newline at end of file
117 </%def>
General Comments 0
You need to be logged in to leave comments. Login now