##// END OF EJS Templates
fixes issue #455 Creating an archive generates an exception on Windows...
marcink -
r2318:058e2743 beta
parent child Browse files
Show More
@@ -1,490 +1,490 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 from paste.fileapp import FileApp, _FileIter
36 36
37 37 from rhodecode.lib import diffs
38 38 from rhodecode.lib import helpers as h
39 39
40 40 from rhodecode.lib.compat import OrderedDict
41 41 from rhodecode.lib.utils2 import convert_line_endings, detect_mode, safe_str
42 42 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
43 43 from rhodecode.lib.base import BaseRepoController, render
44 44 from rhodecode.lib.utils 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 48 ImproperArchiveTypeError, VCSError, NodeAlreadyExistsError
49 49 from rhodecode.lib.vcs.nodes import FileNode
50 50
51 51 from rhodecode.model.repo import RepoModel
52 52 from rhodecode.model.scm import ScmModel
53 53 from rhodecode.model.db import Repository
54 54
55 55 from rhodecode.controllers.changeset import anchor_url, _ignorews_url,\
56 56 _context_url, get_line_ctx, get_ignore_ws
57 57
58 58
59 59 log = logging.getLogger(__name__)
60 60
61 61
62 62 class FilesController(BaseRepoController):
63 63
64 64 @LoginRequired()
65 65 def __before__(self):
66 66 super(FilesController, self).__before__()
67 67 c.cut_off_limit = self.cut_off_limit
68 68
69 69 def __get_cs_or_redirect(self, rev, repo_name, redirect_after=True):
70 70 """
71 71 Safe way to get changeset if error occur it redirects to tip with
72 72 proper message
73 73
74 74 :param rev: revision to fetch
75 75 :param repo_name: repo name to redirect after
76 76 """
77 77
78 78 try:
79 79 return c.rhodecode_repo.get_changeset(rev)
80 80 except EmptyRepositoryError, e:
81 81 if not redirect_after:
82 82 return None
83 83 url_ = url('files_add_home',
84 84 repo_name=c.repo_name,
85 85 revision=0, f_path='')
86 86 add_new = '<a href="%s">[%s]</a>' % (url_, _('add new'))
87 87 h.flash(h.literal(_('There are no files yet %s' % add_new)),
88 88 category='warning')
89 89 redirect(h.url('summary_home', repo_name=repo_name))
90 90
91 91 except RepositoryError, e:
92 92 h.flash(str(e), category='warning')
93 93 redirect(h.url('files_home', repo_name=repo_name, revision='tip'))
94 94
95 95 def __get_filenode_or_redirect(self, repo_name, cs, path):
96 96 """
97 97 Returns file_node, if error occurs or given path is directory,
98 98 it'll redirect to top level path
99 99
100 100 :param repo_name: repo_name
101 101 :param cs: given changeset
102 102 :param path: path to lookup
103 103 """
104 104
105 105 try:
106 106 file_node = cs.get_node(path)
107 107 if file_node.is_dir():
108 108 raise RepositoryError('given path is a directory')
109 109 except RepositoryError, e:
110 110 h.flash(str(e), category='warning')
111 111 redirect(h.url('files_home', repo_name=repo_name,
112 112 revision=cs.raw_id))
113 113
114 114 return file_node
115 115
116 116 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
117 117 'repository.admin')
118 118 def index(self, repo_name, revision, f_path, annotate=False):
119 119 # redirect to given revision from form if given
120 120 post_revision = request.POST.get('at_rev', None)
121 121 if post_revision:
122 122 cs = self.__get_cs_or_redirect(post_revision, repo_name)
123 123 redirect(url('files_home', repo_name=c.repo_name,
124 124 revision=cs.raw_id, f_path=f_path))
125 125
126 126 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
127 127 c.branch = request.GET.get('branch', None)
128 128 c.f_path = f_path
129 129 c.annotate = annotate
130 130 cur_rev = c.changeset.revision
131 131
132 132 # prev link
133 133 try:
134 134 prev_rev = c.rhodecode_repo.get_changeset(cur_rev).prev(c.branch)
135 135 c.url_prev = url('files_home', repo_name=c.repo_name,
136 136 revision=prev_rev.raw_id, f_path=f_path)
137 137 if c.branch:
138 138 c.url_prev += '?branch=%s' % c.branch
139 139 except (ChangesetDoesNotExistError, VCSError):
140 140 c.url_prev = '#'
141 141
142 142 # next link
143 143 try:
144 144 next_rev = c.rhodecode_repo.get_changeset(cur_rev).next(c.branch)
145 145 c.url_next = url('files_home', repo_name=c.repo_name,
146 146 revision=next_rev.raw_id, f_path=f_path)
147 147 if c.branch:
148 148 c.url_next += '?branch=%s' % c.branch
149 149 except (ChangesetDoesNotExistError, VCSError):
150 150 c.url_next = '#'
151 151
152 152 # files or dirs
153 153 try:
154 154 c.file = c.changeset.get_node(f_path)
155 155
156 156 if c.file.is_file():
157 157 c.file_history = self._get_node_history(c.changeset, f_path)
158 158 else:
159 159 c.file_history = []
160 160 except RepositoryError, e:
161 161 h.flash(str(e), category='warning')
162 162 redirect(h.url('files_home', repo_name=repo_name,
163 163 revision=revision))
164 164
165 165 return render('files/files.html')
166 166
167 167 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
168 168 'repository.admin')
169 169 def rawfile(self, repo_name, revision, f_path):
170 170 cs = self.__get_cs_or_redirect(revision, repo_name)
171 171 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
172 172
173 173 response.content_disposition = 'attachment; filename=%s' % \
174 174 safe_str(f_path.split(Repository.url_sep())[-1])
175 175
176 176 response.content_type = file_node.mimetype
177 177 return file_node.content
178 178
179 179 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
180 180 'repository.admin')
181 181 def raw(self, repo_name, revision, f_path):
182 182 cs = self.__get_cs_or_redirect(revision, repo_name)
183 183 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
184 184
185 185 raw_mimetype_mapping = {
186 186 # map original mimetype to a mimetype used for "show as raw"
187 187 # you can also provide a content-disposition to override the
188 188 # default "attachment" disposition.
189 189 # orig_type: (new_type, new_dispo)
190 190
191 191 # show images inline:
192 192 'image/x-icon': ('image/x-icon', 'inline'),
193 193 'image/png': ('image/png', 'inline'),
194 194 'image/gif': ('image/gif', 'inline'),
195 195 'image/jpeg': ('image/jpeg', 'inline'),
196 196 'image/svg+xml': ('image/svg+xml', 'inline'),
197 197 }
198 198
199 199 mimetype = file_node.mimetype
200 200 try:
201 201 mimetype, dispo = raw_mimetype_mapping[mimetype]
202 202 except KeyError:
203 203 # we don't know anything special about this, handle it safely
204 204 if file_node.is_binary:
205 205 # do same as download raw for binary files
206 206 mimetype, dispo = 'application/octet-stream', 'attachment'
207 207 else:
208 208 # do not just use the original mimetype, but force text/plain,
209 209 # otherwise it would serve text/html and that might be unsafe.
210 210 # Note: underlying vcs library fakes text/plain mimetype if the
211 211 # mimetype can not be determined and it thinks it is not
212 212 # binary.This might lead to erroneous text display in some
213 213 # cases, but helps in other cases, like with text files
214 214 # without extension.
215 215 mimetype, dispo = 'text/plain', 'inline'
216 216
217 217 if dispo == 'attachment':
218 218 dispo = 'attachment; filename=%s' % \
219 219 safe_str(f_path.split(os.sep)[-1])
220 220
221 221 response.content_disposition = dispo
222 222 response.content_type = mimetype
223 223 return file_node.content
224 224
225 225 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
226 226 def edit(self, repo_name, revision, f_path):
227 227 r_post = request.POST
228 228
229 229 c.cs = self.__get_cs_or_redirect(revision, repo_name)
230 230 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
231 231
232 232 if c.file.is_binary:
233 233 return redirect(url('files_home', repo_name=c.repo_name,
234 234 revision=c.cs.raw_id, f_path=f_path))
235 235
236 236 c.f_path = f_path
237 237
238 238 if r_post:
239 239
240 240 old_content = c.file.content
241 241 sl = old_content.splitlines(1)
242 242 first_line = sl[0] if sl else ''
243 243 # modes: 0 - Unix, 1 - Mac, 2 - DOS
244 244 mode = detect_mode(first_line, 0)
245 245 content = convert_line_endings(r_post.get('content'), mode)
246 246
247 247 message = r_post.get('message') or (_('Edited %s via RhodeCode')
248 248 % (f_path))
249 249 author = self.rhodecode_user.full_contact
250 250
251 251 if content == old_content:
252 252 h.flash(_('No changes'),
253 253 category='warning')
254 254 return redirect(url('changeset_home', repo_name=c.repo_name,
255 255 revision='tip'))
256 256
257 257 try:
258 258 self.scm_model.commit_change(repo=c.rhodecode_repo,
259 259 repo_name=repo_name, cs=c.cs,
260 260 user=self.rhodecode_user,
261 261 author=author, message=message,
262 262 content=content, f_path=f_path)
263 263 h.flash(_('Successfully committed to %s' % f_path),
264 264 category='success')
265 265
266 266 except Exception:
267 267 log.error(traceback.format_exc())
268 268 h.flash(_('Error occurred during commit'), category='error')
269 269 return redirect(url('changeset_home',
270 270 repo_name=c.repo_name, revision='tip'))
271 271
272 272 return render('files/files_edit.html')
273 273
274 274 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
275 275 def add(self, repo_name, revision, f_path):
276 276 r_post = request.POST
277 277 c.cs = self.__get_cs_or_redirect(revision, repo_name,
278 278 redirect_after=False)
279 279 if c.cs is None:
280 280 c.cs = EmptyChangeset(alias=c.rhodecode_repo.alias)
281 281
282 282 c.f_path = f_path
283 283
284 284 if r_post:
285 285 unix_mode = 0
286 286 content = convert_line_endings(r_post.get('content'), unix_mode)
287 287
288 288 message = r_post.get('message') or (_('Added %s via RhodeCode')
289 289 % (f_path))
290 290 location = r_post.get('location')
291 291 filename = r_post.get('filename')
292 292 file_obj = r_post.get('upload_file', None)
293 293
294 294 if file_obj is not None and hasattr(file_obj, 'filename'):
295 295 filename = file_obj.filename
296 296 content = file_obj.file
297 297
298 298 node_path = os.path.join(location, filename)
299 299 author = self.rhodecode_user.full_contact
300 300
301 301 if not content:
302 302 h.flash(_('No content'), category='warning')
303 303 return redirect(url('changeset_home', repo_name=c.repo_name,
304 304 revision='tip'))
305 305 if not filename:
306 306 h.flash(_('No filename'), category='warning')
307 307 return redirect(url('changeset_home', repo_name=c.repo_name,
308 308 revision='tip'))
309 309
310 310 try:
311 311 self.scm_model.create_node(repo=c.rhodecode_repo,
312 312 repo_name=repo_name, cs=c.cs,
313 313 user=self.rhodecode_user,
314 314 author=author, message=message,
315 315 content=content, f_path=node_path)
316 316 h.flash(_('Successfully committed to %s' % node_path),
317 317 category='success')
318 318 except NodeAlreadyExistsError, e:
319 319 h.flash(_(e), category='error')
320 320 except Exception:
321 321 log.error(traceback.format_exc())
322 322 h.flash(_('Error occurred during commit'), category='error')
323 323 return redirect(url('changeset_home',
324 324 repo_name=c.repo_name, revision='tip'))
325 325
326 326 return render('files/files_add.html')
327 327
328 328 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
329 329 'repository.admin')
330 330 def archivefile(self, repo_name, fname):
331 331
332 332 fileformat = None
333 333 revision = None
334 334 ext = None
335 335 subrepos = request.GET.get('subrepos') == 'true'
336 336
337 337 for a_type, ext_data in settings.ARCHIVE_SPECS.items():
338 338 archive_spec = fname.split(ext_data[1])
339 339 if len(archive_spec) == 2 and archive_spec[1] == '':
340 340 fileformat = a_type or ext_data[1]
341 341 revision = archive_spec[0]
342 342 ext = ext_data[1]
343 343
344 344 try:
345 345 dbrepo = RepoModel().get_by_repo_name(repo_name)
346 346 if dbrepo.enable_downloads is False:
347 347 return _('downloads disabled')
348 348
349 349 if c.rhodecode_repo.alias == 'hg':
350 350 # patch and reset hooks section of UI config to not run any
351 351 # hooks on fetching archives with subrepos
352 352 for k, v in c.rhodecode_repo._repo.ui.configitems('hooks'):
353 353 c.rhodecode_repo._repo.ui.setconfig('hooks', k, None)
354 354
355 355 cs = c.rhodecode_repo.get_changeset(revision)
356 356 content_type = settings.ARCHIVE_SPECS[fileformat][0]
357 357 except ChangesetDoesNotExistError:
358 358 return _('Unknown revision %s') % revision
359 359 except EmptyRepositoryError:
360 360 return _('Empty repository')
361 361 except (ImproperArchiveTypeError, KeyError):
362 362 return _('Unknown archive type')
363 363
364 fd, _archive_name = tempfile.mkstemp(suffix='rcarchive')
365 with open(_archive_name, 'wb') as f:
366 cs.fill_archive(stream=f, kind=fileformat, subrepos=subrepos)
367
368 content_disposition = 'attachment; filename=%s-%s%s' \
369 % (repo_name, revision[:12], ext)
370 content_length = os.path.getsize(_archive_name)
364 fd, archive = tempfile.mkstemp()
365 t = open(archive, 'wb')
366 cs.fill_archive(stream=t, kind=fileformat, subrepos=subrepos)
367 t.close()
371 368
372 headers = [('Content-Disposition', str(content_disposition)),
373 ('Content-Type', str(content_type)),
374 ('Content-Length', str(content_length))]
369 def get_chunked_archive(archive):
370 stream = open(archive, 'rb')
371 while True:
372 data = stream.read(16 * 1024)
373 if not data:
374 stream.close()
375 os.close(fd)
376 os.remove(archive)
377 break
378 yield data
375 379
376 class _DestroyingFileWrapper(_FileIter):
377 def close(self):
378 self.file.close
379 os.remove(self.file.name)
380
381 request.environ['wsgi.file_wrapper'] = _DestroyingFileWrapper
382 fapp = FileApp(_archive_name, headers=headers)
383 return fapp(request.environ, self.start_response)
380 response.content_disposition = str('attachment; filename=%s-%s%s' \
381 % (repo_name, revision[:12], ext))
382 response.content_type = str(content_type)
383 return get_chunked_archive(archive)
384 384
385 385 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
386 386 'repository.admin')
387 387 def diff(self, repo_name, f_path):
388 388 ignore_whitespace = request.GET.get('ignorews') == '1'
389 389 line_context = request.GET.get('context', 3)
390 390 diff1 = request.GET.get('diff1', '')
391 391 diff2 = request.GET.get('diff2', '')
392 392 c.action = request.GET.get('diff')
393 393 c.no_changes = diff1 == diff2
394 394 c.f_path = f_path
395 395 c.big_diff = False
396 396 c.anchor_url = anchor_url
397 397 c.ignorews_url = _ignorews_url
398 398 c.context_url = _context_url
399 399 c.changes = OrderedDict()
400 400 c.changes[diff2] = []
401 401 try:
402 402 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
403 403 c.changeset_1 = c.rhodecode_repo.get_changeset(diff1)
404 404 node1 = c.changeset_1.get_node(f_path)
405 405 else:
406 406 c.changeset_1 = EmptyChangeset(repo=c.rhodecode_repo)
407 407 node1 = FileNode('.', '', changeset=c.changeset_1)
408 408
409 409 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
410 410 c.changeset_2 = c.rhodecode_repo.get_changeset(diff2)
411 411 node2 = c.changeset_2.get_node(f_path)
412 412 else:
413 413 c.changeset_2 = EmptyChangeset(repo=c.rhodecode_repo)
414 414 node2 = FileNode('.', '', changeset=c.changeset_2)
415 415 except RepositoryError:
416 416 return redirect(url('files_home', repo_name=c.repo_name,
417 417 f_path=f_path))
418 418
419 419 if c.action == 'download':
420 420 _diff = diffs.get_gitdiff(node1, node2,
421 421 ignore_whitespace=ignore_whitespace,
422 422 context=line_context)
423 423 diff = diffs.DiffProcessor(_diff, format='gitdiff')
424 424
425 425 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
426 426 response.content_type = 'text/plain'
427 427 response.content_disposition = (
428 428 'attachment; filename=%s' % diff_name
429 429 )
430 430 return diff.raw_diff()
431 431
432 432 elif c.action == 'raw':
433 433 _diff = diffs.get_gitdiff(node1, node2,
434 434 ignore_whitespace=ignore_whitespace,
435 435 context=line_context)
436 436 diff = diffs.DiffProcessor(_diff, format='gitdiff')
437 437 response.content_type = 'text/plain'
438 438 return diff.raw_diff()
439 439
440 440 else:
441 441 fid = h.FID(diff2, node2.path)
442 442 line_context_lcl = get_line_ctx(fid, request.GET)
443 443 ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
444 444
445 445 lim = request.GET.get('fulldiff') or self.cut_off_limit
446 446 _, cs1, cs2, diff, st = diffs.wrapped_diff(filenode_old=node1,
447 447 filenode_new=node2,
448 448 cut_off_limit=lim,
449 449 ignore_whitespace=ign_whitespace_lcl,
450 450 line_context=line_context_lcl,
451 451 enable_comments=False)
452 452
453 453 c.changes = [('', node2, diff, cs1, cs2, st,)]
454 454
455 455 return render('files/file_diff.html')
456 456
457 457 def _get_node_history(self, cs, f_path):
458 458 changesets = cs.get_file_history(f_path)
459 459 hist_l = []
460 460
461 461 changesets_group = ([], _("Changesets"))
462 462 branches_group = ([], _("Branches"))
463 463 tags_group = ([], _("Tags"))
464 464 _hg = cs.repository.alias == 'hg'
465 465 for chs in changesets:
466 466 _branch = '(%s)' % chs.branch if _hg else ''
467 467 n_desc = 'r%s:%s %s' % (chs.revision, chs.short_id, _branch)
468 468 changesets_group[0].append((chs.raw_id, n_desc,))
469 469
470 470 hist_l.append(changesets_group)
471 471
472 472 for name, chs in c.rhodecode_repo.branches.items():
473 473 branches_group[0].append((chs, name),)
474 474 hist_l.append(branches_group)
475 475
476 476 for name, chs in c.rhodecode_repo.tags.items():
477 477 tags_group[0].append((chs, name),)
478 478 hist_l.append(tags_group)
479 479
480 480 return hist_l
481 481
482 482 @jsonify
483 483 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
484 484 'repository.admin')
485 485 def nodelist(self, repo_name, revision, f_path):
486 486 if request.environ.get('HTTP_X_PARTIAL_XHR'):
487 487 cs = self.__get_cs_or_redirect(revision, repo_name)
488 488 _d, _f = ScmModel().get_nodes(repo_name, cs.raw_id, f_path,
489 489 flat=False)
490 490 return _d + _f
@@ -1,317 +1,320 b''
1 1 from rhodecode.tests import *
2 2
3 3 ARCHIVE_SPECS = {
4 4 '.tar.bz2': ('application/x-bzip2', 'tbz2', ''),
5 5 '.tar.gz': ('application/x-gzip', 'tgz', ''),
6 6 '.zip': ('application/zip', 'zip', ''),
7 7 }
8 8
9 9
10 10 class TestFilesController(TestController):
11 11
12 12 def test_index(self):
13 13 self.log_user()
14 14 response = self.app.get(url(controller='files', action='index',
15 15 repo_name=HG_REPO,
16 16 revision='tip',
17 17 f_path='/'))
18 18 # Test response...
19 19 response.mustcontain('<a class="browser-dir ypjax-link" href="/vcs_test_hg/files/27cd5cce30c96924232dffcd24178a07ffeb5dfc/docs">docs</a>')
20 20 response.mustcontain('<a class="browser-dir ypjax-link" href="/vcs_test_hg/files/27cd5cce30c96924232dffcd24178a07ffeb5dfc/tests">tests</a>')
21 21 response.mustcontain('<a class="browser-dir ypjax-link" href="/vcs_test_hg/files/27cd5cce30c96924232dffcd24178a07ffeb5dfc/vcs">vcs</a>')
22 22 response.mustcontain('<a class="browser-file ypjax-link" href="/vcs_test_hg/files/27cd5cce30c96924232dffcd24178a07ffeb5dfc/.hgignore">.hgignore</a>')
23 23 response.mustcontain('<a class="browser-file ypjax-link" href="/vcs_test_hg/files/27cd5cce30c96924232dffcd24178a07ffeb5dfc/MANIFEST.in">MANIFEST.in</a>')
24 24
25 25 def test_index_revision(self):
26 26 self.log_user()
27 27
28 28 response = self.app.get(
29 29 url(controller='files', action='index',
30 30 repo_name=HG_REPO,
31 31 revision='7ba66bec8d6dbba14a2155be32408c435c5f4492',
32 32 f_path='/')
33 33 )
34 34
35 35 #Test response...
36 36
37 37 response.mustcontain('<a class="browser-dir ypjax-link" href="/vcs_test_hg/files/7ba66bec8d6dbba14a2155be32408c435c5f4492/docs">docs</a>')
38 38 response.mustcontain('<a class="browser-dir ypjax-link" href="/vcs_test_hg/files/7ba66bec8d6dbba14a2155be32408c435c5f4492/tests">tests</a>')
39 39 response.mustcontain('<a class="browser-file ypjax-link" href="/vcs_test_hg/files/7ba66bec8d6dbba14a2155be32408c435c5f4492/README.rst">README.rst</a>')
40 40 response.mustcontain('1.1 KiB')
41 41 response.mustcontain('text/x-python')
42 42
43 43 def test_index_different_branch(self):
44 44 self.log_user()
45 45
46 46 response = self.app.get(url(controller='files', action='index',
47 47 repo_name=HG_REPO,
48 48 revision='97e8b885c04894463c51898e14387d80c30ed1ee',
49 49 f_path='/'))
50 50
51 51 response.mustcontain("""<span style="text-transform: uppercase;"><a href="#">branch: git</a></span>""")
52 52
53 53 def test_index_paging(self):
54 54 self.log_user()
55 55
56 56 for r in [(73, 'a066b25d5df7016b45a41b7e2a78c33b57adc235'),
57 57 (92, 'cc66b61b8455b264a7a8a2d8ddc80fcfc58c221e'),
58 58 (109, '75feb4c33e81186c87eac740cee2447330288412'),
59 59 (1, '3d8f361e72ab303da48d799ff1ac40d5ac37c67e'),
60 60 (0, 'b986218ba1c9b0d6a259fac9b050b1724ed8e545')]:
61 61
62 62 response = self.app.get(url(controller='files', action='index',
63 63 repo_name=HG_REPO,
64 64 revision=r[1],
65 65 f_path='/'))
66 66
67 67 response.mustcontain("""@ r%s:%s""" % (r[0], r[1][:12]))
68 68
69 69 def test_file_source(self):
70 70 self.log_user()
71 71 response = self.app.get(url(controller='files', action='index',
72 72 repo_name=HG_REPO,
73 73 revision='27cd5cce30c96924232dffcd24178a07ffeb5dfc',
74 74 f_path='vcs/nodes.py'))
75 75
76 76 #test or history
77 77 response.mustcontain("""<optgroup label="Changesets">
78 78 <option value="8911406ad776fdd3d0b9932a2e89677e57405a48">r167:8911406ad776 (default)</option>
79 79 <option value="aa957ed78c35a1541f508d2ec90e501b0a9e3167">r165:aa957ed78c35 (default)</option>
80 80 <option value="48e11b73e94c0db33e736eaeea692f990cb0b5f1">r140:48e11b73e94c (default)</option>
81 81 <option value="adf3cbf483298563b968a6c673cd5bde5f7d5eea">r126:adf3cbf48329 (default)</option>
82 82 <option value="6249fd0fb2cfb1411e764129f598e2cf0de79a6f">r113:6249fd0fb2cf (git)</option>
83 83 <option value="75feb4c33e81186c87eac740cee2447330288412">r109:75feb4c33e81 (default)</option>
84 84 <option value="9a4dc232ecdc763ef2e98ae2238cfcbba4f6ad8d">r108:9a4dc232ecdc (default)</option>
85 85 <option value="595cce4efa21fda2f2e4eeb4fe5f2a6befe6fa2d">r107:595cce4efa21 (default)</option>
86 86 <option value="4a8bd421fbc2dfbfb70d85a3fe064075ab2c49da">r104:4a8bd421fbc2 (default)</option>
87 87 <option value="57be63fc8f85e65a0106a53187f7316f8c487ffa">r102:57be63fc8f85 (default)</option>
88 88 <option value="5530bd87f7e2e124a64d07cb2654c997682128be">r101:5530bd87f7e2 (git)</option>
89 89 <option value="e516008b1c93f142263dc4b7961787cbad654ce1">r99:e516008b1c93 (default)</option>
90 90 <option value="41f43fc74b8b285984554532eb105ac3be5c434f">r93:41f43fc74b8b (default)</option>
91 91 <option value="cc66b61b8455b264a7a8a2d8ddc80fcfc58c221e">r92:cc66b61b8455 (default)</option>
92 92 <option value="73ab5b616b3271b0518682fb4988ce421de8099f">r91:73ab5b616b32 (default)</option>
93 93 <option value="e0da75f308c0f18f98e9ce6257626009fdda2b39">r82:e0da75f308c0 (default)</option>
94 94 <option value="fb2e41e0f0810be4d7103bc2a4c7be16ee3ec611">r81:fb2e41e0f081 (default)</option>
95 95 <option value="602ae2f5e7ade70b3b66a58cdd9e3e613dc8a028">r76:602ae2f5e7ad (default)</option>
96 96 <option value="a066b25d5df7016b45a41b7e2a78c33b57adc235">r73:a066b25d5df7 (default)</option>
97 97 <option value="637a933c905958ce5151f154147c25c1c7b68832">r61:637a933c9059 (web)</option>
98 98 <option value="0c21004effeb8ce2d2d5b4a8baf6afa8394b6fbc">r60:0c21004effeb (web)</option>
99 99 <option value="a1f39c56d3f1d52d5fb5920370a2a2716cd9a444">r59:a1f39c56d3f1 (web)</option>
100 100 <option value="97d32df05c715a3bbf936bf3cc4e32fb77fe1a7f">r58:97d32df05c71 (web)</option>
101 101 <option value="08eaf14517718dccea4b67755a93368341aca919">r57:08eaf1451771 (web)</option>
102 102 <option value="22f71ad265265a53238359c883aa976e725aa07d">r56:22f71ad26526 (web)</option>
103 103 <option value="97501f02b7b4330924b647755663a2d90a5e638d">r49:97501f02b7b4 (web)</option>
104 104 <option value="86ede6754f2b27309452bb11f997386ae01d0e5a">r47:86ede6754f2b (web)</option>
105 105 <option value="014c40c0203c423dc19ecf94644f7cac9d4cdce0">r45:014c40c0203c (web)</option>
106 106 <option value="ee87846a61c12153b51543bf860e1026c6d3dcba">r30:ee87846a61c1 (default)</option>
107 107 <option value="9bb326a04ae5d98d437dece54be04f830cf1edd9">r26:9bb326a04ae5 (default)</option>
108 108 <option value="536c1a19428381cfea92ac44985304f6a8049569">r24:536c1a194283 (default)</option>
109 109 <option value="dc5d2c0661b61928834a785d3e64a3f80d3aad9c">r8:dc5d2c0661b6 (default)</option>
110 110 <option value="3803844fdbd3b711175fc3da9bdacfcd6d29a6fb">r7:3803844fdbd3 (default)</option>
111 111 </optgroup>
112 112 <optgroup label="Branches">
113 113 <option selected="selected" value="27cd5cce30c96924232dffcd24178a07ffeb5dfc">default</option>
114 114 <option value="97e8b885c04894463c51898e14387d80c30ed1ee">git</option>
115 115 <option value="2e6a2bf9356ca56df08807f4ad86d480da72a8f4">web</option>
116 116 </optgroup>
117 117 <optgroup label="Tags">
118 118 <option selected="selected" value="27cd5cce30c96924232dffcd24178a07ffeb5dfc">tip</option>
119 119 <option value="fd4bdb5e9b2a29b4393a4ac6caef48c17ee1a200">0.1.4</option>
120 120 <option value="17544fbfcd33ffb439e2b728b5d526b1ef30bfcf">0.1.3</option>
121 121 <option value="a7e60bff65d57ac3a1a1ce3b12a70f8a9e8a7720">0.1.2</option>
122 122 <option value="eb3a60fc964309c1a318b8dfe26aa2d1586c85ae">0.1.1</option>
123 123 </optgroup>
124 124 """)
125 125
126 126 response.mustcontain("""<div class="commit">merge</div>""")
127 127
128 128 response.mustcontain("""<span style="text-transform: uppercase;"><a href="#">branch: default</a></span>""")
129 129
130 130 def test_file_annotation(self):
131 131 self.log_user()
132 132 response = self.app.get(url(controller='files', action='index',
133 133 repo_name=HG_REPO,
134 134 revision='27cd5cce30c96924232dffcd24178a07ffeb5dfc',
135 135 f_path='vcs/nodes.py',
136 136 annotate=True))
137 137
138 138
139 139 response.mustcontain("""<optgroup label="Changesets">
140 140 <option value="8911406ad776fdd3d0b9932a2e89677e57405a48">r167:8911406ad776 (default)</option>
141 141 <option value="aa957ed78c35a1541f508d2ec90e501b0a9e3167">r165:aa957ed78c35 (default)</option>
142 142 <option value="48e11b73e94c0db33e736eaeea692f990cb0b5f1">r140:48e11b73e94c (default)</option>
143 143 <option value="adf3cbf483298563b968a6c673cd5bde5f7d5eea">r126:adf3cbf48329 (default)</option>
144 144 <option value="6249fd0fb2cfb1411e764129f598e2cf0de79a6f">r113:6249fd0fb2cf (git)</option>
145 145 <option value="75feb4c33e81186c87eac740cee2447330288412">r109:75feb4c33e81 (default)</option>
146 146 <option value="9a4dc232ecdc763ef2e98ae2238cfcbba4f6ad8d">r108:9a4dc232ecdc (default)</option>
147 147 <option value="595cce4efa21fda2f2e4eeb4fe5f2a6befe6fa2d">r107:595cce4efa21 (default)</option>
148 148 <option value="4a8bd421fbc2dfbfb70d85a3fe064075ab2c49da">r104:4a8bd421fbc2 (default)</option>
149 149 <option value="57be63fc8f85e65a0106a53187f7316f8c487ffa">r102:57be63fc8f85 (default)</option>
150 150 <option value="5530bd87f7e2e124a64d07cb2654c997682128be">r101:5530bd87f7e2 (git)</option>
151 151 <option value="e516008b1c93f142263dc4b7961787cbad654ce1">r99:e516008b1c93 (default)</option>
152 152 <option value="41f43fc74b8b285984554532eb105ac3be5c434f">r93:41f43fc74b8b (default)</option>
153 153 <option value="cc66b61b8455b264a7a8a2d8ddc80fcfc58c221e">r92:cc66b61b8455 (default)</option>
154 154 <option value="73ab5b616b3271b0518682fb4988ce421de8099f">r91:73ab5b616b32 (default)</option>
155 155 <option value="e0da75f308c0f18f98e9ce6257626009fdda2b39">r82:e0da75f308c0 (default)</option>
156 156 <option value="fb2e41e0f0810be4d7103bc2a4c7be16ee3ec611">r81:fb2e41e0f081 (default)</option>
157 157 <option value="602ae2f5e7ade70b3b66a58cdd9e3e613dc8a028">r76:602ae2f5e7ad (default)</option>
158 158 <option value="a066b25d5df7016b45a41b7e2a78c33b57adc235">r73:a066b25d5df7 (default)</option>
159 159 <option value="637a933c905958ce5151f154147c25c1c7b68832">r61:637a933c9059 (web)</option>
160 160 <option value="0c21004effeb8ce2d2d5b4a8baf6afa8394b6fbc">r60:0c21004effeb (web)</option>
161 161 <option value="a1f39c56d3f1d52d5fb5920370a2a2716cd9a444">r59:a1f39c56d3f1 (web)</option>
162 162 <option value="97d32df05c715a3bbf936bf3cc4e32fb77fe1a7f">r58:97d32df05c71 (web)</option>
163 163 <option value="08eaf14517718dccea4b67755a93368341aca919">r57:08eaf1451771 (web)</option>
164 164 <option value="22f71ad265265a53238359c883aa976e725aa07d">r56:22f71ad26526 (web)</option>
165 165 <option value="97501f02b7b4330924b647755663a2d90a5e638d">r49:97501f02b7b4 (web)</option>
166 166 <option value="86ede6754f2b27309452bb11f997386ae01d0e5a">r47:86ede6754f2b (web)</option>
167 167 <option value="014c40c0203c423dc19ecf94644f7cac9d4cdce0">r45:014c40c0203c (web)</option>
168 168 <option value="ee87846a61c12153b51543bf860e1026c6d3dcba">r30:ee87846a61c1 (default)</option>
169 169 <option value="9bb326a04ae5d98d437dece54be04f830cf1edd9">r26:9bb326a04ae5 (default)</option>
170 170 <option value="536c1a19428381cfea92ac44985304f6a8049569">r24:536c1a194283 (default)</option>
171 171 <option value="dc5d2c0661b61928834a785d3e64a3f80d3aad9c">r8:dc5d2c0661b6 (default)</option>
172 172 <option value="3803844fdbd3b711175fc3da9bdacfcd6d29a6fb">r7:3803844fdbd3 (default)</option>
173 173 </optgroup>
174 174 <optgroup label="Branches">
175 175 <option selected="selected" value="27cd5cce30c96924232dffcd24178a07ffeb5dfc">default</option>
176 176 <option value="97e8b885c04894463c51898e14387d80c30ed1ee">git</option>
177 177 <option value="2e6a2bf9356ca56df08807f4ad86d480da72a8f4">web</option>
178 178 </optgroup>
179 179 <optgroup label="Tags">
180 180 <option selected="selected" value="27cd5cce30c96924232dffcd24178a07ffeb5dfc">tip</option>
181 181 <option value="fd4bdb5e9b2a29b4393a4ac6caef48c17ee1a200">0.1.4</option>
182 182 <option value="17544fbfcd33ffb439e2b728b5d526b1ef30bfcf">0.1.3</option>
183 183 <option value="a7e60bff65d57ac3a1a1ce3b12a70f8a9e8a7720">0.1.2</option>
184 184 <option value="eb3a60fc964309c1a318b8dfe26aa2d1586c85ae">0.1.1</option>
185 185 </optgroup>""")
186 186
187 187 response.mustcontain("""<span style="text-transform: uppercase;"><a href="#">branch: default</a></span>""")
188 188
189 189 def test_archival(self):
190 190 self.log_user()
191 191
192 192 for arch_ext, info in ARCHIVE_SPECS.items():
193 193 short = '27cd5cce30c9%s' % arch_ext
194 194 fname = '27cd5cce30c96924232dffcd24178a07ffeb5dfc%s' % arch_ext
195 195 filename = '%s-%s' % (HG_REPO, short)
196 196 response = self.app.get(url(controller='files',
197 197 action='archivefile',
198 198 repo_name=HG_REPO,
199 199 fname=fname))
200 200
201 201 self.assertEqual(response.status, '200 OK')
202 202 heads = [
203 ('Content-Type', 'text/html; charset=utf-8'),
204 ('Content-Length', '0'), ('Pragma', 'no-cache'),
205 ('Cache-Control', 'no-cache')
203 ('Pragma', 'no-cache'),
204 ('Cache-Control', 'no-cache'),
205 ('Content-Disposition', 'attachment; filename=%s' % filename),
206 ('Content-Type', '%s; charset=utf-8' % info[0]),
206 207 ]
207 208 self.assertEqual(response.response._headers.items(), heads)
208 209
209 210 def test_archival_wrong_ext(self):
210 211 self.log_user()
211 212
212 213 for arch_ext in ['tar', 'rar', 'x', '..ax', '.zipz']:
213 214 fname = '27cd5cce30c96924232dffcd24178a07ffeb5dfc%s' % arch_ext
214 215
215 response = self.app.get(url(controller='files', action='archivefile',
216 response = self.app.get(url(controller='files',
217 action='archivefile',
216 218 repo_name=HG_REPO,
217 219 fname=fname))
218 220 response.mustcontain('Unknown archive type')
219 221
220 222 def test_archival_wrong_revision(self):
221 223 self.log_user()
222 224
223 for rev in ['00x000000', 'tar', 'wrong', '@##$@$424213232', '232dffcd']:
225 for rev in ['00x000000', 'tar', 'wrong', '@##$@$42413232', '232dffcd']:
224 226 fname = '%s.zip' % rev
225 227
226 response = self.app.get(url(controller='files', action='archivefile',
228 response = self.app.get(url(controller='files',
229 action='archivefile',
227 230 repo_name=HG_REPO,
228 231 fname=fname))
229 232 response.mustcontain('Unknown revision')
230 233
231 234 #==========================================================================
232 235 # RAW FILE
233 236 #==========================================================================
234 237 def test_raw_file_ok(self):
235 238 self.log_user()
236 239 response = self.app.get(url(controller='files', action='rawfile',
237 240 repo_name=HG_REPO,
238 241 revision='27cd5cce30c96924232dffcd24178a07ffeb5dfc',
239 242 f_path='vcs/nodes.py'))
240 243
241 244 self.assertEqual(response.content_disposition, "attachment; filename=nodes.py")
242 245 self.assertEqual(response.content_type, "text/x-python")
243 246
244 247 def test_raw_file_wrong_cs(self):
245 248 self.log_user()
246 249 rev = u'ERRORce30c96924232dffcd24178a07ffeb5dfc'
247 250 f_path = 'vcs/nodes.py'
248 251
249 252 response = self.app.get(url(controller='files', action='rawfile',
250 253 repo_name=HG_REPO,
251 254 revision=rev,
252 255 f_path=f_path))
253 256
254 257 msg = """Revision %r does not exist for this repository""" % (rev)
255 258 self.checkSessionFlash(response, msg)
256 259
257 260 msg = """%s""" % (HG_REPO)
258 261 self.checkSessionFlash(response, msg)
259 262
260 263 def test_raw_file_wrong_f_path(self):
261 264 self.log_user()
262 265 rev = '27cd5cce30c96924232dffcd24178a07ffeb5dfc'
263 266 f_path = 'vcs/ERRORnodes.py'
264 267 response = self.app.get(url(controller='files', action='rawfile',
265 268 repo_name=HG_REPO,
266 269 revision=rev,
267 270 f_path=f_path))
268 271
269 272 msg = "There is no file nor directory at the given path: %r at revision %r" % (f_path, rev[:12])
270 273 self.checkSessionFlash(response, msg)
271 274
272 275 #==========================================================================
273 276 # RAW RESPONSE - PLAIN
274 277 #==========================================================================
275 278 def test_raw_ok(self):
276 279 self.log_user()
277 280 response = self.app.get(url(controller='files', action='raw',
278 281 repo_name=HG_REPO,
279 282 revision='27cd5cce30c96924232dffcd24178a07ffeb5dfc',
280 283 f_path='vcs/nodes.py'))
281 284
282 285 self.assertEqual(response.content_type, "text/plain")
283 286
284 287 def test_raw_wrong_cs(self):
285 288 self.log_user()
286 289 rev = u'ERRORcce30c96924232dffcd24178a07ffeb5dfc'
287 290 f_path = 'vcs/nodes.py'
288 291
289 292 response = self.app.get(url(controller='files', action='raw',
290 293 repo_name=HG_REPO,
291 294 revision=rev,
292 295 f_path=f_path))
293 296 msg = """Revision %r does not exist for this repository""" % (rev)
294 297 self.checkSessionFlash(response, msg)
295 298
296 299 msg = """%s""" % (HG_REPO)
297 300 self.checkSessionFlash(response, msg)
298 301
299 302 def test_raw_wrong_f_path(self):
300 303 self.log_user()
301 304 rev = '27cd5cce30c96924232dffcd24178a07ffeb5dfc'
302 305 f_path = 'vcs/ERRORnodes.py'
303 306 response = self.app.get(url(controller='files', action='raw',
304 307 repo_name=HG_REPO,
305 308 revision=rev,
306 309 f_path=f_path))
307 310 msg = "There is no file nor directory at the given path: %r at revision %r" % (f_path, rev[:12])
308 311 self.checkSessionFlash(response, msg)
309 312
310 313 def test_ajaxed_files_list(self):
311 314 self.log_user()
312 315 rev = '27cd5cce30c96924232dffcd24178a07ffeb5dfc'
313 316 response = self.app.get(
314 317 url('files_nodelist_home', repo_name=HG_REPO,f_path='/',revision=rev),
315 318 extra_environ={'HTTP_X_PARTIAL_XHR': '1'},
316 319 )
317 320 response.mustcontain("vcs/web/simplevcs/views/repository.py")
General Comments 0
You need to be logged in to leave comments. Login now