##// END OF EJS Templates
Added message tooltip into journal revisions in push....
marcink -
r899:d65843e0 beta
parent child Browse files
Show More
@@ -1,273 +1,268 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.files
3 rhodecode.controllers.files
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Files controller for RhodeCode
6 Files controller for RhodeCode
7
7
8 :created_on: Apr 21, 2010
8 :created_on: Apr 21, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27 import tempfile
27 import tempfile
28 import logging
28 import logging
29 import rhodecode.lib.helpers as h
29 import rhodecode.lib.helpers as h
30
30
31 from mercurial import archival
31 from mercurial import archival
32
32
33 from pylons import request, response, session, tmpl_context as c, url
33 from pylons import request, response, session, tmpl_context as c, url
34 from pylons.i18n.translation import _
34 from pylons.i18n.translation import _
35 from pylons.controllers.util import redirect
35 from pylons.controllers.util import redirect
36
36
37 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
37 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
38 from rhodecode.lib.base import BaseController, render
38 from rhodecode.lib.base import BaseController, render
39 from rhodecode.lib.utils import EmptyChangeset
39 from rhodecode.lib.utils import EmptyChangeset
40 from rhodecode.model.scm import ScmModel
40 from rhodecode.model.scm import ScmModel
41
41
42 from vcs.exceptions import RepositoryError, ChangesetError, ChangesetDoesNotExistError
42 from vcs.exceptions import RepositoryError, ChangesetError, ChangesetDoesNotExistError
43 from vcs.nodes import FileNode
43 from vcs.nodes import FileNode
44 from vcs.utils import diffs as differ
44 from vcs.utils import diffs as differ
45
45
46 log = logging.getLogger(__name__)
46 log = logging.getLogger(__name__)
47
47
48 class FilesController(BaseController):
48 class FilesController(BaseController):
49
49
50 @LoginRequired()
50 @LoginRequired()
51 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
51 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
52 'repository.admin')
52 'repository.admin')
53 def __before__(self):
53 def __before__(self):
54 super(FilesController, self).__before__()
54 super(FilesController, self).__before__()
55 c.cut_off_limit = self.cut_off_limit
55 c.cut_off_limit = self.cut_off_limit
56
56
57 def index(self, repo_name, revision, f_path):
57 def index(self, repo_name, revision, f_path):
58 hg_model = ScmModel()
58 hg_model = ScmModel()
59 c.repo = hg_model.get_repo(c.repo_name)
59 c.repo = hg_model.get_repo(c.repo_name)
60
60
61 try:
61 try:
62 #reditect to given revision from form
62 #reditect to given revision from form
63 post_revision = request.POST.get('at_rev', None)
63 post_revision = request.POST.get('at_rev', None)
64 if post_revision:
64 if post_revision:
65 post_revision = c.repo.get_changeset(post_revision).raw_id
65 post_revision = c.repo.get_changeset(post_revision).raw_id
66 redirect(url('files_home', repo_name=c.repo_name,
66 redirect(url('files_home', repo_name=c.repo_name,
67 revision=post_revision, f_path=f_path))
67 revision=post_revision, f_path=f_path))
68
68
69 c.branch = request.GET.get('branch', None)
69 c.branch = request.GET.get('branch', None)
70
70
71 c.f_path = f_path
71 c.f_path = f_path
72
72
73 c.changeset = c.repo.get_changeset(revision)
73 c.changeset = c.repo.get_changeset(revision)
74 cur_rev = c.changeset.revision
74 cur_rev = c.changeset.revision
75
75
76 #prev link
76 #prev link
77 try:
77 try:
78 prev_rev = c.repo.get_changeset(cur_rev).prev(c.branch).raw_id
78 prev_rev = c.repo.get_changeset(cur_rev).prev(c.branch).raw_id
79 c.url_prev = url('files_home', repo_name=c.repo_name,
79 c.url_prev = url('files_home', repo_name=c.repo_name,
80 revision=prev_rev, f_path=f_path)
80 revision=prev_rev, f_path=f_path)
81 if c.branch:
81 if c.branch:
82 c.url_prev += '?branch=%s' % c.branch
82 c.url_prev += '?branch=%s' % c.branch
83 except ChangesetDoesNotExistError:
83 except ChangesetDoesNotExistError:
84 c.url_prev = '#'
84 c.url_prev = '#'
85
85
86 #next link
86 #next link
87 try:
87 try:
88 next_rev = c.repo.get_changeset(cur_rev).next(c.branch).raw_id
88 next_rev = c.repo.get_changeset(cur_rev).next(c.branch).raw_id
89 c.url_next = url('files_home', repo_name=c.repo_name,
89 c.url_next = url('files_home', repo_name=c.repo_name,
90 revision=next_rev, f_path=f_path)
90 revision=next_rev, f_path=f_path)
91 if c.branch:
91 if c.branch:
92 c.url_next += '?branch=%s' % c.branch
92 c.url_next += '?branch=%s' % c.branch
93 except ChangesetDoesNotExistError:
93 except ChangesetDoesNotExistError:
94 c.url_next = '#'
94 c.url_next = '#'
95
95
96 #files
96 #files
97 try:
97 try:
98 c.files_list = c.changeset.get_node(f_path)
98 c.files_list = c.changeset.get_node(f_path)
99 c.file_history = self._get_history(c.repo, c.files_list, f_path)
99 c.file_history = self._get_history(c.repo, c.files_list, f_path)
100 except RepositoryError, e:
100 except RepositoryError, e:
101 h.flash(str(e), category='warning')
101 h.flash(str(e), category='warning')
102 redirect(h.url('files_home', repo_name=repo_name, revision=revision))
102 redirect(h.url('files_home', repo_name=repo_name, revision=revision))
103
103
104 except RepositoryError, e:
104 except RepositoryError, e:
105 h.flash(str(e), category='warning')
105 h.flash(str(e), category='warning')
106 redirect(h.url('files_home', repo_name=repo_name, revision='tip'))
106 redirect(h.url('files_home', repo_name=repo_name, revision='tip'))
107
107
108
108
109
109
110 return render('files/files.html')
110 return render('files/files.html')
111
111
112 def rawfile(self, repo_name, revision, f_path):
112 def rawfile(self, repo_name, revision, f_path):
113 hg_model = ScmModel()
113 hg_model = ScmModel()
114 c.repo = hg_model.get_repo(c.repo_name)
114 c.repo = hg_model.get_repo(c.repo_name)
115 file_node = c.repo.get_changeset(revision).get_node(f_path)
115 file_node = c.repo.get_changeset(revision).get_node(f_path)
116 response.content_type = file_node.mimetype
116 response.content_type = file_node.mimetype
117 response.content_disposition = 'attachment; filename=%s' \
117 response.content_disposition = 'attachment; filename=%s' \
118 % f_path.split('/')[-1]
118 % f_path.split('/')[-1]
119 return file_node.content
119 return file_node.content
120
120
121 def raw(self, repo_name, revision, f_path):
121 def raw(self, repo_name, revision, f_path):
122 hg_model = ScmModel()
122 hg_model = ScmModel()
123 c.repo = hg_model.get_repo(c.repo_name)
123 c.repo = hg_model.get_repo(c.repo_name)
124 file_node = c.repo.get_changeset(revision).get_node(f_path)
124 file_node = c.repo.get_changeset(revision).get_node(f_path)
125 response.content_type = 'text/plain'
125 response.content_type = 'text/plain'
126
126
127 return file_node.content
127 return file_node.content
128
128
129 def annotate(self, repo_name, revision, f_path):
129 def annotate(self, repo_name, revision, f_path):
130 hg_model = ScmModel()
130 hg_model = ScmModel()
131 c.repo = hg_model.get_repo(c.repo_name)
131 c.repo = hg_model.get_repo(c.repo_name)
132
132
133 try:
133 try:
134 c.cs = c.repo.get_changeset(revision)
134 c.cs = c.repo.get_changeset(revision)
135 c.file = c.cs.get_node(f_path)
135 c.file = c.cs.get_node(f_path)
136 except RepositoryError, e:
136 except RepositoryError, e:
137 h.flash(str(e), category='warning')
137 h.flash(str(e), category='warning')
138 redirect(h.url('files_home', repo_name=repo_name, revision=revision))
138 redirect(h.url('files_home', repo_name=repo_name, revision=revision))
139
139
140 c.file_history = self._get_history(c.repo, c.file, f_path)
140 c.file_history = self._get_history(c.repo, c.file, f_path)
141
141
142 c.f_path = f_path
142 c.f_path = f_path
143
143
144 return render('files/files_annotate.html')
144 return render('files/files_annotate.html')
145
145
146 def archivefile(self, repo_name, fname):
146 def archivefile(self, repo_name, fname):
147 info = fname.split('.')
147 info = fname.split('.')
148 revision, fileformat = info[0], '.' + '.'.join(info[1:])
148 revision, fileformat = info[0], '.' + '.'.join(info[1:])
149 archive_specs = {
149 archive_specs = {
150 '.tar.bz2': ('application/x-tar', 'tbz2'),
150 '.tar.bz2': ('application/x-tar', 'tbz2'),
151 '.tar.gz': ('application/x-tar', 'tgz'),
151 '.tar.gz': ('application/x-tar', 'tgz'),
152 '.zip': ('application/zip', 'zip'),
152 '.zip': ('application/zip', 'zip'),
153 }
153 }
154 if not archive_specs.has_key(fileformat):
154 if not archive_specs.has_key(fileformat):
155 return _('Unknown archive type %s') % fileformat
155 return _('Unknown archive type %s') % fileformat
156
156
157 repo = ScmModel().get_repo(repo_name)
157 repo = ScmModel().get_repo(repo_name)
158
158
159 try:
159 try:
160 repo.get_changeset(revision)
160 repo.get_changeset(revision)
161 except ChangesetDoesNotExistError:
161 except ChangesetDoesNotExistError:
162 return _('Unknown revision %s') % revision
162 return _('Unknown revision %s') % revision
163
163
164 archive = tempfile.TemporaryFile()
164 archive = tempfile.TemporaryFile()
165 localrepo = repo.repo
165 localrepo = repo.repo
166 fname = '%s-%s%s' % (repo_name, revision, fileformat)
166 fname = '%s-%s%s' % (repo_name, revision, fileformat)
167 archival.archive(localrepo, archive, revision, archive_specs[fileformat][1],
167 archival.archive(localrepo, archive, revision, archive_specs[fileformat][1],
168 prefix='%s-%s' % (repo_name, revision))
168 prefix='%s-%s' % (repo_name, revision))
169 response.content_type = archive_specs[fileformat][0]
169 response.content_type = archive_specs[fileformat][0]
170 response.content_disposition = 'attachment; filename=%s' % fname
170 response.content_disposition = 'attachment; filename=%s' % fname
171 archive.seek(0)
171 archive.seek(0)
172
172
173 def read_in_chunks(file_object, chunk_size=1024 * 40):
173 def read_in_chunks(file_object, chunk_size=1024 * 40):
174 """Lazy function (generator) to read a file piece by piece.
174 """Lazy function (generator) to read a file piece by piece.
175 Default chunk size: 40k."""
175 Default chunk size: 40k."""
176 while True:
176 while True:
177 data = file_object.read(chunk_size)
177 data = file_object.read(chunk_size)
178 if not data:
178 if not data:
179 break
179 break
180 yield data
180 yield data
181
181
182 return read_in_chunks(archive)
182 return read_in_chunks(archive)
183
183
184 def diff(self, repo_name, f_path):
184 def diff(self, repo_name, f_path):
185 hg_model = ScmModel()
185 hg_model = ScmModel()
186 diff1 = request.GET.get('diff1')
186 diff1 = request.GET.get('diff1')
187 diff2 = request.GET.get('diff2')
187 diff2 = request.GET.get('diff2')
188 c.action = request.GET.get('diff')
188 c.action = request.GET.get('diff')
189 c.no_changes = diff1 == diff2
189 c.no_changes = diff1 == diff2
190 c.f_path = f_path
190 c.f_path = f_path
191 c.repo = hg_model.get_repo(c.repo_name)
191 c.repo = hg_model.get_repo(c.repo_name)
192
192
193 try:
193 try:
194 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
194 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
195 c.changeset_1 = c.repo.get_changeset(diff1)
195 c.changeset_1 = c.repo.get_changeset(diff1)
196 node1 = c.changeset_1.get_node(f_path)
196 node1 = c.changeset_1.get_node(f_path)
197 else:
197 else:
198 c.changeset_1 = EmptyChangeset()
198 c.changeset_1 = EmptyChangeset()
199 node1 = FileNode('.', '', changeset=c.changeset_1)
199 node1 = FileNode('.', '', changeset=c.changeset_1)
200
200
201 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
201 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
202 c.changeset_2 = c.repo.get_changeset(diff2)
202 c.changeset_2 = c.repo.get_changeset(diff2)
203 node2 = c.changeset_2.get_node(f_path)
203 node2 = c.changeset_2.get_node(f_path)
204 else:
204 else:
205 c.changeset_2 = EmptyChangeset()
205 c.changeset_2 = EmptyChangeset()
206 node2 = FileNode('.', '', changeset=c.changeset_2)
206 node2 = FileNode('.', '', changeset=c.changeset_2)
207 except RepositoryError:
207 except RepositoryError:
208 return redirect(url('files_home',
208 return redirect(url('files_home',
209 repo_name=c.repo_name, f_path=f_path))
209 repo_name=c.repo_name, f_path=f_path))
210
210
211 f_udiff = differ.get_udiff(node1, node2)
211 f_udiff = differ.get_udiff(node1, node2)
212 diff = differ.DiffProcessor(f_udiff)
212 diff = differ.DiffProcessor(f_udiff)
213
213
214 if c.action == 'download':
214 if c.action == 'download':
215 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
215 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
216 response.content_type = 'text/plain'
216 response.content_type = 'text/plain'
217 response.content_disposition = 'attachment; filename=%s' \
217 response.content_disposition = 'attachment; filename=%s' \
218 % diff_name
218 % diff_name
219 return diff.raw_diff()
219 return diff.raw_diff()
220
220
221 elif c.action == 'raw':
221 elif c.action == 'raw':
222 response.content_type = 'text/plain'
222 response.content_type = 'text/plain'
223 return diff.raw_diff()
223 return diff.raw_diff()
224
224
225 elif c.action == 'diff':
225 elif c.action == 'diff':
226 if node1.size > self.cut_off_limit or node2.size > self.cut_off_limit:
226 if node1.size > self.cut_off_limit or node2.size > self.cut_off_limit:
227 c.cur_diff = _('Diff is to big to display')
227 c.cur_diff = _('Diff is to big to display')
228 else:
228 else:
229 c.cur_diff = diff.as_html()
229 c.cur_diff = diff.as_html()
230 else:
230 else:
231 #default option
231 #default option
232 if node1.size > self.cut_off_limit or node2.size > self.cut_off_limit:
232 if node1.size > self.cut_off_limit or node2.size > self.cut_off_limit:
233 c.cur_diff = _('Diff is to big to display')
233 c.cur_diff = _('Diff is to big to display')
234 else:
234 else:
235 c.cur_diff = diff.as_html()
235 c.cur_diff = diff.as_html()
236
236
237 if not c.cur_diff: c.no_changes = True
237 if not c.cur_diff: c.no_changes = True
238 return render('files/file_diff.html')
238 return render('files/file_diff.html')
239
239
240 def _get_history(self, repo, node, f_path):
240 def _get_history(self, repo, node, f_path):
241 from vcs.nodes import NodeKind
241 from vcs.nodes import NodeKind
242 if not node.kind is NodeKind.FILE:
242 if not node.kind is NodeKind.FILE:
243 return []
243 return []
244 changesets = node.history
244 changesets = node.history
245 hist_l = []
245 hist_l = []
246
246
247 changesets_group = ([], _("Changesets"))
247 changesets_group = ([], _("Changesets"))
248 branches_group = ([], _("Branches"))
248 branches_group = ([], _("Branches"))
249 tags_group = ([], _("Tags"))
249 tags_group = ([], _("Tags"))
250
250
251 for chs in changesets:
251 for chs in changesets:
252 n_desc = 'r%s:%s' % (chs.revision, chs.short_id)
252 n_desc = 'r%s:%s' % (chs.revision, chs.short_id)
253 changesets_group[0].append((chs.raw_id, n_desc,))
253 changesets_group[0].append((chs.raw_id, n_desc,))
254
254
255 hist_l.append(changesets_group)
255 hist_l.append(changesets_group)
256
256
257 for name, chs in c.repository_branches.items():
257 for name, chs in c.repository_branches.items():
258 #chs = chs.split(':')[-1]
258 #chs = chs.split(':')[-1]
259 branches_group[0].append((chs, name),)
259 branches_group[0].append((chs, name),)
260 hist_l.append(branches_group)
260 hist_l.append(branches_group)
261
261
262 for name, chs in c.repository_tags.items():
262 for name, chs in c.repository_tags.items():
263 #chs = chs.split(':')[-1]
263 #chs = chs.split(':')[-1]
264 tags_group[0].append((chs, name),)
264 tags_group[0].append((chs, name),)
265 hist_l.append(tags_group)
265 hist_l.append(tags_group)
266
266
267 return hist_l
267 return hist_l
268
268
269 # [
270 # ([("u1", "User1"), ("u2", "User2")], "Users"),
271 # ([("g1", "Group1"), ("g2", "Group2")], "Groups")
272 # ]
273
@@ -1,546 +1,547 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 import random
6 import random
7 import hashlib
7 import hashlib
8 from pygments.formatters import HtmlFormatter
8 from pygments.formatters import HtmlFormatter
9 from pygments import highlight as code_highlight
9 from pygments import highlight as code_highlight
10 from pylons import url, app_globals as g
10 from pylons import url, app_globals as g
11 from pylons.i18n.translation import _, ungettext
11 from pylons.i18n.translation import _, ungettext
12 from vcs.utils.annotate import annotate_highlight
12 from vcs.utils.annotate import annotate_highlight
13 from webhelpers.html import literal, HTML, escape
13 from webhelpers.html import literal, HTML, escape
14 from webhelpers.html.tools import *
14 from webhelpers.html.tools import *
15 from webhelpers.html.builder import make_tag
15 from webhelpers.html.builder import make_tag
16 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
16 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
17 end_form, file, form, hidden, image, javascript_link, link_to, link_to_if, \
17 end_form, file, form, hidden, image, javascript_link, link_to, link_to_if, \
18 link_to_unless, ol, required_legend, select, stylesheet_link, submit, text, \
18 link_to_unless, ol, required_legend, select, stylesheet_link, submit, text, \
19 password, textarea, title, ul, xml_declaration, radio
19 password, textarea, title, ul, xml_declaration, radio
20 from webhelpers.html.tools import auto_link, button_to, highlight, js_obfuscate, \
20 from webhelpers.html.tools import auto_link, button_to, highlight, js_obfuscate, \
21 mail_to, strip_links, strip_tags, tag_re
21 mail_to, strip_links, strip_tags, tag_re
22 from webhelpers.number import format_byte_size, format_bit_size
22 from webhelpers.number import format_byte_size, format_bit_size
23 from webhelpers.pylonslib import Flash as _Flash
23 from webhelpers.pylonslib import Flash as _Flash
24 from webhelpers.pylonslib.secure_form import secure_form
24 from webhelpers.pylonslib.secure_form import secure_form
25 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
25 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
26 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
26 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
27 replace_whitespace, urlify, truncate, wrap_paragraphs
27 replace_whitespace, urlify, truncate, wrap_paragraphs
28 from webhelpers.date import time_ago_in_words
28 from webhelpers.date import time_ago_in_words
29
29
30 from webhelpers.html.tags import _set_input_attrs, _set_id_attr, \
30 from webhelpers.html.tags import _set_input_attrs, _set_id_attr, \
31 convert_boolean_attrs, NotGiven
31 convert_boolean_attrs, NotGiven
32
32
33 def _reset(name, value=None, id=NotGiven, type="reset", **attrs):
33 def _reset(name, value=None, id=NotGiven, type="reset", **attrs):
34 """Reset button
35 """
34 _set_input_attrs(attrs, type, name, value)
36 _set_input_attrs(attrs, type, name, value)
35 _set_id_attr(attrs, id, name)
37 _set_id_attr(attrs, id, name)
36 convert_boolean_attrs(attrs, ["disabled"])
38 convert_boolean_attrs(attrs, ["disabled"])
37 return HTML.input(**attrs)
39 return HTML.input(**attrs)
38
40
39 reset = _reset
41 reset = _reset
40
42
41
43
42 def get_token():
44 def get_token():
43 """Return the current authentication token, creating one if one doesn't
45 """Return the current authentication token, creating one if one doesn't
44 already exist.
46 already exist.
45 """
47 """
46 token_key = "_authentication_token"
48 token_key = "_authentication_token"
47 from pylons import session
49 from pylons import session
48 if not token_key in session:
50 if not token_key in session:
49 try:
51 try:
50 token = hashlib.sha1(str(random.getrandbits(128))).hexdigest()
52 token = hashlib.sha1(str(random.getrandbits(128))).hexdigest()
51 except AttributeError: # Python < 2.4
53 except AttributeError: # Python < 2.4
52 token = hashlib.sha1(str(random.randrange(2 ** 128))).hexdigest()
54 token = hashlib.sha1(str(random.randrange(2 ** 128))).hexdigest()
53 session[token_key] = token
55 session[token_key] = token
54 if hasattr(session, 'save'):
56 if hasattr(session, 'save'):
55 session.save()
57 session.save()
56 return session[token_key]
58 return session[token_key]
57
59
58
59 #Custom helpers here :)
60 class _Link(object):
61 '''
62 Make a url based on label and url with help of url_for
63 :param label:name of link if not defined url is used
64 :param url: the url for link
65 '''
66
67 def __call__(self, label='', *url_, **urlargs):
68 if label is None or '':
69 label = url
70 link_fn = link_to(label, url(*url_, **urlargs))
71 return link_fn
72
73 link = _Link()
74
75 class _GetError(object):
60 class _GetError(object):
61 """Get error from form_errors, and represent it as span wrapped error
62 message
63
64 :param field_name: field to fetch errors for
65 :param form_errors: form errors dict
66 """
76
67
77 def __call__(self, field_name, form_errors):
68 def __call__(self, field_name, form_errors):
78 tmpl = """<span class="error_msg">%s</span>"""
69 tmpl = """<span class="error_msg">%s</span>"""
79 if form_errors and form_errors.has_key(field_name):
70 if form_errors and form_errors.has_key(field_name):
80 return literal(tmpl % form_errors.get(field_name))
71 return literal(tmpl % form_errors.get(field_name))
81
72
82 get_error = _GetError()
73 get_error = _GetError()
83
74
84 def recursive_replace(str, replace=' '):
75 def recursive_replace(str, replace=' '):
85 """
76 """Recursive replace of given sign to just one instance
86 Recursive replace of given sign to just one instance
77
87 :param str: given string
78 :param str: given string
88 :param replace:char to find and replace multiple instances
79 :param replace: char to find and replace multiple instances
89
80
90 Examples::
81 Examples::
91 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
82 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
92 'Mighty-Mighty-Bo-sstones'
83 'Mighty-Mighty-Bo-sstones'
93 """
84 """
94
85
95 if str.find(replace * 2) == -1:
86 if str.find(replace * 2) == -1:
96 return str
87 return str
97 else:
88 else:
98 str = str.replace(replace * 2, replace)
89 str = str.replace(replace * 2, replace)
99 return recursive_replace(str, replace)
90 return recursive_replace(str, replace)
100
91
101 class _ToolTip(object):
92 class _ToolTip(object):
102
93
103 def __call__(self, tooltip_title, trim_at=50):
94 def __call__(self, tooltip_title, trim_at=50):
104 """
95 """
105 Special function just to wrap our text into nice formatted autowrapped
96 Special function just to wrap our text into nice formatted autowrapped
106 text
97 text
107 :param tooltip_title:
98 :param tooltip_title:
108 """
99 """
109
100
110 return wrap_paragraphs(escape(tooltip_title), trim_at)\
101 return wrap_paragraphs(escape(tooltip_title), trim_at)\
111 .replace('\n', '<br/>')
102 .replace('\n', '<br/>')
112
103
113 def activate(self):
104 def activate(self):
114 """
105 """
115 Adds tooltip mechanism to the given Html all tooltips have to have
106 Adds tooltip mechanism to the given Html all tooltips have to have
116 set class tooltip and set attribute tooltip_title.
107 set class tooltip and set attribute tooltip_title.
117 Then a tooltip will be generated based on that
108 Then a tooltip will be generated based on that
118 All with yui js tooltip
109 All with yui js tooltip
119 """
110 """
120
111
121 js = '''
112 js = '''
122 YAHOO.util.Event.onDOMReady(function(){
113 YAHOO.util.Event.onDOMReady(function(){
123 function toolTipsId(){
114 function toolTipsId(){
124 var ids = [];
115 var ids = [];
125 var tts = YAHOO.util.Dom.getElementsByClassName('tooltip');
116 var tts = YAHOO.util.Dom.getElementsByClassName('tooltip');
126
117
127 for (var i = 0; i < tts.length; i++) {
118 for (var i = 0; i < tts.length; i++) {
128 //if element doesn't not have and id autgenerate one for tooltip
119 //if element doesn't not have and id autgenerate one for tooltip
129
120
130 if (!tts[i].id){
121 if (!tts[i].id){
131 tts[i].id='tt'+i*100;
122 tts[i].id='tt'+i*100;
132 }
123 }
133 ids.push(tts[i].id);
124 ids.push(tts[i].id);
134 }
125 }
135 return ids
126 return ids
136 };
127 };
137 var myToolTips = new YAHOO.widget.Tooltip("tooltip", {
128 var myToolTips = new YAHOO.widget.Tooltip("tooltip", {
138 context: toolTipsId(),
129 context: toolTipsId(),
139 monitorresize:false,
130 monitorresize:false,
140 xyoffset :[0,0],
131 xyoffset :[0,0],
141 autodismissdelay:300000,
132 autodismissdelay:300000,
142 hidedelay:5,
133 hidedelay:5,
143 showdelay:20,
134 showdelay:20,
144 });
135 });
145
136
146 //Mouse Over event disabled for new repositories since they don't
137 //Mouse Over event disabled for new repositories since they don't
147 //have last commit message
138 //have last commit message
148 myToolTips.contextMouseOverEvent.subscribe(
139 myToolTips.contextMouseOverEvent.subscribe(
149 function(type, args) {
140 function(type, args) {
150 var context = args[0];
141 var context = args[0];
151 var txt = context.getAttribute('tooltip_title');
142 var txt = context.getAttribute('tooltip_title');
152 if(txt){
143 if(txt){
153 return true;
144 return true;
154 }
145 }
155 else{
146 else{
156 return false;
147 return false;
157 }
148 }
158 });
149 });
159
150
160
151
161 // Set the text for the tooltip just before we display it. Lazy method
152 // Set the text for the tooltip just before we display it. Lazy method
162 myToolTips.contextTriggerEvent.subscribe(
153 myToolTips.contextTriggerEvent.subscribe(
163 function(type, args) {
154 function(type, args) {
164
155
165
156
166 var context = args[0];
157 var context = args[0];
167
158
168 var txt = context.getAttribute('tooltip_title');
159 var txt = context.getAttribute('tooltip_title');
169 this.cfg.setProperty("text", txt);
160 this.cfg.setProperty("text", txt);
170
161
171
162
172 // positioning of tooltip
163 // positioning of tooltip
173 var tt_w = this.element.clientWidth;
164 var tt_w = this.element.clientWidth;
174 var tt_h = this.element.clientHeight;
165 var tt_h = this.element.clientHeight;
175
166
176 var context_w = context.offsetWidth;
167 var context_w = context.offsetWidth;
177 var context_h = context.offsetHeight;
168 var context_h = context.offsetHeight;
178
169
179 var pos_x = YAHOO.util.Dom.getX(context);
170 var pos_x = YAHOO.util.Dom.getX(context);
180 var pos_y = YAHOO.util.Dom.getY(context);
171 var pos_y = YAHOO.util.Dom.getY(context);
181
172
182 var display_strategy = 'top';
173 var display_strategy = 'top';
183 var xy_pos = [0,0];
174 var xy_pos = [0,0];
184 switch (display_strategy){
175 switch (display_strategy){
185
176
186 case 'top':
177 case 'top':
187 var cur_x = (pos_x+context_w/2)-(tt_w/2);
178 var cur_x = (pos_x+context_w/2)-(tt_w/2);
188 var cur_y = pos_y-tt_h-4;
179 var cur_y = pos_y-tt_h-4;
189 xy_pos = [cur_x,cur_y];
180 xy_pos = [cur_x,cur_y];
190 break;
181 break;
191 case 'bottom':
182 case 'bottom':
192 var cur_x = (pos_x+context_w/2)-(tt_w/2);
183 var cur_x = (pos_x+context_w/2)-(tt_w/2);
193 var cur_y = pos_y+context_h+4;
184 var cur_y = pos_y+context_h+4;
194 xy_pos = [cur_x,cur_y];
185 xy_pos = [cur_x,cur_y];
195 break;
186 break;
196 case 'left':
187 case 'left':
197 var cur_x = (pos_x-tt_w-4);
188 var cur_x = (pos_x-tt_w-4);
198 var cur_y = pos_y-((tt_h/2)-context_h/2);
189 var cur_y = pos_y-((tt_h/2)-context_h/2);
199 xy_pos = [cur_x,cur_y];
190 xy_pos = [cur_x,cur_y];
200 break;
191 break;
201 case 'right':
192 case 'right':
202 var cur_x = (pos_x+context_w+4);
193 var cur_x = (pos_x+context_w+4);
203 var cur_y = pos_y-((tt_h/2)-context_h/2);
194 var cur_y = pos_y-((tt_h/2)-context_h/2);
204 xy_pos = [cur_x,cur_y];
195 xy_pos = [cur_x,cur_y];
205 break;
196 break;
206 default:
197 default:
207 var cur_x = (pos_x+context_w/2)-(tt_w/2);
198 var cur_x = (pos_x+context_w/2)-(tt_w/2);
208 var cur_y = pos_y-tt_h-4;
199 var cur_y = pos_y-tt_h-4;
209 xy_pos = [cur_x,cur_y];
200 xy_pos = [cur_x,cur_y];
210 break;
201 break;
211
202
212 }
203 }
213
204
214 this.cfg.setProperty("xy",xy_pos);
205 this.cfg.setProperty("xy",xy_pos);
215
206
216 });
207 });
217
208
218 //Mouse out
209 //Mouse out
219 myToolTips.contextMouseOutEvent.subscribe(
210 myToolTips.contextMouseOutEvent.subscribe(
220 function(type, args) {
211 function(type, args) {
221 var context = args[0];
212 var context = args[0];
222
213
223 });
214 });
224 });
215 });
225 '''
216 '''
226 return literal(js)
217 return literal(js)
227
218
228 tooltip = _ToolTip()
219 tooltip = _ToolTip()
229
220
230 class _FilesBreadCrumbs(object):
221 class _FilesBreadCrumbs(object):
231
222
232 def __call__(self, repo_name, rev, paths):
223 def __call__(self, repo_name, rev, paths):
233 url_l = [link_to(repo_name, url('files_home',
224 url_l = [link_to(repo_name, url('files_home',
234 repo_name=repo_name,
225 repo_name=repo_name,
235 revision=rev, f_path=''))]
226 revision=rev, f_path=''))]
236 paths_l = paths.split('/')
227 paths_l = paths.split('/')
237
228
238 for cnt, p in enumerate(paths_l):
229 for cnt, p in enumerate(paths_l):
239 if p != '':
230 if p != '':
240 url_l.append(link_to(p, url('files_home',
231 url_l.append(link_to(p, url('files_home',
241 repo_name=repo_name,
232 repo_name=repo_name,
242 revision=rev,
233 revision=rev,
243 f_path='/'.join(paths_l[:cnt + 1]))))
234 f_path='/'.join(paths_l[:cnt + 1]))))
244
235
245 return literal('/'.join(url_l))
236 return literal('/'.join(url_l))
246
237
247 files_breadcrumbs = _FilesBreadCrumbs()
238 files_breadcrumbs = _FilesBreadCrumbs()
239
248 class CodeHtmlFormatter(HtmlFormatter):
240 class CodeHtmlFormatter(HtmlFormatter):
249
241
250 def wrap(self, source, outfile):
242 def wrap(self, source, outfile):
251 return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
243 return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
252
244
253 def _wrap_code(self, source):
245 def _wrap_code(self, source):
254 for cnt, it in enumerate(source):
246 for cnt, it in enumerate(source):
255 i, t = it
247 i, t = it
256 t = '<div id="#S-%s">%s</div>' % (cnt + 1, t)
248 t = '<div id="#S-%s">%s</div>' % (cnt + 1, t)
257 yield i, t
249 yield i, t
258 def pygmentize(filenode, **kwargs):
250 def pygmentize(filenode, **kwargs):
259 """
251 """pygmentize function using pygments
260 pygmentize function using pygments
252
261 :param filenode:
253 :param filenode:
262 """
254 """
255
263 return literal(code_highlight(filenode.content,
256 return literal(code_highlight(filenode.content,
264 filenode.lexer, CodeHtmlFormatter(**kwargs)))
257 filenode.lexer, CodeHtmlFormatter(**kwargs)))
265
258
266 def pygmentize_annotation(filenode, **kwargs):
259 def pygmentize_annotation(filenode, **kwargs):
267 """
260 """pygmentize function for annotation
268 pygmentize function for annotation
261
269 :param filenode:
262 :param filenode:
270 """
263 """
271
264
272 color_dict = {}
265 color_dict = {}
273 def gen_color():
266 def gen_color():
274 """generator for getting 10k of evenly distibuted colors using hsv color
267 """generator for getting 10k of evenly distibuted colors using hsv color
275 and golden ratio.
268 and golden ratio.
276 """
269 """
277 import colorsys
270 import colorsys
278 n = 10000
271 n = 10000
279 golden_ratio = 0.618033988749895
272 golden_ratio = 0.618033988749895
280 h = 0.22717784590367374
273 h = 0.22717784590367374
281 #generate 10k nice web friendly colors in the same order
274 #generate 10k nice web friendly colors in the same order
282 for c in xrange(n):
275 for c in xrange(n):
283 h += golden_ratio
276 h += golden_ratio
284 h %= 1
277 h %= 1
285 HSV_tuple = [h, 0.95, 0.95]
278 HSV_tuple = [h, 0.95, 0.95]
286 RGB_tuple = colorsys.hsv_to_rgb(*HSV_tuple)
279 RGB_tuple = colorsys.hsv_to_rgb(*HSV_tuple)
287 yield map(lambda x:str(int(x * 256)), RGB_tuple)
280 yield map(lambda x:str(int(x * 256)), RGB_tuple)
288
281
289 cgenerator = gen_color()
282 cgenerator = gen_color()
290
283
291 def get_color_string(cs):
284 def get_color_string(cs):
292 if color_dict.has_key(cs):
285 if color_dict.has_key(cs):
293 col = color_dict[cs]
286 col = color_dict[cs]
294 else:
287 else:
295 col = color_dict[cs] = cgenerator.next()
288 col = color_dict[cs] = cgenerator.next()
296 return "color: rgb(%s)! important;" % (', '.join(col))
289 return "color: rgb(%s)! important;" % (', '.join(col))
297
290
298 def url_func(changeset):
291 def url_func(changeset):
299 tooltip_html = "<div style='font-size:0.8em'><b>Author:</b>" + \
292 tooltip_html = "<div style='font-size:0.8em'><b>Author:</b>" + \
300 " %s<br/><b>Date:</b> %s</b><br/><b>Message:</b> %s<br/></div>"
293 " %s<br/><b>Date:</b> %s</b><br/><b>Message:</b> %s<br/></div>"
301
294
302 tooltip_html = tooltip_html % (changeset.author,
295 tooltip_html = tooltip_html % (changeset.author,
303 changeset.date,
296 changeset.date,
304 tooltip(changeset.message))
297 tooltip(changeset.message))
305 lnk_format = '%5s:%s' % ('r%s' % changeset.revision,
298 lnk_format = '%5s:%s' % ('r%s' % changeset.revision,
306 short_id(changeset.raw_id))
299 short_id(changeset.raw_id))
307 uri = link_to(
300 uri = link_to(
308 lnk_format,
301 lnk_format,
309 url('changeset_home', repo_name=changeset.repository.name,
302 url('changeset_home', repo_name=changeset.repository.name,
310 revision=changeset.raw_id),
303 revision=changeset.raw_id),
311 style=get_color_string(changeset.raw_id),
304 style=get_color_string(changeset.raw_id),
312 class_='tooltip',
305 class_='tooltip',
313 tooltip_title=tooltip_html
306 tooltip_title=tooltip_html
314 )
307 )
315
308
316 uri += '\n'
309 uri += '\n'
317 return uri
310 return uri
318 return literal(annotate_highlight(filenode, url_func, **kwargs))
311 return literal(annotate_highlight(filenode, url_func, **kwargs))
319
312
320 def repo_name_slug(value):
313 def repo_name_slug(value):
321 """Return slug of name of repository
314 """Return slug of name of repository
322 This function is called on each creation/modification
315 This function is called on each creation/modification
323 of repository to prevent bad names in repo
316 of repository to prevent bad names in repo
324 """
317 """
318
325 slug = remove_formatting(value)
319 slug = remove_formatting(value)
326 slug = strip_tags(slug)
320 slug = strip_tags(slug)
327
321
328 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
322 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
329 slug = slug.replace(c, '-')
323 slug = slug.replace(c, '-')
330 slug = recursive_replace(slug, '-')
324 slug = recursive_replace(slug, '-')
331 slug = collapse(slug, '-')
325 slug = collapse(slug, '-')
332 return slug
326 return slug
333
327
334 def get_changeset_safe(repo, rev):
328 def get_changeset_safe(repo, rev):
335 from vcs.backends.base import BaseRepository
329 from vcs.backends.base import BaseRepository
336 from vcs.exceptions import RepositoryError
330 from vcs.exceptions import RepositoryError
337 if not isinstance(repo, BaseRepository):
331 if not isinstance(repo, BaseRepository):
338 raise Exception('You must pass an Repository '
332 raise Exception('You must pass an Repository '
339 'object as first argument got %s', type(repo))
333 'object as first argument got %s', type(repo))
340
334
341 try:
335 try:
342 cs = repo.get_changeset(rev)
336 cs = repo.get_changeset(rev)
343 except RepositoryError:
337 except RepositoryError:
344 from rhodecode.lib.utils import EmptyChangeset
338 from rhodecode.lib.utils import EmptyChangeset
345 cs = EmptyChangeset()
339 cs = EmptyChangeset()
346 return cs
340 return cs
347
341
348
342
349 flash = _Flash()
343 flash = _Flash()
350
344
351
345
352 #==============================================================================
346 #==============================================================================
353 # MERCURIAL FILTERS available via h.
347 # MERCURIAL FILTERS available via h.
354 #==============================================================================
348 #==============================================================================
355 from mercurial import util
349 from mercurial import util
356 from mercurial.templatefilters import person as _person
350 from mercurial.templatefilters import person as _person
357
351
358
359
360 def _age(curdate):
352 def _age(curdate):
361 """turns a datetime into an age string."""
353 """turns a datetime into an age string."""
362
354
363 if not curdate:
355 if not curdate:
364 return ''
356 return ''
365
357
366 from datetime import timedelta, datetime
358 from datetime import timedelta, datetime
367
359
368 agescales = [("year", 3600 * 24 * 365),
360 agescales = [("year", 3600 * 24 * 365),
369 ("month", 3600 * 24 * 30),
361 ("month", 3600 * 24 * 30),
370 ("day", 3600 * 24),
362 ("day", 3600 * 24),
371 ("hour", 3600),
363 ("hour", 3600),
372 ("minute", 60),
364 ("minute", 60),
373 ("second", 1), ]
365 ("second", 1), ]
374
366
375 age = datetime.now() - curdate
367 age = datetime.now() - curdate
376 age_seconds = (age.days * agescales[2][1]) + age.seconds
368 age_seconds = (age.days * agescales[2][1]) + age.seconds
377 pos = 1
369 pos = 1
378 for scale in agescales:
370 for scale in agescales:
379 if scale[1] <= age_seconds:
371 if scale[1] <= age_seconds:
380 if pos == 6:pos = 5
372 if pos == 6:pos = 5
381 return time_ago_in_words(curdate, agescales[pos][0]) + ' ' + _('ago')
373 return time_ago_in_words(curdate, agescales[pos][0]) + ' ' + _('ago')
382 pos += 1
374 pos += 1
383
375
384 return _('just now')
376 return _('just now')
385
377
386 age = lambda x:_age(x)
378 age = lambda x:_age(x)
387 capitalize = lambda x: x.capitalize()
379 capitalize = lambda x: x.capitalize()
388 email = util.email
380 email = util.email
389 email_or_none = lambda x: util.email(x) if util.email(x) != x else None
381 email_or_none = lambda x: util.email(x) if util.email(x) != x else None
390 person = lambda x: _person(x)
382 person = lambda x: _person(x)
391 short_id = lambda x: x[:12]
383 short_id = lambda x: x[:12]
392
384
393
385
394 def bool2icon(value):
386 def bool2icon(value):
395 """
387 """Returns True/False values represented as small html image of true/false
396 Returns True/False values represented as small html image of true/false
397 icons
388 icons
389
398 :param value: bool value
390 :param value: bool value
399 """
391 """
400
392
401 if value is True:
393 if value is True:
402 return HTML.tag('img', src="/images/icons/accept.png", alt=_('True'))
394 return HTML.tag('img', src="/images/icons/accept.png", alt=_('True'))
403
395
404 if value is False:
396 if value is False:
405 return HTML.tag('img', src="/images/icons/cancel.png", alt=_('False'))
397 return HTML.tag('img', src="/images/icons/cancel.png", alt=_('False'))
406
398
407 return value
399 return value
408
400
409
401
410 def action_parser(user_log):
402 def action_parser(user_log):
411 """
403 """This helper will map the specified string action into translated
412 This helper will map the specified string action into translated
413 fancy names with icons and links
404 fancy names with icons and links
414
405
415 @param action:
406 :param user_log: user log instance
416 """
407 """
408
417 action = user_log.action
409 action = user_log.action
418 action_params = ' '
410 action_params = ' '
419
411
420 x = action.split(':')
412 x = action.split(':')
421
413
422 if len(x) > 1:
414 if len(x) > 1:
423 action, action_params = x
415 action, action_params = x
424
416
425 def get_cs_links():
417 def get_cs_links():
426 if action == 'push':
418 if action == 'push':
427 revs_limit = 5
419 revs_limit = 5 #display this amount always
420 revs_top_limit = 50 #show upto this amount of changesets hidden
428 revs = action_params.split(',')
421 revs = action_params.split(',')
429 cs_links = " " + ', '.join ([link(rev,
422 repo_name = user_log.repository.repo_name
423 from rhodecode.model.scm import ScmModel
424
425 message = lambda rev: get_changeset_safe(ScmModel().get(repo_name),
426 rev).message
427
428 cs_links = " " + ', '.join ([link_to(rev,
430 url('changeset_home',
429 url('changeset_home',
431 repo_name=user_log.repository.repo_name,
430 repo_name=repo_name,
432 revision=rev)) for rev in revs[:revs_limit] ])
431 revision=rev), tooltip_title=message(rev),
432 class_='tooltip') for rev in revs[:revs_limit] ])
433 if len(revs) > revs_limit:
433 if len(revs) > revs_limit:
434 uniq_id = revs[0]
434 uniq_id = revs[0]
435 html_tmpl = ('<span> %s '
435 html_tmpl = ('<span> %s '
436 '<a class="show_more" id="_%s" href="#">%s</a> '
436 '<a class="show_more" id="_%s" href="#">%s</a> '
437 '%s</span>')
437 '%s</span>')
438 cs_links += html_tmpl % (_('and'), uniq_id, _('%s more') \
438 cs_links += html_tmpl % (_('and'), uniq_id, _('%s more') \
439 % (len(revs) - revs_limit),
439 % (len(revs) - revs_limit),
440 _('revisions'))
440 _('revisions'))
441
441
442 html_tmpl = '<span id="%s" style="display:none"> %s </span>'
442 html_tmpl = '<span id="%s" style="display:none"> %s </span>'
443 cs_links += html_tmpl % (uniq_id, ', '.join([link(rev,
443 cs_links += html_tmpl % (uniq_id, ', '.join([link_to(rev,
444 url('changeset_home',
444 url('changeset_home',
445 repo_name=user_log.repository.repo_name,
445 repo_name=repo_name, revision=rev),
446 revision=rev)) for rev in revs[revs_limit:] ]))
446 tooltip_title=message(rev), class_='tooltip')
447 for rev in revs[revs_limit:revs_top_limit]]))
447
448
448 return cs_links
449 return cs_links
449 return ''
450 return ''
450
451
451 def get_fork_name():
452 def get_fork_name():
452 if action == 'user_forked_repo':
453 if action == 'user_forked_repo':
453 from rhodecode.model.scm import ScmModel
454 from rhodecode.model.scm import ScmModel
454 repo_name = action_params
455 repo_name = action_params
455 repo = ScmModel().get(repo_name)
456 repo = ScmModel().get(repo_name)
456 if repo is None:
457 if repo is None:
457 return repo_name
458 return repo_name
458 return link_to(action_params, url('summary_home',
459 return link_to(action_params, url('summary_home',
459 repo_name=repo.name,),
460 repo_name=repo.name,),
460 title=repo.dbrepo.description)
461 title=repo.dbrepo.description)
461 return ''
462 return ''
462 map = {'user_deleted_repo':_('User [deleted] repository'),
463 map = {'user_deleted_repo':_('User [deleted] repository'),
463 'user_created_repo':_('User [created] repository'),
464 'user_created_repo':_('User [created] repository'),
464 'user_forked_repo':_('User [forked] repository as: %s') % get_fork_name(),
465 'user_forked_repo':_('User [forked] repository as: %s') % get_fork_name(),
465 'user_updated_repo':_('User [updated] repository'),
466 'user_updated_repo':_('User [updated] repository'),
466 'admin_deleted_repo':_('Admin [delete] repository'),
467 'admin_deleted_repo':_('Admin [delete] repository'),
467 'admin_created_repo':_('Admin [created] repository'),
468 'admin_created_repo':_('Admin [created] repository'),
468 'admin_forked_repo':_('Admin [forked] repository'),
469 'admin_forked_repo':_('Admin [forked] repository'),
469 'admin_updated_repo':_('Admin [updated] repository'),
470 'admin_updated_repo':_('Admin [updated] repository'),
470 'push':_('[Pushed] %s') % get_cs_links(),
471 'push':_('[Pushed] %s') % get_cs_links(),
471 'pull':_('[Pulled]'),
472 'pull':_('[Pulled]'),
472 'started_following_repo':_('User [started following] repository'),
473 'started_following_repo':_('User [started following] repository'),
473 'stopped_following_repo':_('User [stopped following] repository'),
474 'stopped_following_repo':_('User [stopped following] repository'),
474 }
475 }
475
476
476 action_str = map.get(action, action)
477 action_str = map.get(action, action)
477 return literal(action_str.replace('[', '<span class="journal_highlight">')\
478 return literal(action_str.replace('[', '<span class="journal_highlight">')\
478 .replace(']', '</span>'))
479 .replace(']', '</span>'))
479
480
480 def action_parser_icon(user_log):
481 def action_parser_icon(user_log):
481 action = user_log.action
482 action = user_log.action
482 action_params = None
483 action_params = None
483 x = action.split(':')
484 x = action.split(':')
484
485
485 if len(x) > 1:
486 if len(x) > 1:
486 action, action_params = x
487 action, action_params = x
487
488
488 tmpl = """<img src="/images/icons/%s" alt="%s"/>"""
489 tmpl = """<img src="/images/icons/%s" alt="%s"/>"""
489 map = {'user_deleted_repo':'database_delete.png',
490 map = {'user_deleted_repo':'database_delete.png',
490 'user_created_repo':'database_add.png',
491 'user_created_repo':'database_add.png',
491 'user_forked_repo':'arrow_divide.png',
492 'user_forked_repo':'arrow_divide.png',
492 'user_updated_repo':'database_edit.png',
493 'user_updated_repo':'database_edit.png',
493 'admin_deleted_repo':'database_delete.png',
494 'admin_deleted_repo':'database_delete.png',
494 'admin_created_repo':'database_ddd.png',
495 'admin_created_repo':'database_add.png',
495 'admin_forked_repo':'arrow_divide.png',
496 'admin_forked_repo':'arrow_divide.png',
496 'admin_updated_repo':'database_edit.png',
497 'admin_updated_repo':'database_edit.png',
497 'push':'script_add.png',
498 'push':'script_add.png',
498 'pull':'down_16.png',
499 'pull':'down_16.png',
499 'started_following_repo':'heart_add.png',
500 'started_following_repo':'heart_add.png',
500 'stopped_following_repo':'heart_delete.png',
501 'stopped_following_repo':'heart_delete.png',
501 }
502 }
502 return literal(tmpl % (map.get(action, action), action))
503 return literal(tmpl % (map.get(action, action), action))
503
504
504
505
505 #==============================================================================
506 #==============================================================================
506 # PERMS
507 # PERMS
507 #==============================================================================
508 #==============================================================================
508 from rhodecode.lib.auth import HasPermissionAny, HasPermissionAll, \
509 from rhodecode.lib.auth import HasPermissionAny, HasPermissionAll, \
509 HasRepoPermissionAny, HasRepoPermissionAll
510 HasRepoPermissionAny, HasRepoPermissionAll
510
511
511 #==============================================================================
512 #==============================================================================
512 # GRAVATAR URL
513 # GRAVATAR URL
513 #==============================================================================
514 #==============================================================================
514 import hashlib
515 import hashlib
515 import urllib
516 import urllib
516 from pylons import request
517 from pylons import request
517
518
518 def gravatar_url(email_address, size=30):
519 def gravatar_url(email_address, size=30):
519 ssl_enabled = 'https' == request.environ.get('HTTP_X_URL_SCHEME')
520 ssl_enabled = 'https' == request.environ.get('HTTP_X_URL_SCHEME')
520 default = 'identicon'
521 default = 'identicon'
521 baseurl_nossl = "http://www.gravatar.com/avatar/"
522 baseurl_nossl = "http://www.gravatar.com/avatar/"
522 baseurl_ssl = "https://secure.gravatar.com/avatar/"
523 baseurl_ssl = "https://secure.gravatar.com/avatar/"
523 baseurl = baseurl_ssl if ssl_enabled else baseurl_nossl
524 baseurl = baseurl_ssl if ssl_enabled else baseurl_nossl
524
525
525
526
526 # construct the url
527 # construct the url
527 gravatar_url = baseurl + hashlib.md5(email_address.lower()).hexdigest() + "?"
528 gravatar_url = baseurl + hashlib.md5(email_address.lower()).hexdigest() + "?"
528 gravatar_url += urllib.urlencode({'d':default, 's':str(size)})
529 gravatar_url += urllib.urlencode({'d':default, 's':str(size)})
529
530
530 return gravatar_url
531 return gravatar_url
531
532
532 def safe_unicode(str):
533 def safe_unicode(str):
533 """safe unicode function. In case of UnicodeDecode error we try to return
534 """safe unicode function. In case of UnicodeDecode error we try to return
534 unicode with errors replace, if this failes we return unicode with
535 unicode with errors replace, if this failes we return unicode with
535 string_escape decoding """
536 string_escape decoding """
536
537
537 try:
538 try:
538 u_str = unicode(str)
539 u_str = unicode(str)
539 except UnicodeDecodeError:
540 except UnicodeDecodeError:
540 try:
541 try:
541 u_str = unicode(str, 'utf-8', 'replace')
542 u_str = unicode(str, 'utf-8', 'replace')
542 except UnicodeDecodeError:
543 except UnicodeDecodeError:
543 #incase we have a decode error just represent as byte string
544 #incase we have a decode error just represent as byte string
544 u_str = unicode(str(str).encode('string_escape'))
545 u_str = unicode(str(str).encode('string_escape'))
545
546
546 return u_str
547 return u_str
General Comments 0
You need to be logged in to leave comments. Login now