##// END OF EJS Templates
fixed unicode problems with file paths....
marcink -
r1087:51076a2a beta
parent child Browse files
Show More
@@ -1,593 +1,602
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 import random
6 import random
7 import hashlib
7 import hashlib
8 import StringIO
8 import StringIO
9 from pygments.formatters import HtmlFormatter
9 from pygments.formatters import HtmlFormatter
10 from pygments import highlight as code_highlight
10 from pygments import highlight as code_highlight
11 from pylons import url
11 from pylons import url
12 from pylons.i18n.translation import _, ungettext
12 from pylons.i18n.translation import _, ungettext
13 from vcs.utils.annotate import annotate_highlight
13 from vcs.utils.annotate import annotate_highlight
14 from rhodecode.lib.utils import repo_name_slug
14 from rhodecode.lib.utils import repo_name_slug
15
15
16 from webhelpers.html import literal, HTML, escape
16 from webhelpers.html import literal, HTML, escape
17 from webhelpers.html.tools import *
17 from webhelpers.html.tools import *
18 from webhelpers.html.builder import make_tag
18 from webhelpers.html.builder import make_tag
19 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
19 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
20 end_form, file, form, hidden, image, javascript_link, link_to, link_to_if, \
20 end_form, file, form, hidden, image, javascript_link, link_to, link_to_if, \
21 link_to_unless, ol, required_legend, select, stylesheet_link, submit, text, \
21 link_to_unless, ol, required_legend, select, stylesheet_link, submit, text, \
22 password, textarea, title, ul, xml_declaration, radio
22 password, textarea, title, ul, xml_declaration, radio
23 from webhelpers.html.tools import auto_link, button_to, highlight, js_obfuscate, \
23 from webhelpers.html.tools import auto_link, button_to, highlight, js_obfuscate, \
24 mail_to, strip_links, strip_tags, tag_re
24 mail_to, strip_links, strip_tags, tag_re
25 from webhelpers.number import format_byte_size, format_bit_size
25 from webhelpers.number import format_byte_size, format_bit_size
26 from webhelpers.pylonslib import Flash as _Flash
26 from webhelpers.pylonslib import Flash as _Flash
27 from webhelpers.pylonslib.secure_form import secure_form
27 from webhelpers.pylonslib.secure_form import secure_form
28 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
28 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
29 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
29 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
30 replace_whitespace, urlify, truncate, wrap_paragraphs
30 replace_whitespace, urlify, truncate, wrap_paragraphs
31 from webhelpers.date import time_ago_in_words
31 from webhelpers.date import time_ago_in_words
32
32
33 from webhelpers.html.tags import _set_input_attrs, _set_id_attr, \
33 from webhelpers.html.tags import _set_input_attrs, _set_id_attr, \
34 convert_boolean_attrs, NotGiven
34 convert_boolean_attrs, NotGiven
35
35
36 def _reset(name, value=None, id=NotGiven, type="reset", **attrs):
36 def _reset(name, value=None, id=NotGiven, type="reset", **attrs):
37 """Reset button
37 """Reset button
38 """
38 """
39 _set_input_attrs(attrs, type, name, value)
39 _set_input_attrs(attrs, type, name, value)
40 _set_id_attr(attrs, id, name)
40 _set_id_attr(attrs, id, name)
41 convert_boolean_attrs(attrs, ["disabled"])
41 convert_boolean_attrs(attrs, ["disabled"])
42 return HTML.input(**attrs)
42 return HTML.input(**attrs)
43
43
44 reset = _reset
44 reset = _reset
45
45
46
46
47 def get_token():
47 def get_token():
48 """Return the current authentication token, creating one if one doesn't
48 """Return the current authentication token, creating one if one doesn't
49 already exist.
49 already exist.
50 """
50 """
51 token_key = "_authentication_token"
51 token_key = "_authentication_token"
52 from pylons import session
52 from pylons import session
53 if not token_key in session:
53 if not token_key in session:
54 try:
54 try:
55 token = hashlib.sha1(str(random.getrandbits(128))).hexdigest()
55 token = hashlib.sha1(str(random.getrandbits(128))).hexdigest()
56 except AttributeError: # Python < 2.4
56 except AttributeError: # Python < 2.4
57 token = hashlib.sha1(str(random.randrange(2 ** 128))).hexdigest()
57 token = hashlib.sha1(str(random.randrange(2 ** 128))).hexdigest()
58 session[token_key] = token
58 session[token_key] = token
59 if hasattr(session, 'save'):
59 if hasattr(session, 'save'):
60 session.save()
60 session.save()
61 return session[token_key]
61 return session[token_key]
62
62
63 class _GetError(object):
63 class _GetError(object):
64 """Get error from form_errors, and represent it as span wrapped error
64 """Get error from form_errors, and represent it as span wrapped error
65 message
65 message
66
66
67 :param field_name: field to fetch errors for
67 :param field_name: field to fetch errors for
68 :param form_errors: form errors dict
68 :param form_errors: form errors dict
69 """
69 """
70
70
71 def __call__(self, field_name, form_errors):
71 def __call__(self, field_name, form_errors):
72 tmpl = """<span class="error_msg">%s</span>"""
72 tmpl = """<span class="error_msg">%s</span>"""
73 if form_errors and form_errors.has_key(field_name):
73 if form_errors and form_errors.has_key(field_name):
74 return literal(tmpl % form_errors.get(field_name))
74 return literal(tmpl % form_errors.get(field_name))
75
75
76 get_error = _GetError()
76 get_error = _GetError()
77
77
78 class _ToolTip(object):
78 class _ToolTip(object):
79
79
80 def __call__(self, tooltip_title, trim_at=50):
80 def __call__(self, tooltip_title, trim_at=50):
81 """Special function just to wrap our text into nice formatted
81 """Special function just to wrap our text into nice formatted
82 autowrapped text
82 autowrapped text
83
83
84 :param tooltip_title:
84 :param tooltip_title:
85 """
85 """
86
86
87 return wrap_paragraphs(escape(tooltip_title), trim_at)\
87 return wrap_paragraphs(escape(tooltip_title), trim_at)\
88 .replace('\n', '<br/>')
88 .replace('\n', '<br/>')
89
89
90 def activate(self):
90 def activate(self):
91 """Adds tooltip mechanism to the given Html all tooltips have to have
91 """Adds tooltip mechanism to the given Html all tooltips have to have
92 set class `tooltip` and set attribute `tooltip_title`.
92 set class `tooltip` and set attribute `tooltip_title`.
93 Then a tooltip will be generated based on that. All with yui js tooltip
93 Then a tooltip will be generated based on that. All with yui js tooltip
94 """
94 """
95
95
96 js = '''
96 js = '''
97 YAHOO.util.Event.onDOMReady(function(){
97 YAHOO.util.Event.onDOMReady(function(){
98 function toolTipsId(){
98 function toolTipsId(){
99 var ids = [];
99 var ids = [];
100 var tts = YAHOO.util.Dom.getElementsByClassName('tooltip');
100 var tts = YAHOO.util.Dom.getElementsByClassName('tooltip');
101
101
102 for (var i = 0; i < tts.length; i++) {
102 for (var i = 0; i < tts.length; i++) {
103 //if element doesn't not have and id autogenerate one for tooltip
103 //if element doesn't not have and id autogenerate one for tooltip
104
104
105 if (!tts[i].id){
105 if (!tts[i].id){
106 tts[i].id='tt'+i*100;
106 tts[i].id='tt'+i*100;
107 }
107 }
108 ids.push(tts[i].id);
108 ids.push(tts[i].id);
109 }
109 }
110 return ids
110 return ids
111 };
111 };
112 var myToolTips = new YAHOO.widget.Tooltip("tooltip", {
112 var myToolTips = new YAHOO.widget.Tooltip("tooltip", {
113 context: toolTipsId(),
113 context: toolTipsId(),
114 monitorresize:false,
114 monitorresize:false,
115 xyoffset :[0,0],
115 xyoffset :[0,0],
116 autodismissdelay:300000,
116 autodismissdelay:300000,
117 hidedelay:5,
117 hidedelay:5,
118 showdelay:20,
118 showdelay:20,
119 });
119 });
120
120
121 // Set the text for the tooltip just before we display it. Lazy method
121 // Set the text for the tooltip just before we display it. Lazy method
122 myToolTips.contextTriggerEvent.subscribe(
122 myToolTips.contextTriggerEvent.subscribe(
123 function(type, args) {
123 function(type, args) {
124
124
125 var context = args[0];
125 var context = args[0];
126
126
127 //positioning of tooltip
127 //positioning of tooltip
128 var tt_w = this.element.clientWidth;//tooltip width
128 var tt_w = this.element.clientWidth;//tooltip width
129 var tt_h = this.element.clientHeight;//tooltip height
129 var tt_h = this.element.clientHeight;//tooltip height
130
130
131 var context_w = context.offsetWidth;
131 var context_w = context.offsetWidth;
132 var context_h = context.offsetHeight;
132 var context_h = context.offsetHeight;
133
133
134 var pos_x = YAHOO.util.Dom.getX(context);
134 var pos_x = YAHOO.util.Dom.getX(context);
135 var pos_y = YAHOO.util.Dom.getY(context);
135 var pos_y = YAHOO.util.Dom.getY(context);
136
136
137 var display_strategy = 'right';
137 var display_strategy = 'right';
138 var xy_pos = [0,0];
138 var xy_pos = [0,0];
139 switch (display_strategy){
139 switch (display_strategy){
140
140
141 case 'top':
141 case 'top':
142 var cur_x = (pos_x+context_w/2)-(tt_w/2);
142 var cur_x = (pos_x+context_w/2)-(tt_w/2);
143 var cur_y = (pos_y-tt_h-4);
143 var cur_y = (pos_y-tt_h-4);
144 xy_pos = [cur_x,cur_y];
144 xy_pos = [cur_x,cur_y];
145 break;
145 break;
146 case 'bottom':
146 case 'bottom':
147 var cur_x = (pos_x+context_w/2)-(tt_w/2);
147 var cur_x = (pos_x+context_w/2)-(tt_w/2);
148 var cur_y = pos_y+context_h+4;
148 var cur_y = pos_y+context_h+4;
149 xy_pos = [cur_x,cur_y];
149 xy_pos = [cur_x,cur_y];
150 break;
150 break;
151 case 'left':
151 case 'left':
152 var cur_x = (pos_x-tt_w-4);
152 var cur_x = (pos_x-tt_w-4);
153 var cur_y = pos_y-((tt_h/2)-context_h/2);
153 var cur_y = pos_y-((tt_h/2)-context_h/2);
154 xy_pos = [cur_x,cur_y];
154 xy_pos = [cur_x,cur_y];
155 break;
155 break;
156 case 'right':
156 case 'right':
157 var cur_x = (pos_x+context_w+4);
157 var cur_x = (pos_x+context_w+4);
158 var cur_y = pos_y-((tt_h/2)-context_h/2);
158 var cur_y = pos_y-((tt_h/2)-context_h/2);
159 xy_pos = [cur_x,cur_y];
159 xy_pos = [cur_x,cur_y];
160 break;
160 break;
161 default:
161 default:
162 var cur_x = (pos_x+context_w/2)-(tt_w/2);
162 var cur_x = (pos_x+context_w/2)-(tt_w/2);
163 var cur_y = pos_y-tt_h-4;
163 var cur_y = pos_y-tt_h-4;
164 xy_pos = [cur_x,cur_y];
164 xy_pos = [cur_x,cur_y];
165 break;
165 break;
166
166
167 }
167 }
168
168
169 this.cfg.setProperty("xy",xy_pos);
169 this.cfg.setProperty("xy",xy_pos);
170
170
171 });
171 });
172
172
173 //Mouse out
173 //Mouse out
174 myToolTips.contextMouseOutEvent.subscribe(
174 myToolTips.contextMouseOutEvent.subscribe(
175 function(type, args) {
175 function(type, args) {
176 var context = args[0];
176 var context = args[0];
177
177
178 });
178 });
179 });
179 });
180 '''
180 '''
181 return literal(js)
181 return literal(js)
182
182
183 tooltip = _ToolTip()
183 tooltip = _ToolTip()
184
184
185 class _FilesBreadCrumbs(object):
185 class _FilesBreadCrumbs(object):
186
186
187 def __call__(self, repo_name, rev, paths):
187 def __call__(self, repo_name, rev, paths):
188 if isinstance(paths, str):
188 if isinstance(paths, str):
189 paths = paths.decode('utf-8')
189 paths = paths.decode('utf-8')
190 url_l = [link_to(repo_name, url('files_home',
190 url_l = [link_to(repo_name, url('files_home',
191 repo_name=repo_name,
191 repo_name=repo_name,
192 revision=rev, f_path=''))]
192 revision=rev, f_path=''))]
193 paths_l = paths.split('/')
193 paths_l = paths.split('/')
194 for cnt, p in enumerate(paths_l):
194 for cnt, p in enumerate(paths_l):
195 if p != '':
195 if p != '':
196 url_l.append(link_to(p, url('files_home',
196 url_l.append(link_to(p, url('files_home',
197 repo_name=repo_name,
197 repo_name=repo_name,
198 revision=rev,
198 revision=rev,
199 f_path='/'.join(paths_l[:cnt + 1]))))
199 f_path='/'.join(paths_l[:cnt + 1]))))
200
200
201 return literal('/'.join(url_l))
201 return literal('/'.join(url_l))
202
202
203 files_breadcrumbs = _FilesBreadCrumbs()
203 files_breadcrumbs = _FilesBreadCrumbs()
204
204
205 class CodeHtmlFormatter(HtmlFormatter):
205 class CodeHtmlFormatter(HtmlFormatter):
206 """My code Html Formatter for source codes
206 """My code Html Formatter for source codes
207 """
207 """
208
208
209 def wrap(self, source, outfile):
209 def wrap(self, source, outfile):
210 return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
210 return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
211
211
212 def _wrap_code(self, source):
212 def _wrap_code(self, source):
213 for cnt, it in enumerate(source):
213 for cnt, it in enumerate(source):
214 i, t = it
214 i, t = it
215 t = '<div id="L%s">%s</div>' % (cnt + 1, t)
215 t = '<div id="L%s">%s</div>' % (cnt + 1, t)
216 yield i, t
216 yield i, t
217
217
218 def _wrap_tablelinenos(self, inner):
218 def _wrap_tablelinenos(self, inner):
219 dummyoutfile = StringIO.StringIO()
219 dummyoutfile = StringIO.StringIO()
220 lncount = 0
220 lncount = 0
221 for t, line in inner:
221 for t, line in inner:
222 if t:
222 if t:
223 lncount += 1
223 lncount += 1
224 dummyoutfile.write(line)
224 dummyoutfile.write(line)
225
225
226 fl = self.linenostart
226 fl = self.linenostart
227 mw = len(str(lncount + fl - 1))
227 mw = len(str(lncount + fl - 1))
228 sp = self.linenospecial
228 sp = self.linenospecial
229 st = self.linenostep
229 st = self.linenostep
230 la = self.lineanchors
230 la = self.lineanchors
231 aln = self.anchorlinenos
231 aln = self.anchorlinenos
232 nocls = self.noclasses
232 nocls = self.noclasses
233 if sp:
233 if sp:
234 lines = []
234 lines = []
235
235
236 for i in range(fl, fl + lncount):
236 for i in range(fl, fl + lncount):
237 if i % st == 0:
237 if i % st == 0:
238 if i % sp == 0:
238 if i % sp == 0:
239 if aln:
239 if aln:
240 lines.append('<a href="#%s%d" class="special">%*d</a>' %
240 lines.append('<a href="#%s%d" class="special">%*d</a>' %
241 (la, i, mw, i))
241 (la, i, mw, i))
242 else:
242 else:
243 lines.append('<span class="special">%*d</span>' % (mw, i))
243 lines.append('<span class="special">%*d</span>' % (mw, i))
244 else:
244 else:
245 if aln:
245 if aln:
246 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
246 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
247 else:
247 else:
248 lines.append('%*d' % (mw, i))
248 lines.append('%*d' % (mw, i))
249 else:
249 else:
250 lines.append('')
250 lines.append('')
251 ls = '\n'.join(lines)
251 ls = '\n'.join(lines)
252 else:
252 else:
253 lines = []
253 lines = []
254 for i in range(fl, fl + lncount):
254 for i in range(fl, fl + lncount):
255 if i % st == 0:
255 if i % st == 0:
256 if aln:
256 if aln:
257 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
257 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
258 else:
258 else:
259 lines.append('%*d' % (mw, i))
259 lines.append('%*d' % (mw, i))
260 else:
260 else:
261 lines.append('')
261 lines.append('')
262 ls = '\n'.join(lines)
262 ls = '\n'.join(lines)
263
263
264 # in case you wonder about the seemingly redundant <div> here: since the
264 # in case you wonder about the seemingly redundant <div> here: since the
265 # content in the other cell also is wrapped in a div, some browsers in
265 # content in the other cell also is wrapped in a div, some browsers in
266 # some configurations seem to mess up the formatting...
266 # some configurations seem to mess up the formatting...
267 if nocls:
267 if nocls:
268 yield 0, ('<table class="%stable">' % self.cssclass +
268 yield 0, ('<table class="%stable">' % self.cssclass +
269 '<tr><td><div class="linenodiv" '
269 '<tr><td><div class="linenodiv" '
270 'style="background-color: #f0f0f0; padding-right: 10px">'
270 'style="background-color: #f0f0f0; padding-right: 10px">'
271 '<pre style="line-height: 125%">' +
271 '<pre style="line-height: 125%">' +
272 ls + '</pre></div></td><td class="code">')
272 ls + '</pre></div></td><td class="code">')
273 else:
273 else:
274 yield 0, ('<table class="%stable">' % self.cssclass +
274 yield 0, ('<table class="%stable">' % self.cssclass +
275 '<tr><td class="linenos"><div class="linenodiv"><pre>' +
275 '<tr><td class="linenos"><div class="linenodiv"><pre>' +
276 ls + '</pre></div></td><td class="code">')
276 ls + '</pre></div></td><td class="code">')
277 yield 0, dummyoutfile.getvalue()
277 yield 0, dummyoutfile.getvalue()
278 yield 0, '</td></tr></table>'
278 yield 0, '</td></tr></table>'
279
279
280
280
281 def pygmentize(filenode, **kwargs):
281 def pygmentize(filenode, **kwargs):
282 """pygmentize function using pygments
282 """pygmentize function using pygments
283
283
284 :param filenode:
284 :param filenode:
285 """
285 """
286
286
287 return literal(code_highlight(filenode.content,
287 return literal(code_highlight(filenode.content,
288 filenode.lexer, CodeHtmlFormatter(**kwargs)))
288 filenode.lexer, CodeHtmlFormatter(**kwargs)))
289
289
290 def pygmentize_annotation(filenode, **kwargs):
290 def pygmentize_annotation(filenode, **kwargs):
291 """pygmentize function for annotation
291 """pygmentize function for annotation
292
292
293 :param filenode:
293 :param filenode:
294 """
294 """
295
295
296 color_dict = {}
296 color_dict = {}
297 def gen_color(n=10000):
297 def gen_color(n=10000):
298 """generator for getting n of evenly distributed colors using
298 """generator for getting n of evenly distributed colors using
299 hsv color and golden ratio. It always return same order of colors
299 hsv color and golden ratio. It always return same order of colors
300
300
301 :returns: RGB tuple
301 :returns: RGB tuple
302 """
302 """
303 import colorsys
303 import colorsys
304 golden_ratio = 0.618033988749895
304 golden_ratio = 0.618033988749895
305 h = 0.22717784590367374
305 h = 0.22717784590367374
306
306
307 for c in xrange(n):
307 for c in xrange(n):
308 h += golden_ratio
308 h += golden_ratio
309 h %= 1
309 h %= 1
310 HSV_tuple = [h, 0.95, 0.95]
310 HSV_tuple = [h, 0.95, 0.95]
311 RGB_tuple = colorsys.hsv_to_rgb(*HSV_tuple)
311 RGB_tuple = colorsys.hsv_to_rgb(*HSV_tuple)
312 yield map(lambda x:str(int(x * 256)), RGB_tuple)
312 yield map(lambda x:str(int(x * 256)), RGB_tuple)
313
313
314 cgenerator = gen_color()
314 cgenerator = gen_color()
315
315
316 def get_color_string(cs):
316 def get_color_string(cs):
317 if color_dict.has_key(cs):
317 if color_dict.has_key(cs):
318 col = color_dict[cs]
318 col = color_dict[cs]
319 else:
319 else:
320 col = color_dict[cs] = cgenerator.next()
320 col = color_dict[cs] = cgenerator.next()
321 return "color: rgb(%s)! important;" % (', '.join(col))
321 return "color: rgb(%s)! important;" % (', '.join(col))
322
322
323 def url_func(changeset):
323 def url_func(changeset):
324 tooltip_html = "<div style='font-size:0.8em'><b>Author:</b>" + \
324 tooltip_html = "<div style='font-size:0.8em'><b>Author:</b>" + \
325 " %s<br/><b>Date:</b> %s</b><br/><b>Message:</b> %s<br/></div>"
325 " %s<br/><b>Date:</b> %s</b><br/><b>Message:</b> %s<br/></div>"
326
326
327 tooltip_html = tooltip_html % (changeset.author,
327 tooltip_html = tooltip_html % (changeset.author,
328 changeset.date,
328 changeset.date,
329 tooltip(changeset.message))
329 tooltip(changeset.message))
330 lnk_format = '%5s:%s' % ('r%s' % changeset.revision,
330 lnk_format = '%5s:%s' % ('r%s' % changeset.revision,
331 short_id(changeset.raw_id))
331 short_id(changeset.raw_id))
332 uri = link_to(
332 uri = link_to(
333 lnk_format,
333 lnk_format,
334 url('changeset_home', repo_name=changeset.repository.name,
334 url('changeset_home', repo_name=changeset.repository.name,
335 revision=changeset.raw_id),
335 revision=changeset.raw_id),
336 style=get_color_string(changeset.raw_id),
336 style=get_color_string(changeset.raw_id),
337 class_='tooltip',
337 class_='tooltip',
338 title=tooltip_html
338 title=tooltip_html
339 )
339 )
340
340
341 uri += '\n'
341 uri += '\n'
342 return uri
342 return uri
343 return literal(annotate_highlight(filenode, url_func, **kwargs))
343 return literal(annotate_highlight(filenode, url_func, **kwargs))
344
344
345 def get_changeset_safe(repo, rev):
345 def get_changeset_safe(repo, rev):
346 from vcs.backends.base import BaseRepository
346 from vcs.backends.base import BaseRepository
347 from vcs.exceptions import RepositoryError
347 from vcs.exceptions import RepositoryError
348 if not isinstance(repo, BaseRepository):
348 if not isinstance(repo, BaseRepository):
349 raise Exception('You must pass an Repository '
349 raise Exception('You must pass an Repository '
350 'object as first argument got %s', type(repo))
350 'object as first argument got %s', type(repo))
351
351
352 try:
352 try:
353 cs = repo.get_changeset(rev)
353 cs = repo.get_changeset(rev)
354 except RepositoryError:
354 except RepositoryError:
355 from rhodecode.lib.utils import EmptyChangeset
355 from rhodecode.lib.utils import EmptyChangeset
356 cs = EmptyChangeset()
356 cs = EmptyChangeset()
357 return cs
357 return cs
358
358
359
359
360 def is_following_repo(repo_name, user_id):
360 def is_following_repo(repo_name, user_id):
361 from rhodecode.model.scm import ScmModel
361 from rhodecode.model.scm import ScmModel
362 return ScmModel().is_following_repo(repo_name, user_id)
362 return ScmModel().is_following_repo(repo_name, user_id)
363
363
364 flash = _Flash()
364 flash = _Flash()
365
365
366
366
367 #==============================================================================
367 #==============================================================================
368 # MERCURIAL FILTERS available via h.
368 # MERCURIAL FILTERS available via h.
369 #==============================================================================
369 #==============================================================================
370 from mercurial import util
370 from mercurial import util
371 from mercurial.templatefilters import person as _person
371 from mercurial.templatefilters import person as _person
372
372
373 def _age(curdate):
373 def _age(curdate):
374 """turns a datetime into an age string."""
374 """turns a datetime into an age string."""
375
375
376 if not curdate:
376 if not curdate:
377 return ''
377 return ''
378
378
379 from datetime import timedelta, datetime
379 from datetime import timedelta, datetime
380
380
381 agescales = [("year", 3600 * 24 * 365),
381 agescales = [("year", 3600 * 24 * 365),
382 ("month", 3600 * 24 * 30),
382 ("month", 3600 * 24 * 30),
383 ("day", 3600 * 24),
383 ("day", 3600 * 24),
384 ("hour", 3600),
384 ("hour", 3600),
385 ("minute", 60),
385 ("minute", 60),
386 ("second", 1), ]
386 ("second", 1), ]
387
387
388 age = datetime.now() - curdate
388 age = datetime.now() - curdate
389 age_seconds = (age.days * agescales[2][1]) + age.seconds
389 age_seconds = (age.days * agescales[2][1]) + age.seconds
390 pos = 1
390 pos = 1
391 for scale in agescales:
391 for scale in agescales:
392 if scale[1] <= age_seconds:
392 if scale[1] <= age_seconds:
393 if pos == 6:pos = 5
393 if pos == 6:pos = 5
394 return time_ago_in_words(curdate, agescales[pos][0]) + ' ' + _('ago')
394 return time_ago_in_words(curdate, agescales[pos][0]) + ' ' + _('ago')
395 pos += 1
395 pos += 1
396
396
397 return _('just now')
397 return _('just now')
398
398
399 age = lambda x:_age(x)
399 age = lambda x:_age(x)
400 capitalize = lambda x: x.capitalize()
400 capitalize = lambda x: x.capitalize()
401 email = util.email
401 email = util.email
402 email_or_none = lambda x: util.email(x) if util.email(x) != x else None
402 email_or_none = lambda x: util.email(x) if util.email(x) != x else None
403 person = lambda x: _person(x)
403 person = lambda x: _person(x)
404 short_id = lambda x: x[:12]
404 short_id = lambda x: x[:12]
405
405
406
406
407 def bool2icon(value):
407 def bool2icon(value):
408 """Returns True/False values represented as small html image of true/false
408 """Returns True/False values represented as small html image of true/false
409 icons
409 icons
410
410
411 :param value: bool value
411 :param value: bool value
412 """
412 """
413
413
414 if value is True:
414 if value is True:
415 return HTML.tag('img', src=url("/images/icons/accept.png"),
415 return HTML.tag('img', src=url("/images/icons/accept.png"),
416 alt=_('True'))
416 alt=_('True'))
417
417
418 if value is False:
418 if value is False:
419 return HTML.tag('img', src=url("/images/icons/cancel.png"),
419 return HTML.tag('img', src=url("/images/icons/cancel.png"),
420 alt=_('False'))
420 alt=_('False'))
421
421
422 return value
422 return value
423
423
424
424
425 def action_parser(user_log):
425 def action_parser(user_log, feed=False):
426 """This helper will map the specified string action into translated
426 """This helper will action_map the specified string action into translated
427 fancy names with icons and links
427 fancy names with icons and links
428
428
429 :param user_log: user log instance
429 :param user_log: user log instance
430 :param feed: use output for feeds (no html and fancy icons)
430 """
431 """
431
432
432 action = user_log.action
433 action = user_log.action
433 action_params = ' '
434 action_params = ' '
434
435
435 x = action.split(':')
436 x = action.split(':')
436
437
437 if len(x) > 1:
438 if len(x) > 1:
438 action, action_params = x
439 action, action_params = x
439
440
440 def get_cs_links():
441 def get_cs_links():
441 revs_limit = 5 #display this amount always
442 revs_limit = 5 #display this amount always
442 revs_top_limit = 50 #show upto this amount of changesets hidden
443 revs_top_limit = 50 #show upto this amount of changesets hidden
443 revs = action_params.split(',')
444 revs = action_params.split(',')
444 repo_name = user_log.repository.repo_name
445 repo_name = user_log.repository.repo_name
445
446
446 from rhodecode.model.scm import ScmModel
447 from rhodecode.model.scm import ScmModel
447 repo, dbrepo = ScmModel().get(repo_name, retval='repo',
448 repo, dbrepo = ScmModel().get(repo_name, retval='repo',
448 invalidation_list=[])
449 invalidation_list=[])
449
450
450 message = lambda rev: get_changeset_safe(repo, rev).message
451 message = lambda rev: get_changeset_safe(repo, rev).message
451
452
452 cs_links = " " + ', '.join ([link_to(rev,
453 cs_links = " " + ', '.join ([link_to(rev,
453 url('changeset_home',
454 url('changeset_home',
454 repo_name=repo_name,
455 repo_name=repo_name,
455 revision=rev), title=tooltip(message(rev)),
456 revision=rev), title=tooltip(message(rev)),
456 class_='tooltip') for rev in revs[:revs_limit] ])
457 class_='tooltip') for rev in revs[:revs_limit] ])
457
458
458 compare_view = (' <div class="compare_view tooltip" title="%s">'
459 compare_view = (' <div class="compare_view tooltip" title="%s">'
459 '<a href="%s">%s</a> '
460 '<a href="%s">%s</a> '
460 '</div>' % (_('Show all combined changesets %s->%s' \
461 '</div>' % (_('Show all combined changesets %s->%s' \
461 % (revs[0], revs[-1])),
462 % (revs[0], revs[-1])),
462 url('changeset_home', repo_name=repo_name,
463 url('changeset_home', repo_name=repo_name,
463 revision='%s...%s' % (revs[0], revs[-1])
464 revision='%s...%s' % (revs[0], revs[-1])
464 ),
465 ),
465 _('compare view'))
466 _('compare view'))
466 )
467 )
467
468
468 if len(revs) > revs_limit:
469 if len(revs) > revs_limit:
469 uniq_id = revs[0]
470 uniq_id = revs[0]
470 html_tmpl = ('<span> %s '
471 html_tmpl = ('<span> %s '
471 '<a class="show_more" id="_%s" href="#more">%s</a> '
472 '<a class="show_more" id="_%s" href="#more">%s</a> '
472 '%s</span>')
473 '%s</span>')
473 cs_links += html_tmpl % (_('and'), uniq_id, _('%s more') \
474 if not feed:
475 cs_links += html_tmpl % (_('and'), uniq_id, _('%s more') \
474 % (len(revs) - revs_limit),
476 % (len(revs) - revs_limit),
475 _('revisions'))
477 _('revisions'))
476
478
477 html_tmpl = '<span id="%s" style="display:none"> %s </span>'
479 if not feed:
480 html_tmpl = '<span id="%s" style="display:none"> %s </span>'
481 else:
482 html_tmpl = '<span id="%s"> %s </span>'
483
478 cs_links += html_tmpl % (uniq_id, ', '.join([link_to(rev,
484 cs_links += html_tmpl % (uniq_id, ', '.join([link_to(rev,
479 url('changeset_home',
485 url('changeset_home',
480 repo_name=repo_name, revision=rev),
486 repo_name=repo_name, revision=rev),
481 title=message(rev), class_='tooltip')
487 title=message(rev), class_='tooltip')
482 for rev in revs[revs_limit:revs_top_limit]]))
488 for rev in revs[revs_limit:revs_top_limit]]))
483 if len(revs) > 1:
489 if len(revs) > 1:
484 cs_links += compare_view
490 cs_links += compare_view
485 return cs_links
491 return cs_links
486
492
487 def get_fork_name():
493 def get_fork_name():
488 repo_name = action_params
494 repo_name = action_params
489 return _('fork name ') + str(link_to(action_params, url('summary_home',
495 return _('fork name ') + str(link_to(action_params, url('summary_home',
490 repo_name=repo_name,)))
496 repo_name=repo_name,)))
491
497
492 map = {'user_deleted_repo':(_('[deleted] repository'), None),
498 action_map = {'user_deleted_repo':(_('[deleted] repository'), None),
493 'user_created_repo':(_('[created] repository'), None),
499 'user_created_repo':(_('[created] repository'), None),
494 'user_forked_repo':(_('[forked] repository'), get_fork_name),
500 'user_forked_repo':(_('[forked] repository'), get_fork_name),
495 'user_updated_repo':(_('[updated] repository'), None),
501 'user_updated_repo':(_('[updated] repository'), None),
496 'admin_deleted_repo':(_('[delete] repository'), None),
502 'admin_deleted_repo':(_('[delete] repository'), None),
497 'admin_created_repo':(_('[created] repository'), None),
503 'admin_created_repo':(_('[created] repository'), None),
498 'admin_forked_repo':(_('[forked] repository'), None),
504 'admin_forked_repo':(_('[forked] repository'), None),
499 'admin_updated_repo':(_('[updated] repository'), None),
505 'admin_updated_repo':(_('[updated] repository'), None),
500 'push':(_('[pushed] into'), get_cs_links),
506 'push':(_('[pushed] into'), get_cs_links),
501 'pull':(_('[pulled] from'), None),
507 'pull':(_('[pulled] from'), None),
502 'started_following_repo':(_('[started following] repository'), None),
508 'started_following_repo':(_('[started following] repository'), None),
503 'stopped_following_repo':(_('[stopped following] repository'), None),
509 'stopped_following_repo':(_('[stopped following] repository'), None),
504 }
510 }
505
511
506 action_str = map.get(action, action)
512 action_str = action_map.get(action, action)
507 action = action_str[0].replace('[', '<span class="journal_highlight">')\
513 if feed:
514 action = action_str[0].replace('[', '').replace(']', '')
515 else:
516 action = action_str[0].replace('[', '<span class="journal_highlight">')\
508 .replace(']', '</span>')
517 .replace(']', '</span>')
509 action_params_func = lambda :""
518 action_params_func = lambda :""
510
519
511 if action_str[1] is not None:
520 if action_str[1] is not None:
512 action_params_func = action_str[1]
521 action_params_func = action_str[1]
513
522
514 return [literal(action), action_params_func]
523 return [literal(action), action_params_func]
515
524
516 def action_parser_icon(user_log):
525 def action_parser_icon(user_log):
517 action = user_log.action
526 action = user_log.action
518 action_params = None
527 action_params = None
519 x = action.split(':')
528 x = action.split(':')
520
529
521 if len(x) > 1:
530 if len(x) > 1:
522 action, action_params = x
531 action, action_params = x
523
532
524 tmpl = """<img src="%s/%s" alt="%s"/>"""
533 tmpl = """<img src="%s/%s" alt="%s"/>"""
525 map = {'user_deleted_repo':'database_delete.png',
534 map = {'user_deleted_repo':'database_delete.png',
526 'user_created_repo':'database_add.png',
535 'user_created_repo':'database_add.png',
527 'user_forked_repo':'arrow_divide.png',
536 'user_forked_repo':'arrow_divide.png',
528 'user_updated_repo':'database_edit.png',
537 'user_updated_repo':'database_edit.png',
529 'admin_deleted_repo':'database_delete.png',
538 'admin_deleted_repo':'database_delete.png',
530 'admin_created_repo':'database_add.png',
539 'admin_created_repo':'database_add.png',
531 'admin_forked_repo':'arrow_divide.png',
540 'admin_forked_repo':'arrow_divide.png',
532 'admin_updated_repo':'database_edit.png',
541 'admin_updated_repo':'database_edit.png',
533 'push':'script_add.png',
542 'push':'script_add.png',
534 'pull':'down_16.png',
543 'pull':'down_16.png',
535 'started_following_repo':'heart_add.png',
544 'started_following_repo':'heart_add.png',
536 'stopped_following_repo':'heart_delete.png',
545 'stopped_following_repo':'heart_delete.png',
537 }
546 }
538 return literal(tmpl % ((url('/images/icons/')),
547 return literal(tmpl % ((url('/images/icons/')),
539 map.get(action, action), action))
548 map.get(action, action), action))
540
549
541
550
542 #==============================================================================
551 #==============================================================================
543 # PERMS
552 # PERMS
544 #==============================================================================
553 #==============================================================================
545 from rhodecode.lib.auth import HasPermissionAny, HasPermissionAll, \
554 from rhodecode.lib.auth import HasPermissionAny, HasPermissionAll, \
546 HasRepoPermissionAny, HasRepoPermissionAll
555 HasRepoPermissionAny, HasRepoPermissionAll
547
556
548 #==============================================================================
557 #==============================================================================
549 # GRAVATAR URL
558 # GRAVATAR URL
550 #==============================================================================
559 #==============================================================================
551 import hashlib
560 import hashlib
552 import urllib
561 import urllib
553 from pylons import request
562 from pylons import request
554
563
555 def gravatar_url(email_address, size=30):
564 def gravatar_url(email_address, size=30):
556 ssl_enabled = 'https' == request.environ.get('wsgi.url_scheme')
565 ssl_enabled = 'https' == request.environ.get('wsgi.url_scheme')
557 default = 'identicon'
566 default = 'identicon'
558 baseurl_nossl = "http://www.gravatar.com/avatar/"
567 baseurl_nossl = "http://www.gravatar.com/avatar/"
559 baseurl_ssl = "https://secure.gravatar.com/avatar/"
568 baseurl_ssl = "https://secure.gravatar.com/avatar/"
560 baseurl = baseurl_ssl if ssl_enabled else baseurl_nossl
569 baseurl = baseurl_ssl if ssl_enabled else baseurl_nossl
561
570
562
571
563 # construct the url
572 # construct the url
564 gravatar_url = baseurl + hashlib.md5(email_address.lower()).hexdigest() + "?"
573 gravatar_url = baseurl + hashlib.md5(email_address.lower()).hexdigest() + "?"
565 gravatar_url += urllib.urlencode({'d':default, 's':str(size)})
574 gravatar_url += urllib.urlencode({'d':default, 's':str(size)})
566
575
567 return gravatar_url
576 return gravatar_url
568
577
569 def safe_unicode(str):
578 def safe_unicode(str):
570 """safe unicode function. In case of UnicodeDecode error we try to return
579 """safe unicode function. In case of UnicodeDecode error we try to return
571 unicode with errors replace, if this failes we return unicode with
580 unicode with errors replace, if this failes we return unicode with
572 string_escape decoding """
581 string_escape decoding """
573
582
574 try:
583 try:
575 u_str = unicode(str)
584 u_str = unicode(str)
576 except UnicodeDecodeError:
585 except UnicodeDecodeError:
577 try:
586 try:
578 u_str = unicode(str, 'utf-8', 'replace')
587 u_str = unicode(str, 'utf-8', 'replace')
579 except UnicodeDecodeError:
588 except UnicodeDecodeError:
580 #incase we have a decode error just represent as byte string
589 #incase we have a decode error just represent as byte string
581 u_str = unicode(str(str).encode('string_escape'))
590 u_str = unicode(str(str).encode('string_escape'))
582
591
583 return u_str
592 return u_str
584
593
585 def changed_tooltip(nodes):
594 def changed_tooltip(nodes):
586 if nodes:
595 if nodes:
587 pref = ': <br/> '
596 pref = ': <br/> '
588 suf = ''
597 suf = ''
589 if len(nodes) > 30:
598 if len(nodes) > 30:
590 suf = '<br/>' + _(' and %s more') % (len(nodes) - 30)
599 suf = '<br/>' + _(' and %s more') % (len(nodes) - 30)
591 return literal(pref + '<br/> '.join([x.path for x in nodes[:30]]) + suf)
600 return literal(pref + '<br/> '.join([x.path.decode('utf-8') for x in nodes[:30]]) + suf)
592 else:
601 else:
593 return ': ' + _('No Files')
602 return ': ' + _('No Files')
@@ -1,122 +1,122
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} ${_('Changeset')} - r${c.changeset.revision}:${h.short_id(c.changeset.raw_id)} - ${c.rhodecode_name}
4 ${c.repo_name} ${_('Changeset')} - r${c.changeset.revision}:${h.short_id(c.changeset.raw_id)} - ${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 ${_('Changeset')} - r${c.changeset.revision}:${h.short_id(c.changeset.raw_id)}
12 ${_('Changeset')} - r${c.changeset.revision}:${h.short_id(c.changeset.raw_id)}
13 </%def>
13 </%def>
14
14
15 <%def name="page_nav()">
15 <%def name="page_nav()">
16 ${self.menu('changelog')}
16 ${self.menu('changelog')}
17 </%def>
17 </%def>
18
18
19 <%def name="main()">
19 <%def name="main()">
20 <div class="box">
20 <div class="box">
21 <!-- box / title -->
21 <!-- box / title -->
22 <div class="title">
22 <div class="title">
23 ${self.breadcrumbs()}
23 ${self.breadcrumbs()}
24 </div>
24 </div>
25 <div class="table">
25 <div class="table">
26 <div class="diffblock">
26 <div class="diffblock">
27 <div class="code-header">
27 <div class="code-header">
28 <div>
28 <div>
29 ${_('Changeset')} - r${c.changeset.revision}:${h.short_id(c.changeset.raw_id)}
29 ${_('Changeset')} - r${c.changeset.revision}:${h.short_id(c.changeset.raw_id)}
30 &raquo; <span>${h.link_to(_('raw diff'),
30 &raquo; <span>${h.link_to(_('raw diff'),
31 h.url('raw_changeset_home',repo_name=c.repo_name,revision=c.changeset.raw_id,diff='show'))}</span>
31 h.url('raw_changeset_home',repo_name=c.repo_name,revision=c.changeset.raw_id,diff='show'))}</span>
32 &raquo; <span>${h.link_to(_('download diff'),
32 &raquo; <span>${h.link_to(_('download diff'),
33 h.url('raw_changeset_home',repo_name=c.repo_name,revision=c.changeset.raw_id,diff='download'))}</span>
33 h.url('raw_changeset_home',repo_name=c.repo_name,revision=c.changeset.raw_id,diff='download'))}</span>
34 </div>
34 </div>
35 </div>
35 </div>
36 </div>
36 </div>
37 <div id="changeset_content">
37 <div id="changeset_content">
38 <div class="container">
38 <div class="container">
39 <div class="left">
39 <div class="left">
40 <div class="date">${_('commit')} ${c.changeset.revision}: ${h.short_id(c.changeset.raw_id)}@${c.changeset.date}</div>
40 <div class="date">${_('commit')} ${c.changeset.revision}: ${h.short_id(c.changeset.raw_id)}@${c.changeset.date}</div>
41 <div class="author">
41 <div class="author">
42 <div class="gravatar">
42 <div class="gravatar">
43 <img alt="gravatar" src="${h.gravatar_url(h.email(c.changeset.author),20)}"/>
43 <img alt="gravatar" src="${h.gravatar_url(h.email(c.changeset.author),20)}"/>
44 </div>
44 </div>
45 <span>${h.person(c.changeset.author)}</span><br/>
45 <span>${h.person(c.changeset.author)}</span><br/>
46 <span><a href="mailto:${h.email_or_none(c.changeset.author)}">${h.email_or_none(c.changeset.author)}</a></span><br/>
46 <span><a href="mailto:${h.email_or_none(c.changeset.author)}">${h.email_or_none(c.changeset.author)}</a></span><br/>
47 </div>
47 </div>
48 <div class="message">${h.link_to(h.wrap_paragraphs(c.changeset.message),h.url('changeset_home',repo_name=c.repo_name,revision=c.changeset.raw_id))}</div>
48 <div class="message">${h.link_to(h.wrap_paragraphs(c.changeset.message),h.url('changeset_home',repo_name=c.repo_name,revision=c.changeset.raw_id))}</div>
49 </div>
49 </div>
50 <div class="right">
50 <div class="right">
51 <div class="changes">
51 <div class="changes">
52 <span class="removed" title="${_('removed')}">${len(c.changeset.removed)}</span>
52 <span class="removed" title="${_('removed')}">${len(c.changeset.removed)}</span>
53 <span class="changed" title="${_('changed')}">${len(c.changeset.changed)}</span>
53 <span class="changed" title="${_('changed')}">${len(c.changeset.changed)}</span>
54 <span class="added" title="${_('added')}">${len(c.changeset.added)}</span>
54 <span class="added" title="${_('added')}">${len(c.changeset.added)}</span>
55 </div>
55 </div>
56 %if len(c.changeset.parents)>1:
56 %if len(c.changeset.parents)>1:
57 <div class="merge">
57 <div class="merge">
58 ${_('merge')}<img alt="merge" src="${h.url("/images/icons/arrow_join.png")}"/>
58 ${_('merge')}<img alt="merge" src="${h.url("/images/icons/arrow_join.png")}"/>
59 </div>
59 </div>
60 %endif
60 %endif
61
61
62 %if c.changeset.parents:
62 %if c.changeset.parents:
63 %for p_cs in reversed(c.changeset.parents):
63 %for p_cs in reversed(c.changeset.parents):
64 <div class="parent">${_('Parent')} ${p_cs.revision}: ${h.link_to(h.short_id(p_cs.raw_id),
64 <div class="parent">${_('Parent')} ${p_cs.revision}: ${h.link_to(h.short_id(p_cs.raw_id),
65 h.url('changeset_home',repo_name=c.repo_name,revision=p_cs.raw_id),title=p_cs.message)}
65 h.url('changeset_home',repo_name=c.repo_name,revision=p_cs.raw_id),title=p_cs.message)}
66 </div>
66 </div>
67 %endfor
67 %endfor
68 %else:
68 %else:
69 <div class="parent">${_('No parents')}</div>
69 <div class="parent">${_('No parents')}</div>
70 %endif
70 %endif
71 <span class="logtags">
71 <span class="logtags">
72 <span class="branchtag" title="${'%s %s' % (_('branch'),c.changeset.branch)}">
72 <span class="branchtag" title="${'%s %s' % (_('branch'),c.changeset.branch)}">
73 ${h.link_to(c.changeset.branch,h.url('files_home',repo_name=c.repo_name,revision=c.changeset.raw_id))}</span>
73 ${h.link_to(c.changeset.branch,h.url('files_home',repo_name=c.repo_name,revision=c.changeset.raw_id))}</span>
74 %for tag in c.changeset.tags:
74 %for tag in c.changeset.tags:
75 <span class="tagtag" title="${'%s %s' % (_('tag'),tag)}">
75 <span class="tagtag" title="${'%s %s' % (_('tag'),tag)}">
76 ${h.link_to(tag,h.url('files_home',repo_name=c.repo_name,revision=c.changeset.raw_id))}</span>
76 ${h.link_to(tag,h.url('files_home',repo_name=c.repo_name,revision=c.changeset.raw_id))}</span>
77 %endfor
77 %endfor
78 </span>
78 </span>
79 </div>
79 </div>
80 </div>
80 </div>
81 <span style="font-size:1.1em;font-weight: bold">${_('Files affected')}</span>
81 <span style="font-size:1.1em;font-weight: bold">${_('Files affected')}</span>
82 <div class="cs_files">
82 <div class="cs_files">
83 %for change,filenode,diff,cs1,cs2 in c.changes:
83 %for change,filenode,diff,cs1,cs2 in c.changes:
84 <div class="cs_${change}">${h.link_to(filenode.path,h.url.current(anchor=h.repo_name_slug('C%s' % filenode.path)))}</div>
84 <div class="cs_${change}">${h.link_to(filenode.path.decode('utf-8'),h.url.current(anchor=h.repo_name_slug('C%s' % filenode.path.decode('utf-8'))))}</div>
85 %endfor
85 %endfor
86 </div>
86 </div>
87 </div>
87 </div>
88
88
89 </div>
89 </div>
90
90
91 %for change,filenode,diff,cs1,cs2 in c.changes:
91 %for change,filenode,diff,cs1,cs2 in c.changes:
92 %if change !='removed':
92 %if change !='removed':
93 <div style="clear:both;height:10px"></div>
93 <div style="clear:both;height:10px"></div>
94 <div class="diffblock">
94 <div class="diffblock">
95 <div id="${h.repo_name_slug('C%s' % filenode.path)}" class="code-header">
95 <div id="${h.repo_name_slug('C%s' % filenode.path.decode('utf-8'))}" class="code-header">
96 <div class="changeset_header">
96 <div class="changeset_header">
97 <span class="changeset_file">
97 <span class="changeset_file">
98 ${h.link_to_if(change!='removed',filenode.path,h.url('files_home',repo_name=c.repo_name,
98 ${h.link_to_if(change!='removed',filenode.path.decode('utf-8'),h.url('files_home',repo_name=c.repo_name,
99 revision=filenode.changeset.raw_id,f_path=filenode.path))}
99 revision=filenode.changeset.raw_id,f_path=filenode.path.decode('utf-8')))}
100 </span>
100 </span>
101 %if 1:
101 %if 1:
102 &raquo; <span>${h.link_to(_('diff'),
102 &raquo; <span>${h.link_to(_('diff'),
103 h.url('files_diff_home',repo_name=c.repo_name,f_path=filenode.path,diff2=cs2,diff1=cs1,diff='diff'))}</span>
103 h.url('files_diff_home',repo_name=c.repo_name,f_path=filenode.path.decode('utf-8'),diff2=cs2,diff1=cs1,diff='diff'))}</span>
104 &raquo; <span>${h.link_to(_('raw diff'),
104 &raquo; <span>${h.link_to(_('raw diff'),
105 h.url('files_diff_home',repo_name=c.repo_name,f_path=filenode.path,diff2=cs2,diff1=cs1,diff='raw'))}</span>
105 h.url('files_diff_home',repo_name=c.repo_name,f_path=filenode.path.decode('utf-8'),diff2=cs2,diff1=cs1,diff='raw'))}</span>
106 &raquo; <span>${h.link_to(_('download diff'),
106 &raquo; <span>${h.link_to(_('download diff'),
107 h.url('files_diff_home',repo_name=c.repo_name,f_path=filenode.path,diff2=cs2,diff1=cs1,diff='download'))}</span>
107 h.url('files_diff_home',repo_name=c.repo_name,f_path=filenode.path.decode('utf-8'),diff2=cs2,diff1=cs1,diff='download'))}</span>
108 %endif
108 %endif
109 </div>
109 </div>
110 </div>
110 </div>
111 <div class="code-body">
111 <div class="code-body">
112 %if diff:
112 %if diff:
113 ${diff|n}
113 ${diff|n}
114 %else:
114 %else:
115 ${_('No changes in this file')}
115 ${_('No changes in this file')}
116 %endif
116 %endif
117 </div>
117 </div>
118 </div>
118 </div>
119 %endif
119 %endif
120 %endfor
120 %endfor
121 </div>
121 </div>
122 </%def> No newline at end of file
122 </%def>
@@ -1,97 +1,97
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} ${_('Changesets')} - r${c.cs_ranges[0].revision}:${h.short_id(c.cs_ranges[0].raw_id)} -> r${c.cs_ranges[-1].revision}:${h.short_id(c.cs_ranges[-1].raw_id)} - ${c.rhodecode_name}
4 ${c.repo_name} ${_('Changesets')} - r${c.cs_ranges[0].revision}:${h.short_id(c.cs_ranges[0].raw_id)} -> r${c.cs_ranges[-1].revision}:${h.short_id(c.cs_ranges[-1].raw_id)} - ${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 ${_('Changesets')} - r${c.cs_ranges[0].revision}:${h.short_id(c.cs_ranges[0].raw_id)} -> r${c.cs_ranges[-1].revision}:${h.short_id(c.cs_ranges[-1].raw_id)}
12 ${_('Changesets')} - r${c.cs_ranges[0].revision}:${h.short_id(c.cs_ranges[0].raw_id)} -> r${c.cs_ranges[-1].revision}:${h.short_id(c.cs_ranges[-1].raw_id)}
13 </%def>
13 </%def>
14
14
15 <%def name="page_nav()">
15 <%def name="page_nav()">
16 ${self.menu('changelog')}
16 ${self.menu('changelog')}
17 </%def>
17 </%def>
18
18
19 <%def name="main()">
19 <%def name="main()">
20 <div class="box">
20 <div class="box">
21 <!-- box / title -->
21 <!-- box / title -->
22 <div class="title">
22 <div class="title">
23 ${self.breadcrumbs()}
23 ${self.breadcrumbs()}
24 </div>
24 </div>
25 <div class="table">
25 <div class="table">
26 <div id="body" class="diffblock">
26 <div id="body" class="diffblock">
27 <div class="code-header">
27 <div class="code-header">
28 <div>
28 <div>
29 ${_('Changesets')} - r${c.cs_ranges[0].revision}:${h.short_id(c.cs_ranges[0].raw_id)} -> r${c.cs_ranges[-1].revision}:${h.short_id(c.cs_ranges[-1].raw_id)}
29 ${_('Changesets')} - r${c.cs_ranges[0].revision}:${h.short_id(c.cs_ranges[0].raw_id)} -> r${c.cs_ranges[-1].revision}:${h.short_id(c.cs_ranges[-1].raw_id)}
30 <h3>${_('Compare View')}</h3>
30 <h3>${_('Compare View')}</h3>
31 ##&raquo; <span>${h.link_to(_('raw diff'),
31 ##&raquo; <span>${h.link_to(_('raw diff'),
32 ##h.url('raw_changeset_home',repo_name=c.repo_name,revision=c.changeset.raw_id,diff='show'))}</span>
32 ##h.url('raw_changeset_home',repo_name=c.repo_name,revision=c.changeset.raw_id,diff='show'))}</span>
33 ##&raquo; <span>${h.link_to(_('download diff'),
33 ##&raquo; <span>${h.link_to(_('download diff'),
34 ##h.url('raw_changeset_home',repo_name=c.repo_name,revision=c.changeset.raw_id,diff='download'))}</span>
34 ##h.url('raw_changeset_home',repo_name=c.repo_name,revision=c.changeset.raw_id,diff='download'))}</span>
35 </div>
35 </div>
36 </div>
36 </div>
37 </div>
37 </div>
38 <div id="changeset_compare_view_content">
38 <div id="changeset_compare_view_content">
39 <div class="container">
39 <div class="container">
40 <table class="compare_view_commits">
40 <table class="compare_view_commits">
41 %for cs in c.cs_ranges:
41 %for cs in c.cs_ranges:
42 <tr>
42 <tr>
43 <td><div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(h.email(cs.author),14)}"/></div></td>
43 <td><div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(h.email(cs.author),14)}"/></div></td>
44 <td>${h.link_to('r%s:%s' % (cs.revision,h.short_id(cs.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}</td>
44 <td>${h.link_to('r%s:%s' % (cs.revision,h.short_id(cs.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}</td>
45 <td><div class="author">${h.person(cs.author)}</div></td>
45 <td><div class="author">${h.person(cs.author)}</div></td>
46 <td><span class="tooltip" title="${h.age(cs.date)}">${cs.date}</span></td>
46 <td><span class="tooltip" title="${h.age(cs.date)}">${cs.date}</span></td>
47 <td><div class="message">${h.link_to(h.wrap_paragraphs(cs.message),h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}</div></td>
47 <td><div class="message">${h.link_to(h.wrap_paragraphs(cs.message),h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}</div></td>
48 </tr>
48 </tr>
49 %endfor
49 %endfor
50 </table>
50 </table>
51 </div>
51 </div>
52 <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${_('Files affected')}</div>
52 <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${_('Files affected')}</div>
53 <div class="cs_files">
53 <div class="cs_files">
54 %for cs in c.cs_ranges:
54 %for cs in c.cs_ranges:
55 <div class="cur_cs">r${cs}</div>
55 <div class="cur_cs">r${cs}</div>
56 %for change,filenode,diff,cs1,cs2 in c.changes[cs.raw_id]:
56 %for change,filenode,diff,cs1,cs2 in c.changes[cs.raw_id]:
57 <div class="cs_${change}">${h.link_to(filenode.path,h.url.current(anchor=h.repo_name_slug('C%s-%s' % (cs.short_id,filenode.path))))}</div>
57 <div class="cs_${change}">${h.link_to(filenode.path.decode('utf-8'),h.url.current(anchor=h.repo_name_slug('C%s-%s' % (cs.short_id,filenode.path.decode('utf-8')))))}</div>
58 %endfor
58 %endfor
59 %endfor
59 %endfor
60 </div>
60 </div>
61 </div>
61 </div>
62
62
63 </div>
63 </div>
64 %for cs in c.cs_ranges:
64 %for cs in c.cs_ranges:
65 %for change,filenode,diff,cs1,cs2 in c.changes[cs.raw_id]:
65 %for change,filenode,diff,cs1,cs2 in c.changes[cs.raw_id]:
66 %if change !='removed':
66 %if change !='removed':
67 <div style="clear:both;height:10px"></div>
67 <div style="clear:both;height:10px"></div>
68 <div class="diffblock">
68 <div class="diffblock">
69 <div id="${h.repo_name_slug('C%s-%s' % (cs.short_id,filenode.path))}" class="code-header">
69 <div id="${h.repo_name_slug('C%s-%s' % (cs.short_id,filenode.path.decode('utf-8')))}" class="code-header">
70 <div class="changeset_header">
70 <div class="changeset_header">
71 <span class="changeset_file">
71 <span class="changeset_file">
72 ${h.link_to_if(change!='removed',filenode.path,h.url('files_home',repo_name=c.repo_name,
72 ${h.link_to_if(change!='removed',filenode.path.decode('utf-8'),h.url('files_home',repo_name=c.repo_name,
73 revision=filenode.changeset.raw_id,f_path=filenode.path))}
73 revision=filenode.changeset.raw_id,f_path=filenode.path.decode('utf-8')))}
74 </span>
74 </span>
75 %if 1:
75 %if 1:
76 &raquo; <span>${h.link_to(_('diff'),
76 &raquo; <span>${h.link_to(_('diff'),
77 h.url('files_diff_home',repo_name=c.repo_name,f_path=filenode.path,diff2=cs2,diff1=cs1,diff='diff'))}</span>
77 h.url('files_diff_home',repo_name=c.repo_name,f_path=filenode.path.decode('utf-8'),diff2=cs2,diff1=cs1,diff='diff'))}</span>
78 &raquo; <span>${h.link_to(_('raw diff'),
78 &raquo; <span>${h.link_to(_('raw diff'),
79 h.url('files_diff_home',repo_name=c.repo_name,f_path=filenode.path,diff2=cs2,diff1=cs1,diff='raw'))}</span>
79 h.url('files_diff_home',repo_name=c.repo_name,f_path=filenode.path.decode('utf-8'),diff2=cs2,diff1=cs1,diff='raw'))}</span>
80 &raquo; <span>${h.link_to(_('download diff'),
80 &raquo; <span>${h.link_to(_('download diff'),
81 h.url('files_diff_home',repo_name=c.repo_name,f_path=filenode.path,diff2=cs2,diff1=cs1,diff='download'))}</span>
81 h.url('files_diff_home',repo_name=c.repo_name,f_path=filenode.path.decode('utf-8'),diff2=cs2,diff1=cs1,diff='download'))}</span>
82 %endif
82 %endif
83 </div>
83 </div>
84 </div>
84 </div>
85 <div class="code-body">
85 <div class="code-body">
86 %if diff:
86 %if diff:
87 ${diff|n}
87 ${diff|n}
88 %else:
88 %else:
89 ${_('No changes in this file')}
89 ${_('No changes in this file')}
90 %endif
90 %endif
91 </div>
91 </div>
92 </div>
92 </div>
93 %endif
93 %endif
94 %endfor
94 %endfor
95 %endfor
95 %endfor
96 </div>
96 </div>
97 </%def> No newline at end of file
97 </%def>
@@ -1,31 +1,41
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
3 <%def name="title()">
3 <%def name="title()">
4 ${_('Journal')} - ${c.rhodecode_name}
4 ${_('Journal')} - ${c.rhodecode_name}
5 </%def>
5 </%def>
6 <%def name="breadcrumbs()">
6 <%def name="breadcrumbs()">
7 ${c.rhodecode_name}
7 ${c.rhodecode_name}
8 </%def>
8 </%def>
9 <%def name="page_nav()">
9 <%def name="page_nav()">
10 ${self.menu('home')}
10 ${self.menu('home')}
11 </%def>
11 </%def>
12 <%def name="main()">
12 <%def name="main()">
13
13
14 <div class="box">
14 <div class="box">
15 <!-- box / title -->
15 <!-- box / title -->
16 <div class="title">
16 <div class="title">
17 <h5>${_('Public Journal')}</h5>
17 <h5>${_('Public Journal')}</h5>
18 <ul class="links">
19 <li>
20 <span>${h.link_to(_('RSS'),h.url('public_journal_rss'),class_='rss_icon')}</span>
21 </li>
22 <li>
23 <span>${h.link_to(_('Atom'),h.url('public_journal_atom'),class_='atom_icon')}</span>
24 </li>
25
26 </ul>
27
18 </div>
28 </div>
19 <script type="text/javascript">
29 <script type="text/javascript">
20 function show_more_event(){
30 function show_more_event(){
21 YUE.on(YUD.getElementsByClassName('show_more'),'click',function(e){
31 YUE.on(YUD.getElementsByClassName('show_more'),'click',function(e){
22 var el = e.target;
32 var el = e.target;
23 YUD.setStyle(YUD.get(el.id.substring(1)),'display','');
33 YUD.setStyle(YUD.get(el.id.substring(1)),'display','');
24 YUD.setStyle(el.parentNode,'display','none');
34 YUD.setStyle(el.parentNode,'display','none');
25 });
35 });
26 }
36 }
27 </script>
37 </script>
28 <div id="journal">${c.journal_data}</div>
38 <div id="journal">${c.journal_data}</div>
29 </div>
39 </div>
30
40
31 </%def>
41 </%def>
@@ -1,332 +1,332
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.tests.test_hg_operations
3 rhodecode.tests.test_hg_operations
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Test suite for making push/pull operations
6 Test suite for making push/pull operations
7
7
8 :created_on: Dec 30, 2010
8 :created_on: Dec 30, 2010
9 :copyright: (c) 2010 by marcink.
9 :copyright: (c) 2010 by marcink.
10 :license: LICENSE_NAME, see LICENSE_FILE for more details.
10 :license: LICENSE_NAME, see LICENSE_FILE for more details.
11 """
11 """
12
12
13 import os
13 import os
14 import shutil
14 import shutil
15 import logging
15 import logging
16 from os.path import join as jn
16 from os.path import join as jn
17
17
18 from tempfile import _RandomNameSequence
18 from tempfile import _RandomNameSequence
19 from subprocess import Popen, PIPE
19 from subprocess import Popen, PIPE
20
20
21 from paste.deploy import appconfig
21 from paste.deploy import appconfig
22 from pylons import config
22 from pylons import config
23 from sqlalchemy import engine_from_config
23 from sqlalchemy import engine_from_config
24
24
25 from rhodecode.lib.utils import add_cache
25 from rhodecode.lib.utils import add_cache
26 from rhodecode.model import init_model
26 from rhodecode.model import init_model
27 from rhodecode.model import meta
27 from rhodecode.model import meta
28 from rhodecode.model.db import User, Repository
28 from rhodecode.model.db import User, Repository
29 from rhodecode.lib.auth import get_crypt_password
29 from rhodecode.lib.auth import get_crypt_password
30
30
31 from rhodecode.tests import TESTS_TMP_PATH, NEW_HG_REPO, HG_REPO
31 from rhodecode.tests import TESTS_TMP_PATH, NEW_HG_REPO, HG_REPO
32 from rhodecode.config.environment import load_environment
32 from rhodecode.config.environment import load_environment
33
33
34 conf = appconfig('config:development.ini', relative_to='./../../')
34 conf = appconfig('config:development.ini', relative_to='./../../')
35 load_environment(conf.global_conf, conf.local_conf)
35 load_environment(conf.global_conf, conf.local_conf)
36
36
37 add_cache(conf)
37 add_cache(conf)
38
38
39 USER = 'test_admin'
39 USER = 'test_admin'
40 PASS = 'test12'
40 PASS = 'test12'
41 HOST = '127.0.0.1:5000'
41 HOST = '127.0.0.1:5000'
42 DEBUG = True
42 DEBUG = True
43 log = logging.getLogger(__name__)
43 log = logging.getLogger(__name__)
44
44
45
45
46 class Command(object):
46 class Command(object):
47
47
48 def __init__(self, cwd):
48 def __init__(self, cwd):
49 self.cwd = cwd
49 self.cwd = cwd
50
50
51 def execute(self, cmd, *args):
51 def execute(self, cmd, *args):
52 """Runs command on the system with given ``args``.
52 """Runs command on the system with given ``args``.
53 """
53 """
54
54
55 command = cmd + ' ' + ' '.join(args)
55 command = cmd + ' ' + ' '.join(args)
56 log.debug('Executing %s' % command)
56 log.debug('Executing %s' % command)
57 if DEBUG:
57 if DEBUG:
58 print command
58 print command
59 p = Popen(command, shell=True, stdout=PIPE, stderr=PIPE, cwd=self.cwd)
59 p = Popen(command, shell=True, stdout=PIPE, stderr=PIPE, cwd=self.cwd)
60 stdout, stderr = p.communicate()
60 stdout, stderr = p.communicate()
61 if DEBUG:
61 if DEBUG:
62 print stdout, stderr
62 print stdout, stderr
63 return stdout, stderr
63 return stdout, stderr
64
64
65 def get_session():
65 def get_session():
66 engine = engine_from_config(conf, 'sqlalchemy.db1.')
66 engine = engine_from_config(conf, 'sqlalchemy.db1.')
67 init_model(engine)
67 init_model(engine)
68 sa = meta.Session()
68 sa = meta.Session()
69 return sa
69 return sa
70
70
71
71
72 def create_test_user(force=True):
72 def create_test_user(force=True):
73 print 'creating test user'
73 print 'creating test user'
74 sa = get_session()
74 sa = get_session()
75
75
76 user = sa.query(User).filter(User.username == USER).scalar()
76 user = sa.query(User).filter(User.username == USER).scalar()
77
77
78 if force and user is not None:
78 if force and user is not None:
79 print 'removing current user'
79 print 'removing current user'
80 for repo in sa.query(Repository).filter(Repository.user == user).all():
80 for repo in sa.query(Repository).filter(Repository.user == user).all():
81 sa.delete(repo)
81 sa.delete(repo)
82 sa.delete(user)
82 sa.delete(user)
83 sa.commit()
83 sa.commit()
84
84
85 if user is None or force:
85 if user is None or force:
86 print 'creating new one'
86 print 'creating new one'
87 new_usr = User()
87 new_usr = User()
88 new_usr.username = USER
88 new_usr.username = USER
89 new_usr.password = get_crypt_password(PASS)
89 new_usr.password = get_crypt_password(PASS)
90 new_usr.email = 'mail@mail.com'
90 new_usr.email = 'mail@mail.com'
91 new_usr.name = 'test'
91 new_usr.name = 'test'
92 new_usr.lastname = 'lasttestname'
92 new_usr.lastname = 'lasttestname'
93 new_usr.active = True
93 new_usr.active = True
94 new_usr.admin = True
94 new_usr.admin = True
95 sa.add(new_usr)
95 sa.add(new_usr)
96 sa.commit()
96 sa.commit()
97
97
98 print 'done'
98 print 'done'
99
99
100
100
101 def create_test_repo(force=True):
101 def create_test_repo(force=True):
102 from rhodecode.model.repo import RepoModel
102 from rhodecode.model.repo import RepoModel
103 sa = get_session()
103 sa = get_session()
104
104
105 user = sa.query(User).filter(User.username == USER).scalar()
105 user = sa.query(User).filter(User.username == USER).scalar()
106 if user is None:
106 if user is None:
107 raise Exception('user not found')
107 raise Exception('user not found')
108
108
109
109
110 repo = sa.query(Repository).filter(Repository.repo_name == HG_REPO).scalar()
110 repo = sa.query(Repository).filter(Repository.repo_name == HG_REPO).scalar()
111
111
112 if repo is None:
112 if repo is None:
113 print 'repo not found creating'
113 print 'repo not found creating'
114
114
115 form_data = {'repo_name':HG_REPO,
115 form_data = {'repo_name':HG_REPO,
116 'repo_type':'hg',
116 'repo_type':'hg',
117 'private':False, }
117 'private':False, }
118 rm = RepoModel(sa)
118 rm = RepoModel(sa)
119 rm.base_path = '/home/hg'
119 rm.base_path = '/home/hg'
120 rm.create(form_data, user)
120 rm.create(form_data, user)
121
121
122
122
123 def set_anonymous_access(enable=True):
123 def set_anonymous_access(enable=True):
124 sa = get_session()
124 sa = get_session()
125 user = sa.query(User).filter(User.username == 'default').one()
125 user = sa.query(User).filter(User.username == 'default').one()
126 user.active = enable
126 user.active = enable
127 sa.add(user)
127 sa.add(user)
128 sa.commit()
128 sa.commit()
129
129
130 def get_anonymous_access():
130 def get_anonymous_access():
131 sa = get_session()
131 sa = get_session()
132 return sa.query(User).filter(User.username == 'default').one().active
132 return sa.query(User).filter(User.username == 'default').one().active
133
133
134
134
135 #==============================================================================
135 #==============================================================================
136 # TESTS
136 # TESTS
137 #==============================================================================
137 #==============================================================================
138 def test_clone(no_errors=False):
138 def test_clone(no_errors=False):
139 cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
139 cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
140
140
141 try:
141 try:
142 shutil.rmtree(path, ignore_errors=True)
142 shutil.rmtree(path, ignore_errors=True)
143 os.makedirs(path)
143 os.makedirs(path)
144 #print 'made dirs %s' % jn(path)
144 #print 'made dirs %s' % jn(path)
145 except OSError:
145 except OSError:
146 raise
146 raise
147
147
148
148
149 clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s %(dest)s' % \
149 clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s %(dest)s' % \
150 {'user':USER,
150 {'user':USER,
151 'pass':PASS,
151 'pass':PASS,
152 'host':HOST,
152 'host':HOST,
153 'cloned_repo':HG_REPO,
153 'cloned_repo':HG_REPO,
154 'dest':path}
154 'dest':path}
155
155
156 stdout, stderr = Command(cwd).execute('hg clone', clone_url)
156 stdout, stderr = Command(cwd).execute('hg clone', clone_url)
157
157
158 if no_errors is False:
158 if no_errors is False:
159 assert """adding file changes""" in stdout, 'no messages about cloning'
159 assert """adding file changes""" in stdout, 'no messages about cloning'
160 assert """abort""" not in stderr , 'got error from clone'
160 assert """abort""" not in stderr , 'got error from clone'
161
161
162
162
163
163
164 def test_clone_anonymous_ok():
164 def test_clone_anonymous_ok():
165 cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
165 cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
166
166
167 try:
167 try:
168 shutil.rmtree(path, ignore_errors=True)
168 shutil.rmtree(path, ignore_errors=True)
169 os.makedirs(path)
169 os.makedirs(path)
170 #print 'made dirs %s' % jn(path)
170 #print 'made dirs %s' % jn(path)
171 except OSError:
171 except OSError:
172 raise
172 raise
173
173
174
174
175 print 'checking if anonymous access is enabled'
175 print 'checking if anonymous access is enabled'
176 anonymous_access = get_anonymous_access()
176 anonymous_access = get_anonymous_access()
177 if not anonymous_access:
177 if not anonymous_access:
178 print 'not enabled, enabling it '
178 print 'not enabled, enabling it '
179 set_anonymous_access(enable=True)
179 set_anonymous_access(enable=True)
180
180
181 clone_url = 'http://%(host)s/%(cloned_repo)s %(dest)s' % \
181 clone_url = 'http://%(host)s/%(cloned_repo)s %(dest)s' % \
182 {'user':USER,
182 {'user':USER,
183 'pass':PASS,
183 'pass':PASS,
184 'host':HOST,
184 'host':HOST,
185 'cloned_repo':HG_REPO,
185 'cloned_repo':HG_REPO,
186 'dest':path}
186 'dest':path}
187
187
188 stdout, stderr = Command(cwd).execute('hg clone', clone_url)
188 stdout, stderr = Command(cwd).execute('hg clone', clone_url)
189 print stdout, stderr
189 print stdout, stderr
190
190
191
191
192 assert """adding file changes""" in stdout, 'no messages about cloning'
192 assert """adding file changes""" in stdout, 'no messages about cloning'
193 assert """abort""" not in stderr , 'got error from clone'
193 assert """abort""" not in stderr , 'got error from clone'
194
194
195 #disable if it was enabled
195 #disable if it was enabled
196 if not anonymous_access:
196 if not anonymous_access:
197 print 'disabling anonymous access'
197 print 'disabling anonymous access'
198 set_anonymous_access(enable=False)
198 set_anonymous_access(enable=False)
199
199
200
200
201 def test_clone_wrong_credentials():
201 def test_clone_wrong_credentials():
202 cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
202 cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
203
203
204 try:
204 try:
205 shutil.rmtree(path, ignore_errors=True)
205 shutil.rmtree(path, ignore_errors=True)
206 os.makedirs(path)
206 os.makedirs(path)
207 #print 'made dirs %s' % jn(path)
207 #print 'made dirs %s' % jn(path)
208 except OSError:
208 except OSError:
209 raise
209 raise
210
210
211
211
212 clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s %(dest)s' % \
212 clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s %(dest)s' % \
213 {'user':USER + 'error',
213 {'user':USER + 'error',
214 'pass':PASS,
214 'pass':PASS,
215 'host':HOST,
215 'host':HOST,
216 'cloned_repo':HG_REPO,
216 'cloned_repo':HG_REPO,
217 'dest':path}
217 'dest':path}
218
218
219 stdout, stderr = Command(cwd).execute('hg clone', clone_url)
219 stdout, stderr = Command(cwd).execute('hg clone', clone_url)
220
220
221 assert """abort: authorization failed""" in stderr , 'no error from wrong credentials'
221 assert """abort: authorization failed""" in stderr , 'no error from wrong credentials'
222
222
223
223
224 def test_pull():
224 def test_pull():
225 pass
225 pass
226
226
227 def test_push_modify_file(f_name='setup.py'):
227 def test_push_modify_file(f_name='setup.py'):
228 cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
228 cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
229 modified_file = jn(TESTS_TMP_PATH, HG_REPO, f_name)
229 modified_file = jn(TESTS_TMP_PATH, HG_REPO, f_name)
230 for i in xrange(5):
230 for i in xrange(5):
231 cmd = """echo 'added_line%s' >> %s""" % (i, modified_file)
231 cmd = """echo 'added_line%s' >> %s""" % (i, modified_file)
232 Command(cwd).execute(cmd)
232 Command(cwd).execute(cmd)
233
233
234 cmd = """hg ci -m 'changed file %s' %s """ % (i, modified_file)
234 cmd = """hg ci -m 'changed file %s' %s """ % (i, modified_file)
235 Command(cwd).execute(cmd)
235 Command(cwd).execute(cmd)
236
236
237 Command(cwd).execute('hg push %s' % jn(TESTS_TMP_PATH, HG_REPO))
237 Command(cwd).execute('hg push %s' % jn(TESTS_TMP_PATH, HG_REPO))
238
238
239 def test_push_new_file(commits=15):
239 def test_push_new_file(commits=15):
240
240
241 test_clone(no_errors=True)
241 test_clone(no_errors=True)
242
242
243 cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
243 cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
244 added_file = jn(path, '%ssetup.py' % _RandomNameSequence().next())
244 added_file = jn(path, '%ssetupΔ…ΕΌΕΊΔ‡.py' % _RandomNameSequence().next())
245
245
246 Command(cwd).execute('touch %s' % added_file)
246 Command(cwd).execute('touch %s' % added_file)
247
247
248 Command(cwd).execute('hg add %s' % added_file)
248 Command(cwd).execute('hg add %s' % added_file)
249
249
250 for i in xrange(commits):
250 for i in xrange(commits):
251 cmd = """echo 'added_line%s' >> %s""" % (i, added_file)
251 cmd = """echo 'added_line%s' >> %s""" % (i, added_file)
252 Command(cwd).execute(cmd)
252 Command(cwd).execute(cmd)
253
253
254 cmd = """hg ci -m 'commited new %s' %s """ % (i, added_file)
254 cmd = """hg ci -m 'commited new %s' %s """ % (i, added_file)
255 Command(cwd).execute(cmd)
255 Command(cwd).execute(cmd)
256
256
257 push_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s' % \
257 push_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s' % \
258 {'user':USER,
258 {'user':USER,
259 'pass':PASS,
259 'pass':PASS,
260 'host':HOST,
260 'host':HOST,
261 'cloned_repo':HG_REPO,
261 'cloned_repo':HG_REPO,
262 'dest':jn(TESTS_TMP_PATH, HG_REPO)}
262 'dest':jn(TESTS_TMP_PATH, HG_REPO)}
263
263
264 Command(cwd).execute('hg push --verbose --debug %s' % push_url)
264 Command(cwd).execute('hg push --verbose --debug %s' % push_url)
265
265
266 def test_push_wrong_credentials():
266 def test_push_wrong_credentials():
267 cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
267 cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
268 clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s' % \
268 clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s' % \
269 {'user':USER + 'xxx',
269 {'user':USER + 'xxx',
270 'pass':PASS,
270 'pass':PASS,
271 'host':HOST,
271 'host':HOST,
272 'cloned_repo':HG_REPO,
272 'cloned_repo':HG_REPO,
273 'dest':jn(TESTS_TMP_PATH, HG_REPO)}
273 'dest':jn(TESTS_TMP_PATH, HG_REPO)}
274
274
275 modified_file = jn(TESTS_TMP_PATH, HG_REPO, 'setup.py')
275 modified_file = jn(TESTS_TMP_PATH, HG_REPO, 'setup.py')
276 for i in xrange(5):
276 for i in xrange(5):
277 cmd = """echo 'added_line%s' >> %s""" % (i, modified_file)
277 cmd = """echo 'added_line%s' >> %s""" % (i, modified_file)
278 Command(cwd).execute(cmd)
278 Command(cwd).execute(cmd)
279
279
280 cmd = """hg ci -m 'commited %s' %s """ % (i, modified_file)
280 cmd = """hg ci -m 'commited %s' %s """ % (i, modified_file)
281 Command(cwd).execute(cmd)
281 Command(cwd).execute(cmd)
282
282
283 Command(cwd).execute('hg push %s' % clone_url)
283 Command(cwd).execute('hg push %s' % clone_url)
284
284
285 def test_push_wrong_path():
285 def test_push_wrong_path():
286 cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
286 cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
287 added_file = jn(path, 'somefile.py')
287 added_file = jn(path, 'somefile.py')
288
288
289 try:
289 try:
290 shutil.rmtree(path, ignore_errors=True)
290 shutil.rmtree(path, ignore_errors=True)
291 os.makedirs(path)
291 os.makedirs(path)
292 print 'made dirs %s' % jn(path)
292 print 'made dirs %s' % jn(path)
293 except OSError:
293 except OSError:
294 raise
294 raise
295
295
296 Command(cwd).execute("""echo '' > %s""" % added_file)
296 Command(cwd).execute("""echo '' > %s""" % added_file)
297 Command(cwd).execute("""hg init %s""" % path)
297 Command(cwd).execute("""hg init %s""" % path)
298 Command(cwd).execute("""hg add %s""" % added_file)
298 Command(cwd).execute("""hg add %s""" % added_file)
299
299
300 for i in xrange(2):
300 for i in xrange(2):
301 cmd = """echo 'added_line%s' >> %s""" % (i, added_file)
301 cmd = """echo 'added_line%s' >> %s""" % (i, added_file)
302 Command(cwd).execute(cmd)
302 Command(cwd).execute(cmd)
303
303
304 cmd = """hg ci -m 'commited new %s' %s """ % (i, added_file)
304 cmd = """hg ci -m 'commited new %s' %s """ % (i, added_file)
305 Command(cwd).execute(cmd)
305 Command(cwd).execute(cmd)
306
306
307 clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s' % \
307 clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s' % \
308 {'user':USER,
308 {'user':USER,
309 'pass':PASS,
309 'pass':PASS,
310 'host':HOST,
310 'host':HOST,
311 'cloned_repo':HG_REPO + '_error',
311 'cloned_repo':HG_REPO + '_error',
312 'dest':jn(TESTS_TMP_PATH, HG_REPO)}
312 'dest':jn(TESTS_TMP_PATH, HG_REPO)}
313
313
314 stdout, stderr = Command(cwd).execute('hg push %s' % clone_url)
314 stdout, stderr = Command(cwd).execute('hg push %s' % clone_url)
315 assert """abort: HTTP Error 403: Forbidden""" in stderr
315 assert """abort: HTTP Error 403: Forbidden""" in stderr
316
316
317
317
318 if __name__ == '__main__':
318 if __name__ == '__main__':
319 create_test_user(force=False)
319 create_test_user(force=False)
320 create_test_repo()
320 create_test_repo()
321 #test_push_modify_file()
321 #test_push_modify_file()
322 #test_clone()
322 #test_clone()
323 #test_clone_anonymous_ok()
323 #test_clone_anonymous_ok()
324
324
325 #test_clone_wrong_credentials()
325 #test_clone_wrong_credentials()
326
326
327 #test_pull()
327 #test_pull()
328 test_push_new_file(commits=2)
328 test_push_new_file(commits=15)
329 #test_push_wrong_path()
329 #test_push_wrong_path()
330 #test_push_wrong_credentials()
330 #test_push_wrong_credentials()
331
331
332
332
General Comments 0
You need to be logged in to leave comments. Login now