##// END OF EJS Templates
fixes for vcs settings module
marcink -
r1480:daa29da0 beta
parent child Browse files
Show More
@@ -1,447 +1,447 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) 2009-2011 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
26 26 import os
27 27 import logging
28 28 import traceback
29 29
30 30 from os.path import join as jn
31 31
32 32 from pylons import request, response, session, tmpl_context as c, url
33 33 from pylons.i18n.translation import _
34 34 from pylons.controllers.util import redirect
35 35 from pylons.decorators import jsonify
36 36
37 from vcs.backends import ARCHIVE_SPECS
37 from vcs.conf import settings
38 38 from vcs.exceptions import RepositoryError, ChangesetDoesNotExistError, \
39 39 EmptyRepositoryError, ImproperArchiveTypeError, VCSError
40 40 from vcs.nodes import FileNode, NodeKind
41 41 from vcs.utils import diffs as differ
42 42
43 43 from rhodecode.lib import convert_line_endings, detect_mode, safe_str
44 44 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
45 45 from rhodecode.lib.base import BaseRepoController, render
46 46 from rhodecode.lib.utils import EmptyChangeset
47 47 import rhodecode.lib.helpers as h
48 48 from rhodecode.model.repo import RepoModel
49 49
50 50 log = logging.getLogger(__name__)
51 51
52 52
53 53 class FilesController(BaseRepoController):
54 54
55 55 @LoginRequired()
56 56 def __before__(self):
57 57 super(FilesController, self).__before__()
58 58 c.cut_off_limit = self.cut_off_limit
59 59
60 60 def __get_cs_or_redirect(self, rev, repo_name):
61 61 """
62 62 Safe way to get changeset if error occur it redirects to tip with
63 63 proper message
64 64
65 65 :param rev: revision to fetch
66 66 :param repo_name: repo name to redirect after
67 67 """
68 68
69 69 try:
70 70 return c.rhodecode_repo.get_changeset(rev)
71 71 except EmptyRepositoryError, e:
72 72 h.flash(_('There are no files yet'), category='warning')
73 73 redirect(h.url('summary_home', repo_name=repo_name))
74 74
75 75 except RepositoryError, e:
76 76 h.flash(str(e), category='warning')
77 77 redirect(h.url('files_home', repo_name=repo_name, revision='tip'))
78 78
79 79 def __get_filenode_or_redirect(self, repo_name, cs, path):
80 80 """
81 81 Returns file_node, if error occurs or given path is directory,
82 82 it'll redirect to top level path
83 83
84 84 :param repo_name: repo_name
85 85 :param cs: given changeset
86 86 :param path: path to lookup
87 87 """
88 88
89 89 try:
90 90 file_node = cs.get_node(path)
91 91 if file_node.is_dir():
92 92 raise RepositoryError('given path is a directory')
93 93 except RepositoryError, e:
94 94 h.flash(str(e), category='warning')
95 95 redirect(h.url('files_home', repo_name=repo_name,
96 96 revision=cs.raw_id))
97 97
98 98 return file_node
99 99
100 100
101 101 def __get_paths(self, changeset, starting_path):
102 102 """recursive walk in root dir and return a set of all path in that dir
103 103 based on repository walk function
104 104 """
105 105 _files = list()
106 106 _dirs = list()
107 107
108 108 try:
109 109 tip = changeset
110 110 for topnode, dirs, files in tip.walk(starting_path):
111 111 for f in files:
112 112 _files.append(f.path)
113 113 for d in dirs:
114 114 _dirs.append(d.path)
115 115 except RepositoryError, e:
116 116 log.debug(traceback.format_exc())
117 117 pass
118 118 return _dirs, _files
119 119
120 120 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
121 121 'repository.admin')
122 122 def index(self, repo_name, revision, f_path):
123 123 #reditect to given revision from form if given
124 124 post_revision = request.POST.get('at_rev', None)
125 125 if post_revision:
126 126 cs = self.__get_cs_or_redirect(post_revision, repo_name)
127 127 redirect(url('files_home', repo_name=c.repo_name,
128 128 revision=cs.raw_id, f_path=f_path))
129 129
130 130 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
131 131 c.branch = request.GET.get('branch', None)
132 132 c.f_path = f_path
133 133
134 134 cur_rev = c.changeset.revision
135 135
136 136 #prev link
137 137 try:
138 138 prev_rev = c.rhodecode_repo.get_changeset(cur_rev).prev(c.branch)
139 139 c.url_prev = url('files_home', repo_name=c.repo_name,
140 140 revision=prev_rev.raw_id, f_path=f_path)
141 141 if c.branch:
142 142 c.url_prev += '?branch=%s' % c.branch
143 143 except (ChangesetDoesNotExistError, VCSError):
144 144 c.url_prev = '#'
145 145
146 146 #next link
147 147 try:
148 148 next_rev = c.rhodecode_repo.get_changeset(cur_rev).next(c.branch)
149 149 c.url_next = url('files_home', repo_name=c.repo_name,
150 150 revision=next_rev.raw_id, f_path=f_path)
151 151 if c.branch:
152 152 c.url_next += '?branch=%s' % c.branch
153 153 except (ChangesetDoesNotExistError, VCSError):
154 154 c.url_next = '#'
155 155
156 156 #files or dirs
157 157 try:
158 158 c.files_list = c.changeset.get_node(f_path)
159 159
160 160 if c.files_list.is_file():
161 161 c.file_history = self._get_node_history(c.changeset, f_path)
162 162 else:
163 163 c.file_history = []
164 164 except RepositoryError, e:
165 165 h.flash(str(e), category='warning')
166 166 redirect(h.url('files_home', repo_name=repo_name,
167 167 revision=revision))
168 168
169 169 return render('files/files.html')
170 170
171 171 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
172 172 'repository.admin')
173 173 def rawfile(self, repo_name, revision, f_path):
174 174 cs = self.__get_cs_or_redirect(revision, repo_name)
175 175 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
176 176
177 177 response.content_disposition = 'attachment; filename=%s' % \
178 178 safe_str(f_path.split(os.sep)[-1])
179 179
180 180 response.content_type = file_node.mimetype
181 181 return file_node.content
182 182
183 183 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
184 184 'repository.admin')
185 185 def raw(self, repo_name, revision, f_path):
186 186 cs = self.__get_cs_or_redirect(revision, repo_name)
187 187 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
188 188
189 189 raw_mimetype_mapping = {
190 190 # map original mimetype to a mimetype used for "show as raw"
191 191 # you can also provide a content-disposition to override the
192 192 # default "attachment" disposition.
193 193 # orig_type: (new_type, new_dispo)
194 194
195 195 # show images inline:
196 196 'image/x-icon': ('image/x-icon', 'inline'),
197 197 'image/png': ('image/png', 'inline'),
198 198 'image/gif': ('image/gif', 'inline'),
199 199 'image/jpeg': ('image/jpeg', 'inline'),
200 200 'image/svg+xml': ('image/svg+xml', 'inline'),
201 201 }
202 202
203 203 mimetype = file_node.mimetype
204 204 try:
205 205 mimetype, dispo = raw_mimetype_mapping[mimetype]
206 206 except KeyError:
207 207 # we don't know anything special about this, handle it safely
208 208 if file_node.is_binary:
209 209 # do same as download raw for binary files
210 210 mimetype, dispo = 'application/octet-stream', 'attachment'
211 211 else:
212 212 # do not just use the original mimetype, but force text/plain,
213 213 # otherwise it would serve text/html and that might be unsafe.
214 214 # Note: underlying vcs library fakes text/plain mimetype if the
215 215 # mimetype can not be determined and it thinks it is not
216 216 # binary.This might lead to erroneous text display in some
217 217 # cases, but helps in other cases, like with text files
218 218 # without extension.
219 219 mimetype, dispo = 'text/plain', 'inline'
220 220
221 221 if dispo == 'attachment':
222 222 dispo = 'attachment; filename=%s' % \
223 223 safe_str(f_path.split(os.sep)[-1])
224 224
225 225 response.content_disposition = dispo
226 226 response.content_type = mimetype
227 227 return file_node.content
228 228
229 229 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
230 230 'repository.admin')
231 231 def annotate(self, repo_name, revision, f_path):
232 232 c.cs = self.__get_cs_or_redirect(revision, repo_name)
233 233 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
234 234
235 235 c.file_history = self._get_node_history(c.cs, f_path)
236 236 c.f_path = f_path
237 237 return render('files/files_annotate.html')
238 238
239 239 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
240 240 def edit(self, repo_name, revision, f_path):
241 241 r_post = request.POST
242 242
243 243 c.cs = self.__get_cs_or_redirect(revision, repo_name)
244 244 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
245 245
246 246 if c.file.is_binary:
247 247 return redirect(url('files_home', repo_name=c.repo_name,
248 248 revision=c.cs.raw_id, f_path=f_path))
249 249
250 250 c.file_history = self._get_node_history(c.cs, f_path)
251 251 c.f_path = f_path
252 252
253 253 if r_post:
254 254
255 255 old_content = c.file.content
256 256 sl = old_content.splitlines(1)
257 257 first_line = sl[0] if sl else ''
258 258 # modes: 0 - Unix, 1 - Mac, 2 - DOS
259 259 mode = detect_mode(first_line, 0)
260 260 content = convert_line_endings(r_post.get('content'), mode)
261 261
262 262 message = r_post.get('message') or (_('Edited %s via RhodeCode')
263 263 % (f_path))
264 264 author = self.rhodecode_user.full_contact
265 265
266 266 if content == old_content:
267 267 h.flash(_('No changes'),
268 268 category='warning')
269 269 return redirect(url('changeset_home', repo_name=c.repo_name,
270 270 revision='tip'))
271 271
272 272 try:
273 273 self.scm_model.commit_change(repo=c.rhodecode_repo,
274 274 repo_name=repo_name, cs=c.cs,
275 275 user=self.rhodecode_user,
276 276 author=author, message=message,
277 277 content=content, f_path=f_path)
278 278 h.flash(_('Successfully committed to %s' % f_path),
279 279 category='success')
280 280
281 281 except Exception:
282 282 log.error(traceback.format_exc())
283 283 h.flash(_('Error occurred during commit'), category='error')
284 284 return redirect(url('changeset_home',
285 285 repo_name=c.repo_name, revision='tip'))
286 286
287 287 return render('files/files_edit.html')
288 288
289 289 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
290 290 'repository.admin')
291 291 def archivefile(self, repo_name, fname):
292 292
293 293 fileformat = None
294 294 revision = None
295 295 ext = None
296 296 subrepos = request.GET.get('subrepos') == 'true'
297 297
298 for a_type, ext_data in ARCHIVE_SPECS.items():
298 for a_type, ext_data in settings.ARCHIVE_SPECS.items():
299 299 archive_spec = fname.split(ext_data[1])
300 300 if len(archive_spec) == 2 and archive_spec[1] == '':
301 301 fileformat = a_type or ext_data[1]
302 302 revision = archive_spec[0]
303 303 ext = ext_data[1]
304 304
305 305 try:
306 306 dbrepo = RepoModel().get_by_repo_name(repo_name)
307 307 if dbrepo.enable_downloads is False:
308 308 return _('downloads disabled')
309 309
310 310 cs = c.rhodecode_repo.get_changeset(revision)
311 content_type = ARCHIVE_SPECS[fileformat][0]
311 content_type = settings.ARCHIVE_SPECS[fileformat][0]
312 312 except ChangesetDoesNotExistError:
313 313 return _('Unknown revision %s') % revision
314 314 except EmptyRepositoryError:
315 315 return _('Empty repository')
316 316 except (ImproperArchiveTypeError, KeyError):
317 317 return _('Unknown archive type')
318 318
319 319 response.content_type = content_type
320 320 response.content_disposition = 'attachment; filename=%s-%s%s' \
321 321 % (repo_name, revision, ext)
322 322
323 323 import tempfile
324 324 archive = tempfile.mkstemp()[1]
325 325 t = open(archive, 'wb')
326 326 cs.fill_archive(stream=t, kind=fileformat, subrepos=subrepos)
327 327
328 328 def get_chunked_archive(archive):
329 329 stream = open(archive, 'rb')
330 330 while True:
331 331 data = stream.read(4096)
332 332 if not data:
333 333 os.remove(archive)
334 334 break
335 335 yield data
336 336
337 337 return get_chunked_archive(archive)
338 338
339 339 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
340 340 'repository.admin')
341 341 def diff(self, repo_name, f_path):
342 342 diff1 = request.GET.get('diff1')
343 343 diff2 = request.GET.get('diff2')
344 344 c.action = request.GET.get('diff')
345 345 c.no_changes = diff1 == diff2
346 346 c.f_path = f_path
347 347 c.big_diff = False
348 348
349 349 try:
350 350 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
351 351 c.changeset_1 = c.rhodecode_repo.get_changeset(diff1)
352 352 node1 = c.changeset_1.get_node(f_path)
353 353 else:
354 354 c.changeset_1 = EmptyChangeset(repo=c.rhodecode_repo)
355 355 node1 = FileNode('.', '', changeset=c.changeset_1)
356 356
357 357 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
358 358 c.changeset_2 = c.rhodecode_repo.get_changeset(diff2)
359 359 node2 = c.changeset_2.get_node(f_path)
360 360 else:
361 361 c.changeset_2 = EmptyChangeset(repo=c.rhodecode_repo)
362 362 node2 = FileNode('.', '', changeset=c.changeset_2)
363 363 except RepositoryError:
364 364 return redirect(url('files_home',
365 365 repo_name=c.repo_name, f_path=f_path))
366 366
367 367 if c.action == 'download':
368 368 diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2),
369 369 format='gitdiff')
370 370
371 371 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
372 372 response.content_type = 'text/plain'
373 373 response.content_disposition = 'attachment; filename=%s' \
374 374 % diff_name
375 375 return diff.raw_diff()
376 376
377 377 elif c.action == 'raw':
378 378 diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2),
379 379 format='gitdiff')
380 380 response.content_type = 'text/plain'
381 381 return diff.raw_diff()
382 382
383 383 elif c.action == 'diff':
384 384 if node1.is_binary or node2.is_binary:
385 385 c.cur_diff = _('Binary file')
386 386 elif node1.size > self.cut_off_limit or \
387 387 node2.size > self.cut_off_limit:
388 388 c.cur_diff = ''
389 389 c.big_diff = True
390 390 else:
391 391 diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2),
392 392 format='gitdiff')
393 393 c.cur_diff = diff.as_html()
394 394 else:
395 395
396 396 #default option
397 397 if node1.is_binary or node2.is_binary:
398 398 c.cur_diff = _('Binary file')
399 399 elif node1.size > self.cut_off_limit or \
400 400 node2.size > self.cut_off_limit:
401 401 c.cur_diff = ''
402 402 c.big_diff = True
403 403
404 404 else:
405 405 diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2),
406 406 format='gitdiff')
407 407 c.cur_diff = diff.as_html()
408 408
409 409 if not c.cur_diff and not c.big_diff:
410 410 c.no_changes = True
411 411 return render('files/file_diff.html')
412 412
413 413 def _get_node_history(self, cs, f_path):
414 414 changesets = cs.get_file_history(f_path)
415 415 hist_l = []
416 416
417 417 changesets_group = ([], _("Changesets"))
418 418 branches_group = ([], _("Branches"))
419 419 tags_group = ([], _("Tags"))
420 420
421 421 for chs in changesets:
422 422 n_desc = 'r%s:%s' % (chs.revision, chs.short_id)
423 423 changesets_group[0].append((chs.raw_id, n_desc,))
424 424
425 425 hist_l.append(changesets_group)
426 426
427 427 for name, chs in c.rhodecode_repo.branches.items():
428 428 #chs = chs.split(':')[-1]
429 429 branches_group[0].append((chs, name),)
430 430 hist_l.append(branches_group)
431 431
432 432 for name, chs in c.rhodecode_repo.tags.items():
433 433 #chs = chs.split(':')[-1]
434 434 tags_group[0].append((chs, name),)
435 435 hist_l.append(tags_group)
436 436
437 437 return hist_l
438 438
439 439 @jsonify
440 440 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
441 441 'repository.admin')
442 442 def nodelist(self, repo_name, revision, f_path):
443 443 if request.environ.get('HTTP_X_PARTIAL_XHR'):
444 444 cs = self.__get_cs_or_redirect(revision, repo_name)
445 445 _d, _f = self.__get_paths(cs, f_path)
446 446 return _d + _f
447 447
General Comments 0
You need to be logged in to leave comments. Login now