##// END OF EJS Templates
fixed issue with show at revision button. Some JS were not properly loaded due to ajaxified files view....
marcink -
r2931:4c7cc3a4 beta
parent child Browse files
Show More
@@ -1,528 +1,541 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) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 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 modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
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, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 from __future__ import with_statement
25 from __future__ import with_statement
26 import os
26 import os
27 import logging
27 import logging
28 import traceback
28 import traceback
29 import tempfile
29 import tempfile
30
30
31 from pylons import request, response, tmpl_context as c, url
31 from pylons import request, response, tmpl_context as c, url
32 from pylons.i18n.translation import _
32 from pylons.i18n.translation import _
33 from pylons.controllers.util import redirect
33 from pylons.controllers.util import redirect
34 from pylons.decorators import jsonify
34 from pylons.decorators import jsonify
35
35
36 from rhodecode.lib import diffs
36 from rhodecode.lib import diffs
37 from rhodecode.lib import helpers as h
37 from rhodecode.lib import helpers as h
38
38
39 from rhodecode.lib.compat import OrderedDict
39 from rhodecode.lib.compat import OrderedDict
40 from rhodecode.lib.utils2 import convert_line_endings, detect_mode, safe_str
40 from rhodecode.lib.utils2 import convert_line_endings, detect_mode, safe_str,\
41 str2bool
41 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
42 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
42 from rhodecode.lib.base import BaseRepoController, render
43 from rhodecode.lib.base import BaseRepoController, render
43 from rhodecode.lib.vcs.backends.base import EmptyChangeset
44 from rhodecode.lib.vcs.backends.base import EmptyChangeset
44 from rhodecode.lib.vcs.conf import settings
45 from rhodecode.lib.vcs.conf import settings
45 from rhodecode.lib.vcs.exceptions import RepositoryError, \
46 from rhodecode.lib.vcs.exceptions import RepositoryError, \
46 ChangesetDoesNotExistError, EmptyRepositoryError, \
47 ChangesetDoesNotExistError, EmptyRepositoryError, \
47 ImproperArchiveTypeError, VCSError, NodeAlreadyExistsError
48 ImproperArchiveTypeError, VCSError, NodeAlreadyExistsError
48 from rhodecode.lib.vcs.nodes import FileNode
49 from rhodecode.lib.vcs.nodes import FileNode
49
50
50 from rhodecode.model.repo import RepoModel
51 from rhodecode.model.repo import RepoModel
51 from rhodecode.model.scm import ScmModel
52 from rhodecode.model.scm import ScmModel
52 from rhodecode.model.db import Repository
53 from rhodecode.model.db import Repository
53
54
54 from rhodecode.controllers.changeset import anchor_url, _ignorews_url,\
55 from rhodecode.controllers.changeset import anchor_url, _ignorews_url,\
55 _context_url, get_line_ctx, get_ignore_ws
56 _context_url, get_line_ctx, get_ignore_ws
56
57
57
58
58 log = logging.getLogger(__name__)
59 log = logging.getLogger(__name__)
59
60
60
61
61 class FilesController(BaseRepoController):
62 class FilesController(BaseRepoController):
62
63
63 def __before__(self):
64 def __before__(self):
64 super(FilesController, self).__before__()
65 super(FilesController, self).__before__()
65 c.cut_off_limit = self.cut_off_limit
66 c.cut_off_limit = self.cut_off_limit
66
67
67 def __get_cs_or_redirect(self, rev, repo_name, redirect_after=True):
68 def __get_cs_or_redirect(self, rev, repo_name, redirect_after=True):
68 """
69 """
69 Safe way to get changeset if error occur it redirects to tip with
70 Safe way to get changeset if error occur it redirects to tip with
70 proper message
71 proper message
71
72
72 :param rev: revision to fetch
73 :param rev: revision to fetch
73 :param repo_name: repo name to redirect after
74 :param repo_name: repo name to redirect after
74 """
75 """
75
76
76 try:
77 try:
77 return c.rhodecode_repo.get_changeset(rev)
78 return c.rhodecode_repo.get_changeset(rev)
78 except EmptyRepositoryError, e:
79 except EmptyRepositoryError, e:
79 if not redirect_after:
80 if not redirect_after:
80 return None
81 return None
81 url_ = url('files_add_home',
82 url_ = url('files_add_home',
82 repo_name=c.repo_name,
83 repo_name=c.repo_name,
83 revision=0, f_path='')
84 revision=0, f_path='')
84 add_new = '<a href="%s">[%s]</a>' % (url_, _('click here to add new file'))
85 add_new = '<a href="%s">[%s]</a>' % (url_, _('click here to add new file'))
85 h.flash(h.literal(_('There are no files yet %s') % add_new),
86 h.flash(h.literal(_('There are no files yet %s') % add_new),
86 category='warning')
87 category='warning')
87 redirect(h.url('summary_home', repo_name=repo_name))
88 redirect(h.url('summary_home', repo_name=repo_name))
88
89
89 except RepositoryError, e:
90 except RepositoryError, e:
90 h.flash(str(e), category='warning')
91 h.flash(str(e), category='warning')
91 redirect(h.url('files_home', repo_name=repo_name, revision='tip'))
92 redirect(h.url('files_home', repo_name=repo_name, revision='tip'))
92
93
93 def __get_filenode_or_redirect(self, repo_name, cs, path):
94 def __get_filenode_or_redirect(self, repo_name, cs, path):
94 """
95 """
95 Returns file_node, if error occurs or given path is directory,
96 Returns file_node, if error occurs or given path is directory,
96 it'll redirect to top level path
97 it'll redirect to top level path
97
98
98 :param repo_name: repo_name
99 :param repo_name: repo_name
99 :param cs: given changeset
100 :param cs: given changeset
100 :param path: path to lookup
101 :param path: path to lookup
101 """
102 """
102
103
103 try:
104 try:
104 file_node = cs.get_node(path)
105 file_node = cs.get_node(path)
105 if file_node.is_dir():
106 if file_node.is_dir():
106 raise RepositoryError('given path is a directory')
107 raise RepositoryError('given path is a directory')
107 except RepositoryError, e:
108 except RepositoryError, e:
108 h.flash(str(e), category='warning')
109 h.flash(str(e), category='warning')
109 redirect(h.url('files_home', repo_name=repo_name,
110 redirect(h.url('files_home', repo_name=repo_name,
110 revision=cs.raw_id))
111 revision=cs.raw_id))
111
112
112 return file_node
113 return file_node
113
114
114 @LoginRequired()
115 @LoginRequired()
115 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
116 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
116 'repository.admin')
117 'repository.admin')
117 def index(self, repo_name, revision, f_path, annotate=False):
118 def index(self, repo_name, revision, f_path, annotate=False):
118 # redirect to given revision from form if given
119 # redirect to given revision from form if given
119 post_revision = request.POST.get('at_rev', None)
120 post_revision = request.POST.get('at_rev', None)
120 if post_revision:
121 if post_revision:
121 cs = self.__get_cs_or_redirect(post_revision, repo_name)
122 cs = self.__get_cs_or_redirect(post_revision, repo_name)
122 redirect(url('files_home', repo_name=c.repo_name,
123 redirect(url('files_home', repo_name=c.repo_name,
123 revision=cs.raw_id, f_path=f_path))
124 revision=cs.raw_id, f_path=f_path))
124
125
125 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
126 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
126 c.branch = request.GET.get('branch', None)
127 c.branch = request.GET.get('branch', None)
127 c.f_path = f_path
128 c.f_path = f_path
128 c.annotate = annotate
129 c.annotate = annotate
129 cur_rev = c.changeset.revision
130 cur_rev = c.changeset.revision
130
131
131 # prev link
132 # prev link
132 try:
133 try:
133 prev_rev = c.rhodecode_repo.get_changeset(cur_rev).prev(c.branch)
134 prev_rev = c.rhodecode_repo.get_changeset(cur_rev).prev(c.branch)
134 c.url_prev = url('files_home', repo_name=c.repo_name,
135 c.url_prev = url('files_home', repo_name=c.repo_name,
135 revision=prev_rev.raw_id, f_path=f_path)
136 revision=prev_rev.raw_id, f_path=f_path)
136 if c.branch:
137 if c.branch:
137 c.url_prev += '?branch=%s' % c.branch
138 c.url_prev += '?branch=%s' % c.branch
138 except (ChangesetDoesNotExistError, VCSError):
139 except (ChangesetDoesNotExistError, VCSError):
139 c.url_prev = '#'
140 c.url_prev = '#'
140
141
141 # next link
142 # next link
142 try:
143 try:
143 next_rev = c.rhodecode_repo.get_changeset(cur_rev).next(c.branch)
144 next_rev = c.rhodecode_repo.get_changeset(cur_rev).next(c.branch)
144 c.url_next = url('files_home', repo_name=c.repo_name,
145 c.url_next = url('files_home', repo_name=c.repo_name,
145 revision=next_rev.raw_id, f_path=f_path)
146 revision=next_rev.raw_id, f_path=f_path)
146 if c.branch:
147 if c.branch:
147 c.url_next += '?branch=%s' % c.branch
148 c.url_next += '?branch=%s' % c.branch
148 except (ChangesetDoesNotExistError, VCSError):
149 except (ChangesetDoesNotExistError, VCSError):
149 c.url_next = '#'
150 c.url_next = '#'
150
151
151 # files or dirs
152 # files or dirs
152 try:
153 try:
153 c.file = c.changeset.get_node(f_path)
154 c.file = c.changeset.get_node(f_path)
154
155
155 if c.file.is_file():
156 if c.file.is_file():
156 _hist = c.rhodecode_repo.get_changeset().get_file_history(f_path)
157 _hist = c.rhodecode_repo.get_changeset().get_file_history(f_path)
157 c.file_changeset = c.changeset if c.changeset.revision < _hist[0].revision else _hist[0]
158 c.file_changeset = c.changeset if c.changeset.revision < _hist[0].revision else _hist[0]
158 c.file_history = self._get_node_history(None, f_path, _hist)
159 c.file_history = self._get_node_history(None, f_path, _hist)
159 c.authors = []
160 c.authors = []
160 for a in set([x.author for x in _hist]):
161 for a in set([x.author for x in _hist]):
161 c.authors.append((h.email(a), h.person(a)))
162 c.authors.append((h.email(a), h.person(a)))
162 else:
163 else:
163 c.authors = c.file_history = []
164 c.authors = c.file_history = []
164 except RepositoryError, e:
165 except RepositoryError, e:
165 h.flash(str(e), category='warning')
166 h.flash(str(e), category='warning')
166 redirect(h.url('files_home', repo_name=repo_name,
167 redirect(h.url('files_home', repo_name=repo_name,
167 revision='tip'))
168 revision='tip'))
168
169
169 if request.environ.get('HTTP_X_PARTIAL_XHR'):
170 if request.environ.get('HTTP_X_PARTIAL_XHR'):
170 return render('files/files_ypjax.html')
171 return render('files/files_ypjax.html')
171
172
172 return render('files/files.html')
173 return render('files/files.html')
173
174
174 @LoginRequired()
175 @LoginRequired()
175 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
176 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
176 'repository.admin')
177 'repository.admin')
177 def rawfile(self, repo_name, revision, f_path):
178 def rawfile(self, repo_name, revision, f_path):
178 cs = self.__get_cs_or_redirect(revision, repo_name)
179 cs = self.__get_cs_or_redirect(revision, repo_name)
179 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
180 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
180
181
181 response.content_disposition = 'attachment; filename=%s' % \
182 response.content_disposition = 'attachment; filename=%s' % \
182 safe_str(f_path.split(Repository.url_sep())[-1])
183 safe_str(f_path.split(Repository.url_sep())[-1])
183
184
184 response.content_type = file_node.mimetype
185 response.content_type = file_node.mimetype
185 return file_node.content
186 return file_node.content
186
187
187 @LoginRequired()
188 @LoginRequired()
188 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
189 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
189 'repository.admin')
190 'repository.admin')
190 def raw(self, repo_name, revision, f_path):
191 def raw(self, repo_name, revision, f_path):
191 cs = self.__get_cs_or_redirect(revision, repo_name)
192 cs = self.__get_cs_or_redirect(revision, repo_name)
192 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
193 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
193
194
194 raw_mimetype_mapping = {
195 raw_mimetype_mapping = {
195 # map original mimetype to a mimetype used for "show as raw"
196 # map original mimetype to a mimetype used for "show as raw"
196 # you can also provide a content-disposition to override the
197 # you can also provide a content-disposition to override the
197 # default "attachment" disposition.
198 # default "attachment" disposition.
198 # orig_type: (new_type, new_dispo)
199 # orig_type: (new_type, new_dispo)
199
200
200 # show images inline:
201 # show images inline:
201 'image/x-icon': ('image/x-icon', 'inline'),
202 'image/x-icon': ('image/x-icon', 'inline'),
202 'image/png': ('image/png', 'inline'),
203 'image/png': ('image/png', 'inline'),
203 'image/gif': ('image/gif', 'inline'),
204 'image/gif': ('image/gif', 'inline'),
204 'image/jpeg': ('image/jpeg', 'inline'),
205 'image/jpeg': ('image/jpeg', 'inline'),
205 'image/svg+xml': ('image/svg+xml', 'inline'),
206 'image/svg+xml': ('image/svg+xml', 'inline'),
206 }
207 }
207
208
208 mimetype = file_node.mimetype
209 mimetype = file_node.mimetype
209 try:
210 try:
210 mimetype, dispo = raw_mimetype_mapping[mimetype]
211 mimetype, dispo = raw_mimetype_mapping[mimetype]
211 except KeyError:
212 except KeyError:
212 # we don't know anything special about this, handle it safely
213 # we don't know anything special about this, handle it safely
213 if file_node.is_binary:
214 if file_node.is_binary:
214 # do same as download raw for binary files
215 # do same as download raw for binary files
215 mimetype, dispo = 'application/octet-stream', 'attachment'
216 mimetype, dispo = 'application/octet-stream', 'attachment'
216 else:
217 else:
217 # do not just use the original mimetype, but force text/plain,
218 # do not just use the original mimetype, but force text/plain,
218 # otherwise it would serve text/html and that might be unsafe.
219 # otherwise it would serve text/html and that might be unsafe.
219 # Note: underlying vcs library fakes text/plain mimetype if the
220 # Note: underlying vcs library fakes text/plain mimetype if the
220 # mimetype can not be determined and it thinks it is not
221 # mimetype can not be determined and it thinks it is not
221 # binary.This might lead to erroneous text display in some
222 # binary.This might lead to erroneous text display in some
222 # cases, but helps in other cases, like with text files
223 # cases, but helps in other cases, like with text files
223 # without extension.
224 # without extension.
224 mimetype, dispo = 'text/plain', 'inline'
225 mimetype, dispo = 'text/plain', 'inline'
225
226
226 if dispo == 'attachment':
227 if dispo == 'attachment':
227 dispo = 'attachment; filename=%s' % \
228 dispo = 'attachment; filename=%s' % \
228 safe_str(f_path.split(os.sep)[-1])
229 safe_str(f_path.split(os.sep)[-1])
229
230
230 response.content_disposition = dispo
231 response.content_disposition = dispo
231 response.content_type = mimetype
232 response.content_type = mimetype
232 return file_node.content
233 return file_node.content
233
234
234 @LoginRequired()
235 @LoginRequired()
235 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
236 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
236 def edit(self, repo_name, revision, f_path):
237 def edit(self, repo_name, revision, f_path):
237 repo = Repository.get_by_repo_name(repo_name)
238 repo = Repository.get_by_repo_name(repo_name)
238 if repo.enable_locking and repo.locked[0]:
239 if repo.enable_locking and repo.locked[0]:
239 h.flash(_('This repository is has been locked by %s on %s')
240 h.flash(_('This repository is has been locked by %s on %s')
240 % (h.person_by_id(repo.locked[0]),
241 % (h.person_by_id(repo.locked[0]),
241 h.fmt_date(h.time_to_datetime(repo.locked[1]))),
242 h.fmt_date(h.time_to_datetime(repo.locked[1]))),
242 'warning')
243 'warning')
243 return redirect(h.url('files_home',
244 return redirect(h.url('files_home',
244 repo_name=repo_name, revision='tip'))
245 repo_name=repo_name, revision='tip'))
245
246
246 r_post = request.POST
247 r_post = request.POST
247
248
248 c.cs = self.__get_cs_or_redirect(revision, repo_name)
249 c.cs = self.__get_cs_or_redirect(revision, repo_name)
249 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
250 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
250
251
251 if c.file.is_binary:
252 if c.file.is_binary:
252 return redirect(url('files_home', repo_name=c.repo_name,
253 return redirect(url('files_home', repo_name=c.repo_name,
253 revision=c.cs.raw_id, f_path=f_path))
254 revision=c.cs.raw_id, f_path=f_path))
254
255
255 c.f_path = f_path
256 c.f_path = f_path
256
257
257 if r_post:
258 if r_post:
258
259
259 old_content = c.file.content
260 old_content = c.file.content
260 sl = old_content.splitlines(1)
261 sl = old_content.splitlines(1)
261 first_line = sl[0] if sl else ''
262 first_line = sl[0] if sl else ''
262 # modes: 0 - Unix, 1 - Mac, 2 - DOS
263 # modes: 0 - Unix, 1 - Mac, 2 - DOS
263 mode = detect_mode(first_line, 0)
264 mode = detect_mode(first_line, 0)
264 content = convert_line_endings(r_post.get('content'), mode)
265 content = convert_line_endings(r_post.get('content'), mode)
265
266
266 message = r_post.get('message') or (_('Edited %s via RhodeCode')
267 message = r_post.get('message') or (_('Edited %s via RhodeCode')
267 % (f_path))
268 % (f_path))
268 author = self.rhodecode_user.full_contact
269 author = self.rhodecode_user.full_contact
269
270
270 if content == old_content:
271 if content == old_content:
271 h.flash(_('No changes'),
272 h.flash(_('No changes'),
272 category='warning')
273 category='warning')
273 return redirect(url('changeset_home', repo_name=c.repo_name,
274 return redirect(url('changeset_home', repo_name=c.repo_name,
274 revision='tip'))
275 revision='tip'))
275
276
276 try:
277 try:
277 self.scm_model.commit_change(repo=c.rhodecode_repo,
278 self.scm_model.commit_change(repo=c.rhodecode_repo,
278 repo_name=repo_name, cs=c.cs,
279 repo_name=repo_name, cs=c.cs,
279 user=self.rhodecode_user,
280 user=self.rhodecode_user,
280 author=author, message=message,
281 author=author, message=message,
281 content=content, f_path=f_path)
282 content=content, f_path=f_path)
282 h.flash(_('Successfully committed to %s') % f_path,
283 h.flash(_('Successfully committed to %s') % f_path,
283 category='success')
284 category='success')
284
285
285 except Exception:
286 except Exception:
286 log.error(traceback.format_exc())
287 log.error(traceback.format_exc())
287 h.flash(_('Error occurred during commit'), category='error')
288 h.flash(_('Error occurred during commit'), category='error')
288 return redirect(url('changeset_home',
289 return redirect(url('changeset_home',
289 repo_name=c.repo_name, revision='tip'))
290 repo_name=c.repo_name, revision='tip'))
290
291
291 return render('files/files_edit.html')
292 return render('files/files_edit.html')
292
293
293 @LoginRequired()
294 @LoginRequired()
294 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
295 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
295 def add(self, repo_name, revision, f_path):
296 def add(self, repo_name, revision, f_path):
296
297
297 repo = Repository.get_by_repo_name(repo_name)
298 repo = Repository.get_by_repo_name(repo_name)
298 if repo.enable_locking and repo.locked[0]:
299 if repo.enable_locking and repo.locked[0]:
299 h.flash(_('This repository is has been locked by %s on %s')
300 h.flash(_('This repository is has been locked by %s on %s')
300 % (h.person_by_id(repo.locked[0]),
301 % (h.person_by_id(repo.locked[0]),
301 h.fmt_date(h.time_to_datetime(repo.locked[1]))),
302 h.fmt_date(h.time_to_datetime(repo.locked[1]))),
302 'warning')
303 'warning')
303 return redirect(h.url('files_home',
304 return redirect(h.url('files_home',
304 repo_name=repo_name, revision='tip'))
305 repo_name=repo_name, revision='tip'))
305
306
306 r_post = request.POST
307 r_post = request.POST
307 c.cs = self.__get_cs_or_redirect(revision, repo_name,
308 c.cs = self.__get_cs_or_redirect(revision, repo_name,
308 redirect_after=False)
309 redirect_after=False)
309 if c.cs is None:
310 if c.cs is None:
310 c.cs = EmptyChangeset(alias=c.rhodecode_repo.alias)
311 c.cs = EmptyChangeset(alias=c.rhodecode_repo.alias)
311
312
312 c.f_path = f_path
313 c.f_path = f_path
313
314
314 if r_post:
315 if r_post:
315 unix_mode = 0
316 unix_mode = 0
316 content = convert_line_endings(r_post.get('content'), unix_mode)
317 content = convert_line_endings(r_post.get('content'), unix_mode)
317
318
318 message = r_post.get('message') or (_('Added %s via RhodeCode')
319 message = r_post.get('message') or (_('Added %s via RhodeCode')
319 % (f_path))
320 % (f_path))
320 location = r_post.get('location')
321 location = r_post.get('location')
321 filename = r_post.get('filename')
322 filename = r_post.get('filename')
322 file_obj = r_post.get('upload_file', None)
323 file_obj = r_post.get('upload_file', None)
323
324
324 if file_obj is not None and hasattr(file_obj, 'filename'):
325 if file_obj is not None and hasattr(file_obj, 'filename'):
325 filename = file_obj.filename
326 filename = file_obj.filename
326 content = file_obj.file
327 content = file_obj.file
327
328
328 node_path = os.path.join(location, filename)
329 node_path = os.path.join(location, filename)
329 author = self.rhodecode_user.full_contact
330 author = self.rhodecode_user.full_contact
330
331
331 if not content:
332 if not content:
332 h.flash(_('No content'), category='warning')
333 h.flash(_('No content'), category='warning')
333 return redirect(url('changeset_home', repo_name=c.repo_name,
334 return redirect(url('changeset_home', repo_name=c.repo_name,
334 revision='tip'))
335 revision='tip'))
335 if not filename:
336 if not filename:
336 h.flash(_('No filename'), category='warning')
337 h.flash(_('No filename'), category='warning')
337 return redirect(url('changeset_home', repo_name=c.repo_name,
338 return redirect(url('changeset_home', repo_name=c.repo_name,
338 revision='tip'))
339 revision='tip'))
339
340
340 try:
341 try:
341 self.scm_model.create_node(repo=c.rhodecode_repo,
342 self.scm_model.create_node(repo=c.rhodecode_repo,
342 repo_name=repo_name, cs=c.cs,
343 repo_name=repo_name, cs=c.cs,
343 user=self.rhodecode_user,
344 user=self.rhodecode_user,
344 author=author, message=message,
345 author=author, message=message,
345 content=content, f_path=node_path)
346 content=content, f_path=node_path)
346 h.flash(_('Successfully committed to %s') % node_path,
347 h.flash(_('Successfully committed to %s') % node_path,
347 category='success')
348 category='success')
348 except NodeAlreadyExistsError, e:
349 except NodeAlreadyExistsError, e:
349 h.flash(_(e), category='error')
350 h.flash(_(e), category='error')
350 except Exception:
351 except Exception:
351 log.error(traceback.format_exc())
352 log.error(traceback.format_exc())
352 h.flash(_('Error occurred during commit'), category='error')
353 h.flash(_('Error occurred during commit'), category='error')
353 return redirect(url('changeset_home',
354 return redirect(url('changeset_home',
354 repo_name=c.repo_name, revision='tip'))
355 repo_name=c.repo_name, revision='tip'))
355
356
356 return render('files/files_add.html')
357 return render('files/files_add.html')
357
358
358 @LoginRequired()
359 @LoginRequired()
359 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
360 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
360 'repository.admin')
361 'repository.admin')
361 def archivefile(self, repo_name, fname):
362 def archivefile(self, repo_name, fname):
362
363
363 fileformat = None
364 fileformat = None
364 revision = None
365 revision = None
365 ext = None
366 ext = None
366 subrepos = request.GET.get('subrepos') == 'true'
367 subrepos = request.GET.get('subrepos') == 'true'
367
368
368 for a_type, ext_data in settings.ARCHIVE_SPECS.items():
369 for a_type, ext_data in settings.ARCHIVE_SPECS.items():
369 archive_spec = fname.split(ext_data[1])
370 archive_spec = fname.split(ext_data[1])
370 if len(archive_spec) == 2 and archive_spec[1] == '':
371 if len(archive_spec) == 2 and archive_spec[1] == '':
371 fileformat = a_type or ext_data[1]
372 fileformat = a_type or ext_data[1]
372 revision = archive_spec[0]
373 revision = archive_spec[0]
373 ext = ext_data[1]
374 ext = ext_data[1]
374
375
375 try:
376 try:
376 dbrepo = RepoModel().get_by_repo_name(repo_name)
377 dbrepo = RepoModel().get_by_repo_name(repo_name)
377 if dbrepo.enable_downloads is False:
378 if dbrepo.enable_downloads is False:
378 return _('downloads disabled')
379 return _('downloads disabled')
379
380
380 if c.rhodecode_repo.alias == 'hg':
381 if c.rhodecode_repo.alias == 'hg':
381 # patch and reset hooks section of UI config to not run any
382 # patch and reset hooks section of UI config to not run any
382 # hooks on fetching archives with subrepos
383 # hooks on fetching archives with subrepos
383 for k, v in c.rhodecode_repo._repo.ui.configitems('hooks'):
384 for k, v in c.rhodecode_repo._repo.ui.configitems('hooks'):
384 c.rhodecode_repo._repo.ui.setconfig('hooks', k, None)
385 c.rhodecode_repo._repo.ui.setconfig('hooks', k, None)
385
386
386 cs = c.rhodecode_repo.get_changeset(revision)
387 cs = c.rhodecode_repo.get_changeset(revision)
387 content_type = settings.ARCHIVE_SPECS[fileformat][0]
388 content_type = settings.ARCHIVE_SPECS[fileformat][0]
388 except ChangesetDoesNotExistError:
389 except ChangesetDoesNotExistError:
389 return _('Unknown revision %s') % revision
390 return _('Unknown revision %s') % revision
390 except EmptyRepositoryError:
391 except EmptyRepositoryError:
391 return _('Empty repository')
392 return _('Empty repository')
392 except (ImproperArchiveTypeError, KeyError):
393 except (ImproperArchiveTypeError, KeyError):
393 return _('Unknown archive type')
394 return _('Unknown archive type')
394
395
395 fd, archive = tempfile.mkstemp()
396 fd, archive = tempfile.mkstemp()
396 t = open(archive, 'wb')
397 t = open(archive, 'wb')
397 cs.fill_archive(stream=t, kind=fileformat, subrepos=subrepos)
398 cs.fill_archive(stream=t, kind=fileformat, subrepos=subrepos)
398 t.close()
399 t.close()
399
400
400 def get_chunked_archive(archive):
401 def get_chunked_archive(archive):
401 stream = open(archive, 'rb')
402 stream = open(archive, 'rb')
402 while True:
403 while True:
403 data = stream.read(16 * 1024)
404 data = stream.read(16 * 1024)
404 if not data:
405 if not data:
405 stream.close()
406 stream.close()
406 os.close(fd)
407 os.close(fd)
407 os.remove(archive)
408 os.remove(archive)
408 break
409 break
409 yield data
410 yield data
410
411
411 response.content_disposition = str('attachment; filename=%s-%s%s' \
412 response.content_disposition = str('attachment; filename=%s-%s%s' \
412 % (repo_name, revision[:12], ext))
413 % (repo_name, revision[:12], ext))
413 response.content_type = str(content_type)
414 response.content_type = str(content_type)
414 return get_chunked_archive(archive)
415 return get_chunked_archive(archive)
415
416
416 @LoginRequired()
417 @LoginRequired()
417 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
418 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
418 'repository.admin')
419 'repository.admin')
419 def diff(self, repo_name, f_path):
420 def diff(self, repo_name, f_path):
420 ignore_whitespace = request.GET.get('ignorews') == '1'
421 ignore_whitespace = request.GET.get('ignorews') == '1'
421 line_context = request.GET.get('context', 3)
422 line_context = request.GET.get('context', 3)
422 diff1 = request.GET.get('diff1', '')
423 diff1 = request.GET.get('diff1', '')
423 diff2 = request.GET.get('diff2', '')
424 diff2 = request.GET.get('diff2', '')
424 c.action = request.GET.get('diff')
425 c.action = request.GET.get('diff')
425 c.no_changes = diff1 == diff2
426 c.no_changes = diff1 == diff2
426 c.f_path = f_path
427 c.f_path = f_path
427 c.big_diff = False
428 c.big_diff = False
428 c.anchor_url = anchor_url
429 c.anchor_url = anchor_url
429 c.ignorews_url = _ignorews_url
430 c.ignorews_url = _ignorews_url
430 c.context_url = _context_url
431 c.context_url = _context_url
431 c.changes = OrderedDict()
432 c.changes = OrderedDict()
432 c.changes[diff2] = []
433 c.changes[diff2] = []
434
435 #special case if we want a show rev only, it's impl here
436 #to reduce JS and callbacks
437 if request.GET.get('show_rev'):
438 if str2bool(request.GET.get('annotate', 'False')):
439 _url = url('files_annotate_home', repo_name=c.repo_name,
440 revision=diff1, f_path=c.f_path)
441 else:
442 _url = url('files_home', repo_name=c.repo_name,
443 revision=diff1, f_path=c.f_path)
444
445 return redirect(_url)
433 try:
446 try:
434 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
447 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
435 c.changeset_1 = c.rhodecode_repo.get_changeset(diff1)
448 c.changeset_1 = c.rhodecode_repo.get_changeset(diff1)
436 node1 = c.changeset_1.get_node(f_path)
449 node1 = c.changeset_1.get_node(f_path)
437 else:
450 else:
438 c.changeset_1 = EmptyChangeset(repo=c.rhodecode_repo)
451 c.changeset_1 = EmptyChangeset(repo=c.rhodecode_repo)
439 node1 = FileNode('.', '', changeset=c.changeset_1)
452 node1 = FileNode('.', '', changeset=c.changeset_1)
440
453
441 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
454 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
442 c.changeset_2 = c.rhodecode_repo.get_changeset(diff2)
455 c.changeset_2 = c.rhodecode_repo.get_changeset(diff2)
443 node2 = c.changeset_2.get_node(f_path)
456 node2 = c.changeset_2.get_node(f_path)
444 else:
457 else:
445 c.changeset_2 = EmptyChangeset(repo=c.rhodecode_repo)
458 c.changeset_2 = EmptyChangeset(repo=c.rhodecode_repo)
446 node2 = FileNode('.', '', changeset=c.changeset_2)
459 node2 = FileNode('.', '', changeset=c.changeset_2)
447 except RepositoryError:
460 except RepositoryError:
448 return redirect(url('files_home', repo_name=c.repo_name,
461 return redirect(url('files_home', repo_name=c.repo_name,
449 f_path=f_path))
462 f_path=f_path))
450
463
451 if c.action == 'download':
464 if c.action == 'download':
452 _diff = diffs.get_gitdiff(node1, node2,
465 _diff = diffs.get_gitdiff(node1, node2,
453 ignore_whitespace=ignore_whitespace,
466 ignore_whitespace=ignore_whitespace,
454 context=line_context)
467 context=line_context)
455 diff = diffs.DiffProcessor(_diff, format='gitdiff')
468 diff = diffs.DiffProcessor(_diff, format='gitdiff')
456
469
457 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
470 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
458 response.content_type = 'text/plain'
471 response.content_type = 'text/plain'
459 response.content_disposition = (
472 response.content_disposition = (
460 'attachment; filename=%s' % diff_name
473 'attachment; filename=%s' % diff_name
461 )
474 )
462 return diff.raw_diff()
475 return diff.raw_diff()
463
476
464 elif c.action == 'raw':
477 elif c.action == 'raw':
465 _diff = diffs.get_gitdiff(node1, node2,
478 _diff = diffs.get_gitdiff(node1, node2,
466 ignore_whitespace=ignore_whitespace,
479 ignore_whitespace=ignore_whitespace,
467 context=line_context)
480 context=line_context)
468 diff = diffs.DiffProcessor(_diff, format='gitdiff')
481 diff = diffs.DiffProcessor(_diff, format='gitdiff')
469 response.content_type = 'text/plain'
482 response.content_type = 'text/plain'
470 return diff.raw_diff()
483 return diff.raw_diff()
471
484
472 else:
485 else:
473 fid = h.FID(diff2, node2.path)
486 fid = h.FID(diff2, node2.path)
474 line_context_lcl = get_line_ctx(fid, request.GET)
487 line_context_lcl = get_line_ctx(fid, request.GET)
475 ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
488 ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
476
489
477 lim = request.GET.get('fulldiff') or self.cut_off_limit
490 lim = request.GET.get('fulldiff') or self.cut_off_limit
478 _, cs1, cs2, diff, st = diffs.wrapped_diff(filenode_old=node1,
491 _, cs1, cs2, diff, st = diffs.wrapped_diff(filenode_old=node1,
479 filenode_new=node2,
492 filenode_new=node2,
480 cut_off_limit=lim,
493 cut_off_limit=lim,
481 ignore_whitespace=ign_whitespace_lcl,
494 ignore_whitespace=ign_whitespace_lcl,
482 line_context=line_context_lcl,
495 line_context=line_context_lcl,
483 enable_comments=False)
496 enable_comments=False)
484
497
485 c.changes = [('', node2, diff, cs1, cs2, st,)]
498 c.changes = [('', node2, diff, cs1, cs2, st,)]
486
499
487 return render('files/file_diff.html')
500 return render('files/file_diff.html')
488
501
489 def _get_node_history(self, cs, f_path, changesets=None):
502 def _get_node_history(self, cs, f_path, changesets=None):
490 if cs is None:
503 if cs is None:
491 # if we pass empty CS calculate history based on tip
504 # if we pass empty CS calculate history based on tip
492 cs = c.rhodecode_repo.get_changeset()
505 cs = c.rhodecode_repo.get_changeset()
493 if changesets is None:
506 if changesets is None:
494 changesets = cs.get_file_history(f_path)
507 changesets = cs.get_file_history(f_path)
495
508
496 hist_l = []
509 hist_l = []
497
510
498 changesets_group = ([], _("Changesets"))
511 changesets_group = ([], _("Changesets"))
499 branches_group = ([], _("Branches"))
512 branches_group = ([], _("Branches"))
500 tags_group = ([], _("Tags"))
513 tags_group = ([], _("Tags"))
501 _hg = cs.repository.alias == 'hg'
514 _hg = cs.repository.alias == 'hg'
502 for chs in changesets:
515 for chs in changesets:
503 _branch = '(%s)' % chs.branch if _hg else ''
516 _branch = '(%s)' % chs.branch if _hg else ''
504 n_desc = 'r%s:%s %s' % (chs.revision, chs.short_id, _branch)
517 n_desc = 'r%s:%s %s' % (chs.revision, chs.short_id, _branch)
505 changesets_group[0].append((chs.raw_id, n_desc,))
518 changesets_group[0].append((chs.raw_id, n_desc,))
506
519
507 hist_l.append(changesets_group)
520 hist_l.append(changesets_group)
508
521
509 for name, chs in c.rhodecode_repo.branches.items():
522 for name, chs in c.rhodecode_repo.branches.items():
510 branches_group[0].append((chs, name),)
523 branches_group[0].append((chs, name),)
511 hist_l.append(branches_group)
524 hist_l.append(branches_group)
512
525
513 for name, chs in c.rhodecode_repo.tags.items():
526 for name, chs in c.rhodecode_repo.tags.items():
514 tags_group[0].append((chs, name),)
527 tags_group[0].append((chs, name),)
515 hist_l.append(tags_group)
528 hist_l.append(tags_group)
516
529
517 return hist_l
530 return hist_l
518
531
519 @LoginRequired()
532 @LoginRequired()
520 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
533 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
521 'repository.admin')
534 'repository.admin')
522 @jsonify
535 @jsonify
523 def nodelist(self, repo_name, revision, f_path):
536 def nodelist(self, repo_name, revision, f_path):
524 if request.environ.get('HTTP_X_PARTIAL_XHR'):
537 if request.environ.get('HTTP_X_PARTIAL_XHR'):
525 cs = self.__get_cs_or_redirect(revision, repo_name)
538 cs = self.__get_cs_or_redirect(revision, repo_name)
526 _d, _f = ScmModel().get_nodes(repo_name, cs.raw_id, f_path,
539 _d, _f = ScmModel().get_nodes(repo_name, cs.raw_id, f_path,
527 flat=False)
540 flat=False)
528 return {'nodes': _d + _f}
541 return {'nodes': _d + _f}
@@ -1,126 +1,118 b''
1 <dl>
1 <dl>
2 <dt class="file_history">${_('History')}</dt>
2 <dt class="file_history">${_('History')}</dt>
3 <dd>
3 <dd>
4 <div>
4 <div>
5 <div style="float:left">
5 <div style="float:left">
6 ${h.form(h.url('files_diff_home',repo_name=c.repo_name,f_path=c.f_path),method='get')}
6 ${h.form(h.url('files_diff_home',repo_name=c.repo_name,f_path=c.f_path),method='get')}
7 ${h.hidden('diff2',c.file_changeset.raw_id)}
7 ${h.hidden('diff2',c.file_changeset.raw_id)}
8 ${h.select('diff1',c.file_changeset.raw_id,c.file_history)}
8 ${h.select('diff1',c.file_changeset.raw_id,c.file_history)}
9 ${h.submit('diff',_('diff to revision'),class_="ui-btn")}
9 ${h.submit('diff',_('diff to revision'),class_="ui-btn")}
10 ${h.submit('show_rev',_('show at revision'),class_="ui-btn")}
10 ${h.submit('show_rev',_('show at revision'),class_="ui-btn")}
11 ${h.hidden('annotate', c.annotate)}
11 ${h.end_form()}
12 ${h.end_form()}
12 </div>
13 </div>
13 <div class="file_author">
14 <div class="file_author">
14 <div class="item">${h.literal(ungettext(u'%s author',u'%s authors',len(c.authors)) % ('<b>%s</b>' % len(c.authors))) }</div>
15 <div class="item">${h.literal(ungettext(u'%s author',u'%s authors',len(c.authors)) % ('<b>%s</b>' % len(c.authors))) }</div>
15 %for email, user in c.authors:
16 %for email, user in c.authors:
16 <div class="contributor tooltip" style="float:left" title="${h.tooltip(user)}">
17 <div class="contributor tooltip" style="float:left" title="${h.tooltip(user)}">
17 <div class="gravatar" style="margin:1px"><img alt="gravatar" src="${h.gravatar_url(email, 20)}"/> </div>
18 <div class="gravatar" style="margin:1px"><img alt="gravatar" src="${h.gravatar_url(email, 20)}"/> </div>
18 </div>
19 </div>
19 %endfor
20 %endfor
20 </div>
21 </div>
21 </div>
22 </div>
22 <div style="clear:both"></div>
23 <div style="clear:both"></div>
23 </dd>
24 </dd>
24
25
25 </dl>
26 </dl>
26
27
27 <div id="body" class="codeblock">
28 <div id="body" class="codeblock">
28 <div class="code-header">
29 <div class="code-header">
29 <div class="stats">
30 <div class="stats">
30 <div class="left img"><img src="${h.url('/images/icons/file.png')}"/></div>
31 <div class="left img"><img src="${h.url('/images/icons/file.png')}"/></div>
31 <div class="left item"><pre class="tooltip" title="${h.tooltip(h.fmt_date(c.file_changeset.date))}">${h.link_to("r%s:%s" % (c.file_changeset.revision,h.short_id(c.file_changeset.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=c.file_changeset.raw_id))}</pre></div>
32 <div class="left item"><pre class="tooltip" title="${h.tooltip(h.fmt_date(c.file_changeset.date))}">${h.link_to("r%s:%s" % (c.file_changeset.revision,h.short_id(c.file_changeset.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=c.file_changeset.raw_id))}</pre></div>
32 <div class="left item"><pre>${h.format_byte_size(c.file.size,binary=True)}</pre></div>
33 <div class="left item"><pre>${h.format_byte_size(c.file.size,binary=True)}</pre></div>
33 <div class="left item last"><pre>${c.file.mimetype}</pre></div>
34 <div class="left item last"><pre>${c.file.mimetype}</pre></div>
34 <div class="buttons">
35 <div class="buttons">
35 %if c.annotate:
36 %if c.annotate:
36 ${h.link_to(_('show source'), h.url('files_home', repo_name=c.repo_name,revision=c.file_changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
37 ${h.link_to(_('show source'), h.url('files_home', repo_name=c.repo_name,revision=c.file_changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
37 %else:
38 %else:
38 ${h.link_to(_('show annotation'),h.url('files_annotate_home',repo_name=c.repo_name,revision=c.file_changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
39 ${h.link_to(_('show annotation'),h.url('files_annotate_home',repo_name=c.repo_name,revision=c.file_changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
39 %endif
40 %endif
40 ${h.link_to(_('show as raw'),h.url('files_raw_home',repo_name=c.repo_name,revision=c.file_changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
41 ${h.link_to(_('show as raw'),h.url('files_raw_home',repo_name=c.repo_name,revision=c.file_changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
41 ${h.link_to(_('download as raw'),h.url('files_rawfile_home',repo_name=c.repo_name,revision=c.file_changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
42 ${h.link_to(_('download as raw'),h.url('files_rawfile_home',repo_name=c.repo_name,revision=c.file_changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
42 % if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
43 % if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
43 % if not c.file.is_binary:
44 % if not c.file.is_binary:
44 ${h.link_to(_('edit'),h.url('files_edit_home',repo_name=c.repo_name,revision=c.file_changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
45 ${h.link_to(_('edit'),h.url('files_edit_home',repo_name=c.repo_name,revision=c.file_changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
45 % endif
46 % endif
46 % endif
47 % endif
47 </div>
48 </div>
48 </div>
49 </div>
49 <div class="author">
50 <div class="author">
50 <div class="gravatar">
51 <div class="gravatar">
51 <img alt="gravatar" src="${h.gravatar_url(h.email_or_none(c.file_changeset.author),16)}"/>
52 <img alt="gravatar" src="${h.gravatar_url(h.email_or_none(c.file_changeset.author),16)}"/>
52 </div>
53 </div>
53 <div title="${c.file_changeset.author}" class="user">${h.person(c.file_changeset.author)}</div>
54 <div title="${c.file_changeset.author}" class="user">${h.person(c.file_changeset.author)}</div>
54 </div>
55 </div>
55 <div class="commit">${h.urlify_commit(c.file_changeset.message,c.repo_name)}</div>
56 <div class="commit">${h.urlify_commit(c.file_changeset.message,c.repo_name)}</div>
56 </div>
57 </div>
57 <div class="code-body">
58 <div class="code-body">
58 %if c.file.is_binary:
59 %if c.file.is_binary:
59 ${_('Binary file (%s)') % c.file.mimetype}
60 ${_('Binary file (%s)') % c.file.mimetype}
60 %else:
61 %else:
61 % if c.file.size < c.cut_off_limit:
62 % if c.file.size < c.cut_off_limit:
62 %if c.annotate:
63 %if c.annotate:
63 ${h.pygmentize_annotation(c.repo_name,c.file,linenos=True,anchorlinenos=True,lineanchors='L',cssclass="code-highlight")}
64 ${h.pygmentize_annotation(c.repo_name,c.file,linenos=True,anchorlinenos=True,lineanchors='L',cssclass="code-highlight")}
64 %else:
65 %else:
65 ${h.pygmentize(c.file,linenos=True,anchorlinenos=True,lineanchors='L',cssclass="code-highlight")}
66 ${h.pygmentize(c.file,linenos=True,anchorlinenos=True,lineanchors='L',cssclass="code-highlight")}
66 %endif
67 %endif
67 %else:
68 %else:
68 ${_('File is too big to display')} ${h.link_to(_('show as raw'),
69 ${_('File is too big to display')} ${h.link_to(_('show as raw'),
69 h.url('files_raw_home',repo_name=c.repo_name,revision=c.file_changeset.raw_id,f_path=c.f_path))}
70 h.url('files_raw_home',repo_name=c.repo_name,revision=c.file_changeset.raw_id,f_path=c.f_path))}
70 %endif
71 %endif
71 %endif
72 %endif
72 </div>
73 </div>
73 </div>
74 </div>
74
75
75 <script type="text/javascript">
76 <script type="text/javascript">
76 YUE.onDOMReady(function(){
77 YUE.onDOMReady(function(){
77 function highlight_lines(lines){
78 function highlight_lines(lines){
78 for(pos in lines){
79 for(pos in lines){
79 YUD.setStyle('L'+lines[pos],'background-color','#FFFFBE');
80 YUD.setStyle('L'+lines[pos],'background-color','#FFFFBE');
80 }
81 }
81 }
82 }
82 page_highlights = location.href.substring(location.href.indexOf('#')+1).split('L');
83 page_highlights = location.href.substring(location.href.indexOf('#')+1).split('L');
83 if (page_highlights.length == 2){
84 if (page_highlights.length == 2){
84 highlight_ranges = page_highlights[1].split(",");
85 highlight_ranges = page_highlights[1].split(",");
85
86
86 var h_lines = [];
87 var h_lines = [];
87 for (pos in highlight_ranges){
88 for (pos in highlight_ranges){
88 var _range = highlight_ranges[pos].split('-');
89 var _range = highlight_ranges[pos].split('-');
89 if(_range.length == 2){
90 if(_range.length == 2){
90 var start = parseInt(_range[0]);
91 var start = parseInt(_range[0]);
91 var end = parseInt(_range[1]);
92 var end = parseInt(_range[1]);
92 if (start < end){
93 if (start < end){
93 for(var i=start;i<=end;i++){
94 for(var i=start;i<=end;i++){
94 h_lines.push(i);
95 h_lines.push(i);
95 }
96 }
96 }
97 }
97 }
98 }
98 else{
99 else{
99 h_lines.push(parseInt(highlight_ranges[pos]));
100 h_lines.push(parseInt(highlight_ranges[pos]));
100 }
101 }
101 }
102 }
102 highlight_lines(h_lines);
103 highlight_lines(h_lines);
103
104
104 //remember original location
105 //remember original location
105 var old_hash = location.href.substring(location.href.indexOf('#'));
106 var old_hash = location.href.substring(location.href.indexOf('#'));
106
107
107 // this makes a jump to anchor moved by 3 posstions for padding
108 // this makes a jump to anchor moved by 3 posstions for padding
108 window.location.hash = '#L'+Math.max(parseInt(h_lines[0])-3,1);
109 window.location.hash = '#L'+Math.max(parseInt(h_lines[0])-3,1);
109
110
110 //sets old anchor
111 //sets old anchor
111 window.location.hash = old_hash;
112 window.location.hash = old_hash;
112
113
113 }
114 }
114 YUE.on('show_rev','click',function(e){
115
115 YUE.preventDefault(e);
116 var cs = YUD.get('diff1').value;
117 %if c.annotate:
118 var url = "${h.url('files_annotate_home',repo_name=c.repo_name,revision='__CS__',f_path=c.f_path)}".replace('__CS__',cs);
119 %else:
120 var url = "${h.url('files_home',repo_name=c.repo_name,revision='__CS__',f_path=c.f_path)}".replace('__CS__',cs);
121 %endif
122 window.location = url;
123 });
124 YUE.on('hlcode','mouseup',getSelectionLink("${_('Selection link')}"))
116 YUE.on('hlcode','mouseup',getSelectionLink("${_('Selection link')}"))
125 });
117 });
126 </script>
118 </script>
General Comments 0
You need to be logged in to leave comments. Login now