##// END OF EJS Templates
Fixed issue when node didn't exists at 'tip' and we tried calculate history based on that assumption....
marcink -
r2977:cff9d4e1 beta
parent child Browse files
Show More
@@ -1,545 +1,558 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 40 from rhodecode.lib.utils2 import convert_line_endings, detect_mode, safe_str,\
41 41 str2bool
42 42 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
43 43 from rhodecode.lib.base import BaseRepoController, render
44 44 from rhodecode.lib.vcs.backends.base import EmptyChangeset
45 45 from rhodecode.lib.vcs.conf import settings
46 46 from rhodecode.lib.vcs.exceptions import RepositoryError, \
47 47 ChangesetDoesNotExistError, EmptyRepositoryError, \
48 ImproperArchiveTypeError, VCSError, NodeAlreadyExistsError
48 ImproperArchiveTypeError, VCSError, NodeAlreadyExistsError,\
49 NodeDoesNotExistError
49 50 from rhodecode.lib.vcs.nodes import FileNode
50 51
51 52 from rhodecode.model.repo import RepoModel
52 53 from rhodecode.model.scm import ScmModel
53 54 from rhodecode.model.db import Repository
54 55
55 56 from rhodecode.controllers.changeset import anchor_url, _ignorews_url,\
56 57 _context_url, get_line_ctx, get_ignore_ws
57 58
58 59
59 60 log = logging.getLogger(__name__)
60 61
61 62
62 63 class FilesController(BaseRepoController):
63 64
64 65 def __before__(self):
65 66 super(FilesController, self).__before__()
66 67 c.cut_off_limit = self.cut_off_limit
67 68
68 69 def __get_cs_or_redirect(self, rev, repo_name, redirect_after=True):
69 70 """
70 71 Safe way to get changeset if error occur it redirects to tip with
71 72 proper message
72 73
73 74 :param rev: revision to fetch
74 75 :param repo_name: repo name to redirect after
75 76 """
76 77
77 78 try:
78 79 return c.rhodecode_repo.get_changeset(rev)
79 80 except EmptyRepositoryError, e:
80 81 if not redirect_after:
81 82 return None
82 83 url_ = url('files_add_home',
83 84 repo_name=c.repo_name,
84 85 revision=0, f_path='')
85 86 add_new = '<a href="%s">[%s]</a>' % (url_, _('click here to add new file'))
86 87 h.flash(h.literal(_('There are no files yet %s') % add_new),
87 88 category='warning')
88 89 redirect(h.url('summary_home', repo_name=repo_name))
89 90
90 91 except RepositoryError, e:
91 92 h.flash(str(e), category='warning')
92 93 redirect(h.url('files_home', repo_name=repo_name, revision='tip'))
93 94
94 95 def __get_filenode_or_redirect(self, repo_name, cs, path):
95 96 """
96 97 Returns file_node, if error occurs or given path is directory,
97 98 it'll redirect to top level path
98 99
99 100 :param repo_name: repo_name
100 101 :param cs: given changeset
101 102 :param path: path to lookup
102 103 """
103 104
104 105 try:
105 106 file_node = cs.get_node(path)
106 107 if file_node.is_dir():
107 108 raise RepositoryError('given path is a directory')
108 109 except RepositoryError, e:
109 110 h.flash(str(e), category='warning')
110 111 redirect(h.url('files_home', repo_name=repo_name,
111 112 revision=cs.raw_id))
112 113
113 114 return file_node
114 115
115 116 @LoginRequired()
116 117 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
117 118 'repository.admin')
118 119 def index(self, repo_name, revision, f_path, annotate=False):
119 120 # redirect to given revision from form if given
120 121 post_revision = request.POST.get('at_rev', None)
121 122 if post_revision:
122 123 cs = self.__get_cs_or_redirect(post_revision, repo_name)
123 124 redirect(url('files_home', repo_name=c.repo_name,
124 125 revision=cs.raw_id, f_path=f_path))
125 126
126 127 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
127 128 c.branch = request.GET.get('branch', None)
128 129 c.f_path = f_path
129 130 c.annotate = annotate
130 131 cur_rev = c.changeset.revision
131 132
132 133 # prev link
133 134 try:
134 135 prev_rev = c.rhodecode_repo.get_changeset(cur_rev).prev(c.branch)
135 136 c.url_prev = url('files_home', repo_name=c.repo_name,
136 137 revision=prev_rev.raw_id, f_path=f_path)
137 138 if c.branch:
138 139 c.url_prev += '?branch=%s' % c.branch
139 140 except (ChangesetDoesNotExistError, VCSError):
140 141 c.url_prev = '#'
141 142
142 143 # next link
143 144 try:
144 145 next_rev = c.rhodecode_repo.get_changeset(cur_rev).next(c.branch)
145 146 c.url_next = url('files_home', repo_name=c.repo_name,
146 147 revision=next_rev.raw_id, f_path=f_path)
147 148 if c.branch:
148 149 c.url_next += '?branch=%s' % c.branch
149 150 except (ChangesetDoesNotExistError, VCSError):
150 151 c.url_next = '#'
151 152
152 153 # files or dirs
153 154 try:
154 155 c.file = c.changeset.get_node(f_path)
155 156
156 157 if c.file.is_file():
157 158 _hist = c.rhodecode_repo.get_changeset().get_file_history(f_path)
158 159 c.file_changeset = c.changeset
159 160 if _hist:
160 161 c.file_changeset = (c.changeset
161 162 if c.changeset.revision < _hist[0].revision
162 163 else _hist[0])
163 c.file_history = self._get_node_history(None, f_path, _hist)
164 c.file_history = self._get_node_history(c.changeset, f_path,
165 _hist)
164 166 c.authors = []
165 167 for a in set([x.author for x in _hist]):
166 168 c.authors.append((h.email(a), h.person(a)))
167 169 else:
168 170 c.authors = c.file_history = []
169 171 except RepositoryError, e:
170 172 h.flash(str(e), category='warning')
171 173 redirect(h.url('files_home', repo_name=repo_name,
172 174 revision='tip'))
173 175
174 176 if request.environ.get('HTTP_X_PARTIAL_XHR'):
175 177 return render('files/files_ypjax.html')
176 178
177 179 return render('files/files.html')
178 180
179 181 @LoginRequired()
180 182 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
181 183 'repository.admin')
182 184 def rawfile(self, repo_name, revision, f_path):
183 185 cs = self.__get_cs_or_redirect(revision, repo_name)
184 186 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
185 187
186 188 response.content_disposition = 'attachment; filename=%s' % \
187 189 safe_str(f_path.split(Repository.url_sep())[-1])
188 190
189 191 response.content_type = file_node.mimetype
190 192 return file_node.content
191 193
192 194 @LoginRequired()
193 195 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
194 196 'repository.admin')
195 197 def raw(self, repo_name, revision, f_path):
196 198 cs = self.__get_cs_or_redirect(revision, repo_name)
197 199 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
198 200
199 201 raw_mimetype_mapping = {
200 202 # map original mimetype to a mimetype used for "show as raw"
201 203 # you can also provide a content-disposition to override the
202 204 # default "attachment" disposition.
203 205 # orig_type: (new_type, new_dispo)
204 206
205 207 # show images inline:
206 208 'image/x-icon': ('image/x-icon', 'inline'),
207 209 'image/png': ('image/png', 'inline'),
208 210 'image/gif': ('image/gif', 'inline'),
209 211 'image/jpeg': ('image/jpeg', 'inline'),
210 212 'image/svg+xml': ('image/svg+xml', 'inline'),
211 213 }
212 214
213 215 mimetype = file_node.mimetype
214 216 try:
215 217 mimetype, dispo = raw_mimetype_mapping[mimetype]
216 218 except KeyError:
217 219 # we don't know anything special about this, handle it safely
218 220 if file_node.is_binary:
219 221 # do same as download raw for binary files
220 222 mimetype, dispo = 'application/octet-stream', 'attachment'
221 223 else:
222 224 # do not just use the original mimetype, but force text/plain,
223 225 # otherwise it would serve text/html and that might be unsafe.
224 226 # Note: underlying vcs library fakes text/plain mimetype if the
225 227 # mimetype can not be determined and it thinks it is not
226 228 # binary.This might lead to erroneous text display in some
227 229 # cases, but helps in other cases, like with text files
228 230 # without extension.
229 231 mimetype, dispo = 'text/plain', 'inline'
230 232
231 233 if dispo == 'attachment':
232 234 dispo = 'attachment; filename=%s' % \
233 235 safe_str(f_path.split(os.sep)[-1])
234 236
235 237 response.content_disposition = dispo
236 238 response.content_type = mimetype
237 239 return file_node.content
238 240
239 241 @LoginRequired()
240 242 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
241 243 def edit(self, repo_name, revision, f_path):
242 244 repo = Repository.get_by_repo_name(repo_name)
243 245 if repo.enable_locking and repo.locked[0]:
244 246 h.flash(_('This repository is has been locked by %s on %s')
245 247 % (h.person_by_id(repo.locked[0]),
246 248 h.fmt_date(h.time_to_datetime(repo.locked[1]))),
247 249 'warning')
248 250 return redirect(h.url('files_home',
249 251 repo_name=repo_name, revision='tip'))
250 252
251 253 r_post = request.POST
252 254
253 255 c.cs = self.__get_cs_or_redirect(revision, repo_name)
254 256 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
255 257
256 258 if c.file.is_binary:
257 259 return redirect(url('files_home', repo_name=c.repo_name,
258 260 revision=c.cs.raw_id, f_path=f_path))
259 261
260 262 c.f_path = f_path
261 263
262 264 if r_post:
263 265
264 266 old_content = c.file.content
265 267 sl = old_content.splitlines(1)
266 268 first_line = sl[0] if sl else ''
267 269 # modes: 0 - Unix, 1 - Mac, 2 - DOS
268 270 mode = detect_mode(first_line, 0)
269 271 content = convert_line_endings(r_post.get('content'), mode)
270 272
271 273 message = r_post.get('message') or (_('Edited %s via RhodeCode')
272 274 % (f_path))
273 275 author = self.rhodecode_user.full_contact
274 276
275 277 if content == old_content:
276 278 h.flash(_('No changes'),
277 279 category='warning')
278 280 return redirect(url('changeset_home', repo_name=c.repo_name,
279 281 revision='tip'))
280 282
281 283 try:
282 284 self.scm_model.commit_change(repo=c.rhodecode_repo,
283 285 repo_name=repo_name, cs=c.cs,
284 286 user=self.rhodecode_user,
285 287 author=author, message=message,
286 288 content=content, f_path=f_path)
287 289 h.flash(_('Successfully committed to %s') % f_path,
288 290 category='success')
289 291
290 292 except Exception:
291 293 log.error(traceback.format_exc())
292 294 h.flash(_('Error occurred during commit'), category='error')
293 295 return redirect(url('changeset_home',
294 296 repo_name=c.repo_name, revision='tip'))
295 297
296 298 return render('files/files_edit.html')
297 299
298 300 @LoginRequired()
299 301 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
300 302 def add(self, repo_name, revision, f_path):
301 303
302 304 repo = Repository.get_by_repo_name(repo_name)
303 305 if repo.enable_locking and repo.locked[0]:
304 306 h.flash(_('This repository is has been locked by %s on %s')
305 307 % (h.person_by_id(repo.locked[0]),
306 308 h.fmt_date(h.time_to_datetime(repo.locked[1]))),
307 309 'warning')
308 310 return redirect(h.url('files_home',
309 311 repo_name=repo_name, revision='tip'))
310 312
311 313 r_post = request.POST
312 314 c.cs = self.__get_cs_or_redirect(revision, repo_name,
313 315 redirect_after=False)
314 316 if c.cs is None:
315 317 c.cs = EmptyChangeset(alias=c.rhodecode_repo.alias)
316 318
317 319 c.f_path = f_path
318 320
319 321 if r_post:
320 322 unix_mode = 0
321 323 content = convert_line_endings(r_post.get('content'), unix_mode)
322 324
323 325 message = r_post.get('message') or (_('Added %s via RhodeCode')
324 326 % (f_path))
325 327 location = r_post.get('location')
326 328 filename = r_post.get('filename')
327 329 file_obj = r_post.get('upload_file', None)
328 330
329 331 if file_obj is not None and hasattr(file_obj, 'filename'):
330 332 filename = file_obj.filename
331 333 content = file_obj.file
332 334
333 335 node_path = os.path.join(location, filename)
334 336 author = self.rhodecode_user.full_contact
335 337
336 338 if not content:
337 339 h.flash(_('No content'), category='warning')
338 340 return redirect(url('changeset_home', repo_name=c.repo_name,
339 341 revision='tip'))
340 342 if not filename:
341 343 h.flash(_('No filename'), category='warning')
342 344 return redirect(url('changeset_home', repo_name=c.repo_name,
343 345 revision='tip'))
344 346
345 347 try:
346 348 self.scm_model.create_node(repo=c.rhodecode_repo,
347 349 repo_name=repo_name, cs=c.cs,
348 350 user=self.rhodecode_user,
349 351 author=author, message=message,
350 352 content=content, f_path=node_path)
351 353 h.flash(_('Successfully committed to %s') % node_path,
352 354 category='success')
353 355 except NodeAlreadyExistsError, e:
354 356 h.flash(_(e), category='error')
355 357 except Exception:
356 358 log.error(traceback.format_exc())
357 359 h.flash(_('Error occurred during commit'), category='error')
358 360 return redirect(url('changeset_home',
359 361 repo_name=c.repo_name, revision='tip'))
360 362
361 363 return render('files/files_add.html')
362 364
363 365 @LoginRequired()
364 366 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
365 367 'repository.admin')
366 368 def archivefile(self, repo_name, fname):
367 369
368 370 fileformat = None
369 371 revision = None
370 372 ext = None
371 373 subrepos = request.GET.get('subrepos') == 'true'
372 374
373 375 for a_type, ext_data in settings.ARCHIVE_SPECS.items():
374 376 archive_spec = fname.split(ext_data[1])
375 377 if len(archive_spec) == 2 and archive_spec[1] == '':
376 378 fileformat = a_type or ext_data[1]
377 379 revision = archive_spec[0]
378 380 ext = ext_data[1]
379 381
380 382 try:
381 383 dbrepo = RepoModel().get_by_repo_name(repo_name)
382 384 if dbrepo.enable_downloads is False:
383 385 return _('downloads disabled')
384 386
385 387 if c.rhodecode_repo.alias == 'hg':
386 388 # patch and reset hooks section of UI config to not run any
387 389 # hooks on fetching archives with subrepos
388 390 for k, v in c.rhodecode_repo._repo.ui.configitems('hooks'):
389 391 c.rhodecode_repo._repo.ui.setconfig('hooks', k, None)
390 392
391 393 cs = c.rhodecode_repo.get_changeset(revision)
392 394 content_type = settings.ARCHIVE_SPECS[fileformat][0]
393 395 except ChangesetDoesNotExistError:
394 396 return _('Unknown revision %s') % revision
395 397 except EmptyRepositoryError:
396 398 return _('Empty repository')
397 399 except (ImproperArchiveTypeError, KeyError):
398 400 return _('Unknown archive type')
399 401
400 402 fd, archive = tempfile.mkstemp()
401 403 t = open(archive, 'wb')
402 404 cs.fill_archive(stream=t, kind=fileformat, subrepos=subrepos)
403 405 t.close()
404 406
405 407 def get_chunked_archive(archive):
406 408 stream = open(archive, 'rb')
407 409 while True:
408 410 data = stream.read(16 * 1024)
409 411 if not data:
410 412 stream.close()
411 413 os.close(fd)
412 414 os.remove(archive)
413 415 break
414 416 yield data
415 417
416 418 response.content_disposition = str('attachment; filename=%s-%s%s' \
417 419 % (repo_name, revision[:12], ext))
418 420 response.content_type = str(content_type)
419 421 return get_chunked_archive(archive)
420 422
421 423 @LoginRequired()
422 424 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
423 425 'repository.admin')
424 426 def diff(self, repo_name, f_path):
425 427 ignore_whitespace = request.GET.get('ignorews') == '1'
426 428 line_context = request.GET.get('context', 3)
427 429 diff1 = request.GET.get('diff1', '')
428 430 diff2 = request.GET.get('diff2', '')
429 431 c.action = request.GET.get('diff')
430 432 c.no_changes = diff1 == diff2
431 433 c.f_path = f_path
432 434 c.big_diff = False
433 435 c.anchor_url = anchor_url
434 436 c.ignorews_url = _ignorews_url
435 437 c.context_url = _context_url
436 438 c.changes = OrderedDict()
437 439 c.changes[diff2] = []
438 440
439 441 #special case if we want a show rev only, it's impl here
440 442 #to reduce JS and callbacks
441 443 if request.GET.get('show_rev'):
442 444 if str2bool(request.GET.get('annotate', 'False')):
443 445 _url = url('files_annotate_home', repo_name=c.repo_name,
444 446 revision=diff1, f_path=c.f_path)
445 447 else:
446 448 _url = url('files_home', repo_name=c.repo_name,
447 449 revision=diff1, f_path=c.f_path)
448 450
449 451 return redirect(_url)
450 452 try:
451 453 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
452 454 c.changeset_1 = c.rhodecode_repo.get_changeset(diff1)
453 455 node1 = c.changeset_1.get_node(f_path)
454 456 else:
455 457 c.changeset_1 = EmptyChangeset(repo=c.rhodecode_repo)
456 458 node1 = FileNode('.', '', changeset=c.changeset_1)
457 459
458 460 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
459 461 c.changeset_2 = c.rhodecode_repo.get_changeset(diff2)
460 462 node2 = c.changeset_2.get_node(f_path)
461 463 else:
462 464 c.changeset_2 = EmptyChangeset(repo=c.rhodecode_repo)
463 465 node2 = FileNode('.', '', changeset=c.changeset_2)
464 466 except RepositoryError:
465 467 return redirect(url('files_home', repo_name=c.repo_name,
466 468 f_path=f_path))
467 469
468 470 if c.action == 'download':
469 471 _diff = diffs.get_gitdiff(node1, node2,
470 472 ignore_whitespace=ignore_whitespace,
471 473 context=line_context)
472 474 diff = diffs.DiffProcessor(_diff, format='gitdiff')
473 475
474 476 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
475 477 response.content_type = 'text/plain'
476 478 response.content_disposition = (
477 479 'attachment; filename=%s' % diff_name
478 480 )
479 481 return diff.raw_diff()
480 482
481 483 elif c.action == 'raw':
482 484 _diff = diffs.get_gitdiff(node1, node2,
483 485 ignore_whitespace=ignore_whitespace,
484 486 context=line_context)
485 487 diff = diffs.DiffProcessor(_diff, format='gitdiff')
486 488 response.content_type = 'text/plain'
487 489 return diff.raw_diff()
488 490
489 491 else:
490 492 fid = h.FID(diff2, node2.path)
491 493 line_context_lcl = get_line_ctx(fid, request.GET)
492 494 ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
493 495
494 496 lim = request.GET.get('fulldiff') or self.cut_off_limit
495 497 _, cs1, cs2, diff, st = diffs.wrapped_diff(filenode_old=node1,
496 498 filenode_new=node2,
497 499 cut_off_limit=lim,
498 500 ignore_whitespace=ign_whitespace_lcl,
499 501 line_context=line_context_lcl,
500 502 enable_comments=False)
501 503
502 504 c.changes = [('', node2, diff, cs1, cs2, st,)]
503 505
504 506 return render('files/file_diff.html')
505 507
506 508 def _get_node_history(self, cs, f_path, changesets=None):
507 if cs is None:
508 # if we pass empty CS calculate history based on tip
509 cs = c.rhodecode_repo.get_changeset()
509 """
510 get changesets history for given node
511
512 :param cs: changeset to calculate history
513 :param f_path: path for node to calculate history for
514 :param changesets: if passed don't calculate history and take
515 changesets defined in this list
516 """
517 # calculate history based on tip
518 tip_cs = c.rhodecode_repo.get_changeset()
510 519 if changesets is None:
511 changesets = cs.get_file_history(f_path)
520 try:
521 changesets = tip_cs.get_file_history(f_path)
522 except NodeDoesNotExistError:
523 #this node is not present at tip !
524 changesets = cs.get_file_history(f_path)
512 525
513 526 hist_l = []
514 527
515 528 changesets_group = ([], _("Changesets"))
516 529 branches_group = ([], _("Branches"))
517 530 tags_group = ([], _("Tags"))
518 531 _hg = cs.repository.alias == 'hg'
519 532 for chs in changesets:
520 533 _branch = '(%s)' % chs.branch if _hg else ''
521 534 n_desc = 'r%s:%s %s' % (chs.revision, chs.short_id, _branch)
522 535 changesets_group[0].append((chs.raw_id, n_desc,))
523 536
524 537 hist_l.append(changesets_group)
525 538
526 539 for name, chs in c.rhodecode_repo.branches.items():
527 540 branches_group[0].append((chs, name),)
528 541 hist_l.append(branches_group)
529 542
530 543 for name, chs in c.rhodecode_repo.tags.items():
531 544 tags_group[0].append((chs, name),)
532 545 hist_l.append(tags_group)
533 546
534 547 return hist_l
535 548
536 549 @LoginRequired()
537 550 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
538 551 'repository.admin')
539 552 @jsonify
540 553 def nodelist(self, repo_name, revision, f_path):
541 554 if request.environ.get('HTTP_X_PARTIAL_XHR'):
542 555 cs = self.__get_cs_or_redirect(revision, repo_name)
543 556 _d, _f = ScmModel().get_nodes(repo_name, cs.raw_id, f_path,
544 557 flat=False)
545 558 return {'nodes': _d + _f}
@@ -1,144 +1,144 b''
1 1 <%inherit file="/base/base.html"/>
2 2
3 3 <%def name="title()">
4 4 ${_('%s files') % c.repo_name} - ${c.rhodecode_name}
5 5 </%def>
6 6
7 7 <%def name="breadcrumbs_links()">
8 8 ${h.link_to(_(u'Home'),h.url('/'))}
9 9 &raquo;
10 10 ${h.link_to(c.repo_name,h.url('files_home',repo_name=c.repo_name))}
11 11 &raquo;
12 12 ${_('files')}
13 13 %if c.file:
14 14 @ r${c.changeset.revision}:${h.short_id(c.changeset.raw_id)}
15 15 %endif
16 16 </%def>
17 17
18 18 <%def name="page_nav()">
19 19 ${self.menu('files')}
20 20 </%def>
21 21
22 22 <%def name="main()">
23 23 <div class="box">
24 24 <!-- box / title -->
25 25 <div class="title">
26 26 ${self.breadcrumbs()}
27 27 <ul class="links">
28 28 <li>
29 29 <span style="text-transform: uppercase;"><a href="#">${_('branch')}: ${c.changeset.branch}</a></span>
30 30 </li>
31 31 </ul>
32 32 </div>
33 33 <div class="table">
34 34 <div id="files_data">
35 35 <%include file='files_ypjax.html'/>
36 36 </div>
37 37 </div>
38 38 </div>
39 39
40 40 <script type="text/javascript">
41 41 var CACHE = {};
42 42 var CACHE_EXPIRE = 60*1000; //cache for 60s
43 43 //used to construct links from the search list
44 44 var url_base = '${h.url("files_home",repo_name=c.repo_name,revision='__REV__',f_path='__FPATH__')}';
45 45 //send the nodelist request to this url
46 46 var node_list_url = '${h.url("files_nodelist_home",repo_name=c.repo_name,revision='__REV__',f_path='__FPATH__')}';
47 47
48 48 var ypjax_links = function(){
49 49 YUE.on(YUQ('.ypjax-link'), 'click',function(e){
50 50
51 51 //don't do ypjax on middle click
52 52 if(e.which == 2 || !History.enabled){
53 53 return true;
54 54 }
55 55
56 56 var el = e.currentTarget;
57 57 var url = el.href;
58 58
59 59 var _base_url = '${h.url("files_home",repo_name=c.repo_name,revision='',f_path='')}';
60 60 _base_url = _base_url.replace('//','/')
61 61
62 62 //extract rev and the f_path from url.
63 63 parts = url.split(_base_url)
64 64 if(parts.length != 2){
65 65 return false;
66 66 }
67 67
68 68 var parts2 = parts[1].split('/');
69 69 var rev = parts2.shift(); // pop the first element which is the revision
70 70 var f_path = parts2.join('/');
71 71
72 72 var title = "${_('%s files') % c.repo_name}" + " - " + f_path;
73 73
74 74 var _node_list_url = node_list_url.replace('__REV__',rev).replace('__FPATH__', f_path);
75 75 var _url_base = url_base.replace('__REV__',rev);
76 76
77 77 // Change our States and save some data for handling events
78 78 var data = {url:url,title:title, url_base:_url_base,
79 79 node_list_url:_node_list_url};
80 80 History.pushState(data, title, url);
81 81
82 82 //now we're sure that we can do ypjax things
83 YUE.preventDefault(e)
83 YUE.preventDefault(e);
84 84 return false;
85 85 });
86 86 }
87 87
88 88 var callbacks = function(State){
89 89 ypjax_links();
90 90 tooltip_activate();
91 91 fileBrowserListeners(State.url, State.data.node_list_url, State.data.url_base);
92 92 YUE.on('hlcode','mouseup',getSelectionLink("${_('Selection link')}"));
93 93
94 94 // Inform Google Analytics of the change
95 95 if ( typeof window.pageTracker !== 'undefined' ) {
96 96 window.pageTracker._trackPageview(State.url);
97 97 }
98 98 }
99 99
100 100 YUE.onDOMReady(function(){
101 101 ypjax_links();
102 102 var container = 'files_data';
103 103 //Bind to StateChange Event
104 104 History.Adapter.bind(window,'statechange',function(){
105 105 var State = History.getState();
106 106 cache_key = State.url;
107 107 //check if we have this request in cache maybe ?
108 108 var _cache_obj = CACHE[cache_key];
109 109 var _cur_time = new Date().getTime();
110 110 // get from cache if it's there and not yet expired !
111 111 if(_cache_obj !== undefined && _cache_obj[0] > _cur_time){
112 112 YUD.get(container).innerHTML=_cache_obj[1];
113 113 YUD.setStyle(container,'opacity','1.0');
114 114
115 115 //callbacks after ypjax call
116 116 callbacks(State);
117 117 }
118 118 else{
119 119 ypjax(State.url,container,function(o){
120 120 //callbacks after ypjax call
121 121 callbacks(State);
122 122 if (o !== undefined){
123 123 //store our request in cache
124 124 var _expire_on = new Date().getTime()+CACHE_EXPIRE;
125 125 CACHE[cache_key] = [_expire_on, o.responseText];
126 126 }
127 127 });
128 128 }
129 129 });
130 130
131 131 // init the search filter
132 132 var _State = {
133 133 url: "${h.url.current()}",
134 134 data: {
135 135 node_list_url: node_list_url.replace('__REV__',"${c.changeset.raw_id}").replace('__FPATH__', "${h.safe_unicode(c.file.path)}"),
136 136 url_base: url_base.replace('__REV__',"${c.changeset.raw_id}")
137 137 }
138 138 }
139 139 fileBrowserListeners(_State.url, _State.data.node_list_url, _State.data.url_base);
140 140 });
141 141
142 142 </script>
143 143
144 144 </%def>
General Comments 0
You need to be logged in to leave comments. Login now