##// END OF EJS Templates
fixes #24, added generator that generates equally distrybuted colors. Thus skipping creating one large coloring history.
marcink -
r438:0d4fceb9 default
parent child Browse files
Show More
@@ -1,199 +1,199 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # files controller for pylons
3 # files 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 21, 2010
21 Created on April 21, 2010
22 files controller for pylons
22 files controller for pylons
23 @author: marcink
23 @author: marcink
24 """
24 """
25 from mercurial import archival
25 from mercurial import archival
26 from pylons import request, response, session, tmpl_context as c, url
26 from pylons import request, response, session, tmpl_context as c, url
27 from pylons.controllers.util import redirect
27 from pylons.controllers.util import redirect
28 from pylons_app.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
28 from pylons_app.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
29 from pylons_app.lib.base import BaseController, render
29 from pylons_app.lib.base import BaseController, render
30 from pylons_app.lib.utils import EmptyChangeset, get_repo_slug
30 from pylons_app.lib.utils import EmptyChangeset
31 from pylons_app.model.hg_model import HgModel
31 from pylons_app.model.hg_model import HgModel
32 from vcs.exceptions import RepositoryError, ChangesetError
32 from vcs.exceptions import RepositoryError, ChangesetError
33 from vcs.nodes import FileNode
33 from vcs.nodes import FileNode
34 from vcs.utils import diffs as differ
34 from vcs.utils import diffs as differ
35 import logging
35 import logging
36 import pylons_app.lib.helpers as h
36 import pylons_app.lib.helpers as h
37 import tempfile
37 import tempfile
38
38
39 log = logging.getLogger(__name__)
39 log = logging.getLogger(__name__)
40
40
41 class FilesController(BaseController):
41 class FilesController(BaseController):
42
42
43 @LoginRequired()
43 @LoginRequired()
44 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
44 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
45 'repository.admin')
45 'repository.admin')
46 def __before__(self):
46 def __before__(self):
47 super(FilesController, self).__before__()
47 super(FilesController, self).__before__()
48
48
49 def index(self, repo_name, revision, f_path):
49 def index(self, repo_name, revision, f_path):
50 hg_model = HgModel()
50 hg_model = HgModel()
51 c.repo = repo = hg_model.get_repo(c.repo_name)
51 c.repo = repo = hg_model.get_repo(c.repo_name)
52 revision = request.POST.get('at_rev', None) or revision
52 revision = request.POST.get('at_rev', None) or revision
53
53
54 def get_next_rev(cur):
54 def get_next_rev(cur):
55 max_rev = len(c.repo.revisions) - 1
55 max_rev = len(c.repo.revisions) - 1
56 r = cur + 1
56 r = cur + 1
57 if r > max_rev:
57 if r > max_rev:
58 r = max_rev
58 r = max_rev
59 return r
59 return r
60
60
61 def get_prev_rev(cur):
61 def get_prev_rev(cur):
62 r = cur - 1
62 r = cur - 1
63 return r
63 return r
64
64
65 c.f_path = f_path
65 c.f_path = f_path
66
66
67
67
68 try:
68 try:
69 cur_rev = repo.get_changeset(revision).revision
69 cur_rev = repo.get_changeset(revision).revision
70 prev_rev = repo.get_changeset(get_prev_rev(cur_rev)).raw_id
70 prev_rev = repo.get_changeset(get_prev_rev(cur_rev)).raw_id
71 next_rev = repo.get_changeset(get_next_rev(cur_rev)).raw_id
71 next_rev = repo.get_changeset(get_next_rev(cur_rev)).raw_id
72
72
73 c.url_prev = url('files_home', repo_name=c.repo_name,
73 c.url_prev = url('files_home', repo_name=c.repo_name,
74 revision=prev_rev, f_path=f_path)
74 revision=prev_rev, f_path=f_path)
75 c.url_next = url('files_home', repo_name=c.repo_name,
75 c.url_next = url('files_home', repo_name=c.repo_name,
76 revision=next_rev, f_path=f_path)
76 revision=next_rev, f_path=f_path)
77
77
78 c.changeset = repo.get_changeset(revision)
78 c.changeset = repo.get_changeset(revision)
79
79
80
80
81 c.cur_rev = c.changeset.raw_id
81 c.cur_rev = c.changeset.raw_id
82 c.rev_nr = c.changeset.revision
82 c.rev_nr = c.changeset.revision
83 c.files_list = c.changeset.get_node(f_path)
83 c.files_list = c.changeset.get_node(f_path)
84 c.file_history = self._get_history(repo, c.files_list, f_path)
84 c.file_history = self._get_history(repo, c.files_list, f_path)
85
85
86 except (RepositoryError, ChangesetError):
86 except (RepositoryError, ChangesetError):
87 c.files_list = None
87 c.files_list = None
88
88
89 return render('files/files.html')
89 return render('files/files.html')
90
90
91 def rawfile(self, repo_name, revision, f_path):
91 def rawfile(self, repo_name, revision, f_path):
92 hg_model = HgModel()
92 hg_model = HgModel()
93 c.repo = hg_model.get_repo(c.repo_name)
93 c.repo = hg_model.get_repo(c.repo_name)
94 file_node = c.repo.get_changeset(revision).get_node(f_path)
94 file_node = c.repo.get_changeset(revision).get_node(f_path)
95 response.content_type = file_node.mimetype
95 response.content_type = file_node.mimetype
96 response.content_disposition = 'attachment; filename=%s' \
96 response.content_disposition = 'attachment; filename=%s' \
97 % f_path.split('/')[-1]
97 % f_path.split('/')[-1]
98 return file_node.content
98 return file_node.content
99
99
100 def annotate(self, repo_name, revision, f_path):
100 def annotate(self, repo_name, revision, f_path):
101 hg_model = HgModel()
101 hg_model = HgModel()
102 c.repo = hg_model.get_repo(c.repo_name)
102 c.repo = hg_model.get_repo(c.repo_name)
103 cs = c.repo.get_changeset(revision)
103 cs = c.repo.get_changeset(revision)
104 c.file = cs.get_node(f_path)
104 c.file = cs.get_node(f_path)
105 c.file_msg = cs.get_file_message(f_path)
105 c.file_msg = cs.get_file_message(f_path)
106 c.cur_rev = cs.raw_id
106 c.cur_rev = cs.raw_id
107 c.rev_nr = cs.revision
107 c.rev_nr = cs.revision
108 c.f_path = f_path
108 c.f_path = f_path
109
109
110 return render('files/files_annotate.html')
110 return render('files/files_annotate.html')
111
111
112 def archivefile(self, repo_name, revision, fileformat):
112 def archivefile(self, repo_name, revision, fileformat):
113 archive_specs = {
113 archive_specs = {
114 '.tar.bz2': ('application/x-tar', 'tbz2'),
114 '.tar.bz2': ('application/x-tar', 'tbz2'),
115 '.tar.gz': ('application/x-tar', 'tgz'),
115 '.tar.gz': ('application/x-tar', 'tgz'),
116 '.zip': ('application/zip', 'zip'),
116 '.zip': ('application/zip', 'zip'),
117 }
117 }
118 if not archive_specs.has_key(fileformat):
118 if not archive_specs.has_key(fileformat):
119 return 'Unknown archive type %s' % fileformat
119 return 'Unknown archive type %s' % fileformat
120
120
121 def read_in_chunks(file_object, chunk_size=1024 * 40):
121 def read_in_chunks(file_object, chunk_size=1024 * 40):
122 """Lazy function (generator) to read a file piece by piece.
122 """Lazy function (generator) to read a file piece by piece.
123 Default chunk size: 40k."""
123 Default chunk size: 40k."""
124 while True:
124 while True:
125 data = file_object.read(chunk_size)
125 data = file_object.read(chunk_size)
126 if not data:
126 if not data:
127 break
127 break
128 yield data
128 yield data
129
129
130 archive = tempfile.TemporaryFile()
130 archive = tempfile.TemporaryFile()
131 repo = HgModel().get_repo(repo_name).repo
131 repo = HgModel().get_repo(repo_name).repo
132 fname = '%s-%s%s' % (repo_name, revision, fileformat)
132 fname = '%s-%s%s' % (repo_name, revision, fileformat)
133 archival.archive(repo, archive, revision, archive_specs[fileformat][1],
133 archival.archive(repo, archive, revision, archive_specs[fileformat][1],
134 prefix='%s-%s' % (repo_name, revision))
134 prefix='%s-%s' % (repo_name, revision))
135 response.content_type = archive_specs[fileformat][0]
135 response.content_type = archive_specs[fileformat][0]
136 response.content_disposition = 'attachment; filename=%s' % fname
136 response.content_disposition = 'attachment; filename=%s' % fname
137 archive.seek(0)
137 archive.seek(0)
138 return read_in_chunks(archive)
138 return read_in_chunks(archive)
139
139
140 def diff(self, repo_name, f_path):
140 def diff(self, repo_name, f_path):
141 hg_model = HgModel()
141 hg_model = HgModel()
142 diff1 = request.GET.get('diff1')
142 diff1 = request.GET.get('diff1')
143 diff2 = request.GET.get('diff2')
143 diff2 = request.GET.get('diff2')
144 c.action = request.GET.get('diff')
144 c.action = request.GET.get('diff')
145 c.no_changes = diff1 == diff2
145 c.no_changes = diff1 == diff2
146 c.f_path = f_path
146 c.f_path = f_path
147 c.repo = hg_model.get_repo(c.repo_name)
147 c.repo = hg_model.get_repo(c.repo_name)
148
148
149 try:
149 try:
150 if diff1 not in ['', None, 'None', '0' * 12]:
150 if diff1 not in ['', None, 'None', '0' * 12]:
151 c.changeset_1 = c.repo.get_changeset(diff1)
151 c.changeset_1 = c.repo.get_changeset(diff1)
152 node1 = c.changeset_1.get_node(f_path)
152 node1 = c.changeset_1.get_node(f_path)
153 else:
153 else:
154 c.changeset_1 = EmptyChangeset()
154 c.changeset_1 = EmptyChangeset()
155 node1 = FileNode('.', '')
155 node1 = FileNode('.', '')
156 if diff2 not in ['', None, 'None', '0' * 12]:
156 if diff2 not in ['', None, 'None', '0' * 12]:
157 c.changeset_2 = c.repo.get_changeset(diff2)
157 c.changeset_2 = c.repo.get_changeset(diff2)
158 node2 = c.changeset_2.get_node(f_path)
158 node2 = c.changeset_2.get_node(f_path)
159 else:
159 else:
160 c.changeset_2 = EmptyChangeset()
160 c.changeset_2 = EmptyChangeset()
161 node2 = FileNode('.', '')
161 node2 = FileNode('.', '')
162 except RepositoryError:
162 except RepositoryError:
163 return redirect(url('files_home',
163 return redirect(url('files_home',
164 repo_name=c.repo_name, f_path=f_path))
164 repo_name=c.repo_name, f_path=f_path))
165
165
166 c.diff1 = 'r%s:%s' % (c.changeset_1.revision, c.changeset_1.raw_id)
166 c.diff1 = 'r%s:%s' % (c.changeset_1.revision, c.changeset_1.raw_id)
167 c.diff2 = 'r%s:%s' % (c.changeset_2.revision, c.changeset_2.raw_id)
167 c.diff2 = 'r%s:%s' % (c.changeset_2.revision, c.changeset_2.raw_id)
168 f_udiff = differ.get_udiff(node1, node2)
168 f_udiff = differ.get_udiff(node1, node2)
169
169
170 diff = differ.DiffProcessor(f_udiff)
170 diff = differ.DiffProcessor(f_udiff)
171
171
172 if c.action == 'download':
172 if c.action == 'download':
173 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
173 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
174 response.content_type = 'text/plain'
174 response.content_type = 'text/plain'
175 response.content_disposition = 'attachment; filename=%s' \
175 response.content_disposition = 'attachment; filename=%s' \
176 % diff_name
176 % diff_name
177 return diff.raw_diff()
177 return diff.raw_diff()
178
178
179 elif c.action == 'raw':
179 elif c.action == 'raw':
180 c.cur_diff = '<pre class="raw">%s</pre>' % h.escape(diff.raw_diff())
180 c.cur_diff = '<pre class="raw">%s</pre>' % h.escape(diff.raw_diff())
181 elif c.action == 'diff':
181 elif c.action == 'diff':
182 c.cur_diff = diff.as_html()
182 c.cur_diff = diff.as_html()
183 else:
183 else:
184 #default option
184 #default option
185 c.cur_diff = diff.as_html()
185 c.cur_diff = diff.as_html()
186
186
187 if not c.cur_diff: c.no_changes = True
187 if not c.cur_diff: c.no_changes = True
188 return render('files/file_diff.html')
188 return render('files/file_diff.html')
189
189
190 def _get_history(self, repo, node, f_path):
190 def _get_history(self, repo, node, f_path):
191 from vcs.nodes import NodeKind
191 from vcs.nodes import NodeKind
192 if not node.kind is NodeKind.FILE:
192 if not node.kind is NodeKind.FILE:
193 return []
193 return []
194 changesets = node.history
194 changesets = node.history
195 hist_l = []
195 hist_l = []
196 for chs in changesets:
196 for chs in changesets:
197 n_desc = 'r%s:%s' % (chs.revision, chs._short)
197 n_desc = 'r%s:%s' % (chs.revision, chs._short)
198 hist_l.append((chs._short, n_desc,))
198 hist_l.append((chs._short, n_desc,))
199 return hist_l
199 return hist_l
@@ -1,33 +1,31 b''
1 """The application's Globals object"""
1 """The application's Globals object"""
2
2
3 from beaker.cache import CacheManager
3 from beaker.cache import CacheManager
4 from beaker.util import parse_cache_config_options
4 from beaker.util import parse_cache_config_options
5 from vcs.utils.lazy import LazyProperty
5 from vcs.utils.lazy import LazyProperty
6
6
7 class Globals(object):
7 class Globals(object):
8 """Globals acts as a container for objects available throughout the
8 """Globals acts as a container for objects available throughout the
9 life of the application
9 life of the application
10
10
11 """
11 """
12
12
13 def __init__(self, config):
13 def __init__(self, config):
14 """One instance of Globals is created during application
14 """One instance of Globals is created during application
15 initialization and is available during requests via the
15 initialization and is available during requests via the
16 'app_globals' variable
16 'app_globals' variable
17
17
18 """
18 """
19 self.cache = CacheManager(**parse_cache_config_options(config))
19 self.cache = CacheManager(**parse_cache_config_options(config))
20 self.changeset_annotation_colors = {}
21 self.available_permissions = None # propagated after init_model
20 self.available_permissions = None # propagated after init_model
22 self.app_title = None # propagated after init_model
23 self.baseui = None # propagated after init_model
21 self.baseui = None # propagated after init_model
24
22
25 @LazyProperty
23 @LazyProperty
26 def paths(self):
24 def paths(self):
27 if self.baseui:
25 if self.baseui:
28 return self.baseui.configitems('paths')
26 return self.baseui.configitems('paths')
29
27
30 @LazyProperty
28 @LazyProperty
31 def base_path(self):
29 def base_path(self):
32 if self.baseui:
30 if self.baseui:
33 return self.paths[0][1].replace('*', '')
31 return self.paths[0][1].replace('*', '')
@@ -1,325 +1,338 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
28 #Custom helpers here :)
27 #Custom helpers here :)
29 class _Link(object):
28 class _Link(object):
30 '''
29 '''
31 Make a url based on label and url with help of url_for
30 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
31 @param label:name of link if not defined url is used
33 @param url: the url for link
32 @param url: the url for link
34 '''
33 '''
35
34
36 def __call__(self, label='', *url_, **urlargs):
35 def __call__(self, label='', *url_, **urlargs):
37 if label is None or '':
36 if label is None or '':
38 label = url
37 label = url
39 link_fn = link_to(label, url(*url_, **urlargs))
38 link_fn = link_to(label, url(*url_, **urlargs))
40 return link_fn
39 return link_fn
41
40
42 link = _Link()
41 link = _Link()
43
42
44 class _GetError(object):
43 class _GetError(object):
45
44
46 def __call__(self, field_name, form_errors):
45 def __call__(self, field_name, form_errors):
47 tmpl = """<span class="error_msg">%s</span>"""
46 tmpl = """<span class="error_msg">%s</span>"""
48 if form_errors and form_errors.has_key(field_name):
47 if form_errors and form_errors.has_key(field_name):
49 return literal(tmpl % form_errors.get(field_name))
48 return literal(tmpl % form_errors.get(field_name))
50
49
51 get_error = _GetError()
50 get_error = _GetError()
52
51
53 def recursive_replace(str, replace=' '):
52 def recursive_replace(str, replace=' '):
54 """
53 """
55 Recursive replace of given sign to just one instance
54 Recursive replace of given sign to just one instance
56 @param str: given string
55 @param str: given string
57 @param replace:char to find and replace multiple instances
56 @param replace:char to find and replace multiple instances
58
57
59 Examples::
58 Examples::
60 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
59 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
61 'Mighty-Mighty-Bo-sstones'
60 'Mighty-Mighty-Bo-sstones'
62 """
61 """
63
62
64 if str.find(replace * 2) == -1:
63 if str.find(replace * 2) == -1:
65 return str
64 return str
66 else:
65 else:
67 str = str.replace(replace * 2, replace)
66 str = str.replace(replace * 2, replace)
68 return recursive_replace(str, replace)
67 return recursive_replace(str, replace)
69
68
70 class _ToolTip(object):
69 class _ToolTip(object):
71
70
72 def __call__(self, tooltip_title, trim_at=50):
71 def __call__(self, tooltip_title, trim_at=50):
73 """
72 """
74 Special function just to wrap our text into nice formatted autowrapped
73 Special function just to wrap our text into nice formatted autowrapped
75 text
74 text
76 @param tooltip_title:
75 @param tooltip_title:
77 """
76 """
78
77
79 return literal(wrap_paragraphs(tooltip_title, trim_at)\
78 return literal(wrap_paragraphs(tooltip_title, trim_at)\
80 .replace('\n', '<br/>'))
79 .replace('\n', '<br/>'))
81
80
82 def activate(self):
81 def activate(self):
83 """
82 """
84 Adds tooltip mechanism to the given Html all tooltips have to have
83 Adds tooltip mechanism to the given Html all tooltips have to have
85 set class tooltip and set attribute tooltip_title.
84 set class tooltip and set attribute tooltip_title.
86 Then a tooltip will be generated based on that
85 Then a tooltip will be generated based on that
87 All with yui js tooltip
86 All with yui js tooltip
88 """
87 """
89
88
90 js = '''
89 js = '''
91 YAHOO.util.Event.onDOMReady(function(){
90 YAHOO.util.Event.onDOMReady(function(){
92 function toolTipsId(){
91 function toolTipsId(){
93 var ids = [];
92 var ids = [];
94 var tts = YAHOO.util.Dom.getElementsByClassName('tooltip');
93 var tts = YAHOO.util.Dom.getElementsByClassName('tooltip');
95
94
96 for (var i = 0; i < tts.length; i++) {
95 for (var i = 0; i < tts.length; i++) {
97 //if element doesn not have and id autgenerate one for tooltip
96 //if element doesn not have and id autgenerate one for tooltip
98
97
99 if (!tts[i].id){
98 if (!tts[i].id){
100 tts[i].id='tt'+i*100;
99 tts[i].id='tt'+i*100;
101 }
100 }
102 ids.push(tts[i].id);
101 ids.push(tts[i].id);
103 }
102 }
104 return ids
103 return ids
105 };
104 };
106 var myToolTips = new YAHOO.widget.Tooltip("tooltip", {
105 var myToolTips = new YAHOO.widget.Tooltip("tooltip", {
107 context: toolTipsId(),
106 context: toolTipsId(),
108 monitorresize:false,
107 monitorresize:false,
109 xyoffset :[0,0],
108 xyoffset :[0,0],
110 autodismissdelay:300000,
109 autodismissdelay:300000,
111 hidedelay:5,
110 hidedelay:5,
112 showdelay:20,
111 showdelay:20,
113 });
112 });
114
113
115 //Mouse Over event disabled for new repositories since they dont
114 //Mouse Over event disabled for new repositories since they dont
116 //have last commit message
115 //have last commit message
117 myToolTips.contextMouseOverEvent.subscribe(
116 myToolTips.contextMouseOverEvent.subscribe(
118 function(type, args) {
117 function(type, args) {
119 var context = args[0];
118 var context = args[0];
120 var txt = context.getAttribute('tooltip_title');
119 var txt = context.getAttribute('tooltip_title');
121 if(txt){
120 if(txt){
122 return true;
121 return true;
123 }
122 }
124 else{
123 else{
125 return false;
124 return false;
126 }
125 }
127 });
126 });
128
127
129
128
130 // Set the text for the tooltip just before we display it. Lazy method
129 // Set the text for the tooltip just before we display it. Lazy method
131 myToolTips.contextTriggerEvent.subscribe(
130 myToolTips.contextTriggerEvent.subscribe(
132 function(type, args) {
131 function(type, args) {
133
132
134
133
135 var context = args[0];
134 var context = args[0];
136
135
137 var txt = context.getAttribute('tooltip_title');
136 var txt = context.getAttribute('tooltip_title');
138 this.cfg.setProperty("text", txt);
137 this.cfg.setProperty("text", txt);
139
138
140
139
141 // positioning of tooltip
140 // positioning of tooltip
142 var tt_w = this.element.clientWidth;
141 var tt_w = this.element.clientWidth;
143 var tt_h = this.element.clientHeight;
142 var tt_h = this.element.clientHeight;
144
143
145 var context_w = context.offsetWidth;
144 var context_w = context.offsetWidth;
146 var context_h = context.offsetHeight;
145 var context_h = context.offsetHeight;
147
146
148 var pos_x = YAHOO.util.Dom.getX(context);
147 var pos_x = YAHOO.util.Dom.getX(context);
149 var pos_y = YAHOO.util.Dom.getY(context);
148 var pos_y = YAHOO.util.Dom.getY(context);
150
149
151 var display_strategy = 'top';
150 var display_strategy = 'top';
152 var xy_pos = [0,0];
151 var xy_pos = [0,0];
153 switch (display_strategy){
152 switch (display_strategy){
154
153
155 case 'top':
154 case 'top':
156 var cur_x = (pos_x+context_w/2)-(tt_w/2);
155 var cur_x = (pos_x+context_w/2)-(tt_w/2);
157 var cur_y = pos_y-tt_h-4;
156 var cur_y = pos_y-tt_h-4;
158 xy_pos = [cur_x,cur_y];
157 xy_pos = [cur_x,cur_y];
159 break;
158 break;
160 case 'bottom':
159 case 'bottom':
161 var cur_x = (pos_x+context_w/2)-(tt_w/2);
160 var cur_x = (pos_x+context_w/2)-(tt_w/2);
162 var cur_y = pos_y+context_h+4;
161 var cur_y = pos_y+context_h+4;
163 xy_pos = [cur_x,cur_y];
162 xy_pos = [cur_x,cur_y];
164 break;
163 break;
165 case 'left':
164 case 'left':
166 var cur_x = (pos_x-tt_w-4);
165 var cur_x = (pos_x-tt_w-4);
167 var cur_y = pos_y-((tt_h/2)-context_h/2);
166 var cur_y = pos_y-((tt_h/2)-context_h/2);
168 xy_pos = [cur_x,cur_y];
167 xy_pos = [cur_x,cur_y];
169 break;
168 break;
170 case 'right':
169 case 'right':
171 var cur_x = (pos_x+context_w+4);
170 var cur_x = (pos_x+context_w+4);
172 var cur_y = pos_y-((tt_h/2)-context_h/2);
171 var cur_y = pos_y-((tt_h/2)-context_h/2);
173 xy_pos = [cur_x,cur_y];
172 xy_pos = [cur_x,cur_y];
174 break;
173 break;
175 default:
174 default:
176 var cur_x = (pos_x+context_w/2)-(tt_w/2);
175 var cur_x = (pos_x+context_w/2)-(tt_w/2);
177 var cur_y = pos_y-tt_h-4;
176 var cur_y = pos_y-tt_h-4;
178 xy_pos = [cur_x,cur_y];
177 xy_pos = [cur_x,cur_y];
179 break;
178 break;
180
179
181 }
180 }
182
181
183 this.cfg.setProperty("xy",xy_pos);
182 this.cfg.setProperty("xy",xy_pos);
184
183
185 });
184 });
186
185
187 //Mouse out
186 //Mouse out
188 myToolTips.contextMouseOutEvent.subscribe(
187 myToolTips.contextMouseOutEvent.subscribe(
189 function(type, args) {
188 function(type, args) {
190 var context = args[0];
189 var context = args[0];
191
190
192 });
191 });
193 });
192 });
194 '''
193 '''
195 return literal(js)
194 return literal(js)
196
195
197 tooltip = _ToolTip()
196 tooltip = _ToolTip()
198
197
199 class _FilesBreadCrumbs(object):
198 class _FilesBreadCrumbs(object):
200
199
201 def __call__(self, repo_name, rev, paths):
200 def __call__(self, repo_name, rev, paths):
202 url_l = [link_to(repo_name, url('files_home',
201 url_l = [link_to(repo_name, url('files_home',
203 repo_name=repo_name,
202 repo_name=repo_name,
204 revision=rev, f_path=''))]
203 revision=rev, f_path=''))]
205 paths_l = paths.split('/')
204 paths_l = paths.split('/')
206
205
207 for cnt, p in enumerate(paths_l, 1):
206 for cnt, p in enumerate(paths_l, 1):
208 if p != '':
207 if p != '':
209 url_l.append(link_to(p, url('files_home',
208 url_l.append(link_to(p, url('files_home',
210 repo_name=repo_name,
209 repo_name=repo_name,
211 revision=rev,
210 revision=rev,
212 f_path='/'.join(paths_l[:cnt]))))
211 f_path='/'.join(paths_l[:cnt]))))
213
212
214 return literal(' / '.join(url_l))
213 return literal(' / '.join(url_l))
215
214
216 files_breadcrumbs = _FilesBreadCrumbs()
215 files_breadcrumbs = _FilesBreadCrumbs()
217
216
218 def pygmentize(filenode, **kwargs):
217 def pygmentize(filenode, **kwargs):
219 """
218 """
220 pygmentize function using pygments
219 pygmentize function using pygments
221 @param filenode:
220 @param filenode:
222 """
221 """
223 return literal(code_highlight(filenode.content,
222 return literal(code_highlight(filenode.content,
224 filenode.lexer, HtmlFormatter(**kwargs)))
223 filenode.lexer, HtmlFormatter(**kwargs)))
225
224
226 def pygmentize_annotation(filenode, **kwargs):
225 def pygmentize_annotation(filenode, **kwargs):
227 """
226 """
228 pygmentize function for annotation
227 pygmentize function for annotation
229 @param filenode:
228 @param filenode:
230 """
229 """
231
230
232 color_dict = g.changeset_annotation_colors
231 color_dict = {}
233 def gen_color():
232 def gen_color():
234 import random
233 """generator for getting 10k of evenly distibuted colors using hsv color
235 return [str(random.randrange(10, 235)) for _ in xrange(3)]
234 and golden ratio.
235 """
236 import colorsys
237 n = 10000
238 golden_ratio = 0.618033988749895
239 h = 0.22717784590367374
240 #generate 10k nice web friendly colors in the same order
241 for c in xrange(n):
242 h +=golden_ratio
243 h %= 1
244 HSV_tuple = [h, 0.95, 0.95]
245 RGB_tuple = colorsys.hsv_to_rgb(*HSV_tuple)
246 yield map(lambda x:str(int(x*256)),RGB_tuple)
247
248 cgenerator = gen_color()
249
236 def get_color_string(cs):
250 def get_color_string(cs):
237 if color_dict.has_key(cs):
251 if color_dict.has_key(cs):
238 col = color_dict[cs]
252 col = color_dict[cs]
239 else:
253 else:
240 color_dict[cs] = gen_color()
254 col = color_dict[cs] = cgenerator.next()
241 col = color_dict[cs]
255 return "color: rgb(%s)! important;" % (', '.join(col))
242 return "color: rgb(%s) ! important;" % (', '.join(col))
243
256
244 def url_func(changeset):
257 def url_func(changeset):
245 tooltip_html = "<div style='font-size:0.8em'><b>Author:</b>"+\
258 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>"
259 " %s<br/><b>Date:</b> %s</b><br/><b>Message:</b> %s<br/></div>"
247
260
248 tooltip_html = tooltip_html % (changeset.author,
261 tooltip_html = tooltip_html % (changeset.author,
249 changeset.date,
262 changeset.date,
250 tooltip(changeset.message))
263 tooltip(changeset.message))
251 lnk_format = 'r%s:%s' % (changeset.revision,
264 lnk_format = 'r%-5s:%s' % (changeset.revision,
252 changeset.raw_id)
265 changeset.raw_id)
253 uri = link_to(
266 uri = link_to(
254 lnk_format,
267 lnk_format,
255 url('changeset_home', repo_name=changeset.repository.name,
268 url('changeset_home', repo_name=changeset.repository.name,
256 revision=changeset.raw_id),
269 revision=changeset.raw_id),
257 style=get_color_string(changeset.raw_id),
270 style=get_color_string(changeset.raw_id),
258 class_='tooltip',
271 class_='tooltip',
259 tooltip_title=tooltip_html
272 tooltip_title=tooltip_html
260 )
273 )
261
274
262 uri += '\n'
275 uri += '\n'
263 return uri
276 return uri
264 return literal(annotate_highlight(filenode, url_func, **kwargs))
277 return literal(annotate_highlight(filenode, url_func, **kwargs))
265
278
266 def repo_name_slug(value):
279 def repo_name_slug(value):
267 """
280 """
268 Return slug of name of repository
281 Return slug of name of repository
269 """
282 """
270 slug = urlify(value)
283 slug = urlify(value)
271 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|:""":
284 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|:""":
272 slug = slug.replace(c, '-')
285 slug = slug.replace(c, '-')
273 slug = recursive_replace(slug, '-')
286 slug = recursive_replace(slug, '-')
274 return slug
287 return slug
275
288
276 flash = _Flash()
289 flash = _Flash()
277
290
278
291
279 #===============================================================================
292 #===============================================================================
280 # MERCURIAL FILTERS available via h.
293 # MERCURIAL FILTERS available via h.
281 #===============================================================================
294 #===============================================================================
282 from mercurial import util
295 from mercurial import util
283 from mercurial.templatefilters import age as _age, person as _person
296 from mercurial.templatefilters import age as _age, person as _person
284
297
285 age = lambda x:_age(x)
298 age = lambda x:_age(x)
286 capitalize = lambda x: x.capitalize()
299 capitalize = lambda x: x.capitalize()
287 date = lambda x: util.datestr(x)
300 date = lambda x: util.datestr(x)
288 email = util.email
301 email = util.email
289 email_or_none = lambda x: util.email(x) if util.email(x) != x else None
302 email_or_none = lambda x: util.email(x) if util.email(x) != x else None
290 person = lambda x: _person(x)
303 person = lambda x: _person(x)
291 hgdate = lambda x: "%d %d" % x
304 hgdate = lambda x: "%d %d" % x
292 isodate = lambda x: util.datestr(x, '%Y-%m-%d %H:%M %1%2')
305 isodate = lambda x: util.datestr(x, '%Y-%m-%d %H:%M %1%2')
293 isodatesec = lambda x: util.datestr(x, '%Y-%m-%d %H:%M:%S %1%2')
306 isodatesec = lambda x: util.datestr(x, '%Y-%m-%d %H:%M:%S %1%2')
294 localdate = lambda x: (x[0], util.makedate()[1])
307 localdate = lambda x: (x[0], util.makedate()[1])
295 rfc822date = lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S %1%2")
308 rfc822date = lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S %1%2")
296 rfc3339date = lambda x: util.datestr(x, "%Y-%m-%dT%H:%M:%S%1:%2")
309 rfc3339date = lambda x: util.datestr(x, "%Y-%m-%dT%H:%M:%S%1:%2")
297 time_ago = lambda x: util.datestr(_age(x), "%a, %d %b %Y %H:%M:%S %1%2")
310 time_ago = lambda x: util.datestr(_age(x), "%a, %d %b %Y %H:%M:%S %1%2")
298
311
299
312
300 #===============================================================================
313 #===============================================================================
301 # PERMS
314 # PERMS
302 #===============================================================================
315 #===============================================================================
303 from pylons_app.lib.auth import HasPermissionAny, HasPermissionAll, \
316 from pylons_app.lib.auth import HasPermissionAny, HasPermissionAll, \
304 HasRepoPermissionAny, HasRepoPermissionAll
317 HasRepoPermissionAny, HasRepoPermissionAll
305
318
306 #===============================================================================
319 #===============================================================================
307 # GRAVATAR URL
320 # GRAVATAR URL
308 #===============================================================================
321 #===============================================================================
309 import hashlib
322 import hashlib
310 import urllib
323 import urllib
311 from pylons import request
324 from pylons import request
312
325
313 def gravatar_url(email_address, size=30):
326 def gravatar_url(email_address, size=30):
314 ssl_enabled = 'https' == request.environ.get('HTTP_X_URL_SCHEME')
327 ssl_enabled = 'https' == request.environ.get('HTTP_X_URL_SCHEME')
315 default = 'identicon'
328 default = 'identicon'
316 baseurl_nossl = "http://www.gravatar.com/avatar/"
329 baseurl_nossl = "http://www.gravatar.com/avatar/"
317 baseurl_ssl = "https://secure.gravatar.com/avatar/"
330 baseurl_ssl = "https://secure.gravatar.com/avatar/"
318 baseurl = baseurl_ssl if ssl_enabled else baseurl_nossl
331 baseurl = baseurl_ssl if ssl_enabled else baseurl_nossl
319
332
320
333
321 # construct the url
334 # construct the url
322 gravatar_url = baseurl + hashlib.md5(email_address.lower()).hexdigest() + "?"
335 gravatar_url = baseurl + hashlib.md5(email_address.lower()).hexdigest() + "?"
323 gravatar_url += urllib.urlencode({'d':default, 's':str(size)})
336 gravatar_url += urllib.urlencode({'d':default, 's':str(size)})
324
337
325 return gravatar_url
338 return gravatar_url
General Comments 0
You need to be logged in to leave comments. Login now