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