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