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