##// END OF EJS Templates
fix error when diff path is a directory, edge case generated by google bots
marcink -
r3037:789f2068 beta
parent child Browse files
Show More
@@ -1,594 +1,594 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
35
36 from rhodecode.lib import diffs
36 from rhodecode.lib import diffs
37 from rhodecode.lib import helpers as h
37 from rhodecode.lib import helpers as h
38
38
39 from rhodecode.lib.compat import OrderedDict
39 from rhodecode.lib.compat import OrderedDict
40 from rhodecode.lib.utils2 import convert_line_endings, detect_mode, safe_str,\
40 from rhodecode.lib.utils2 import convert_line_endings, detect_mode, safe_str,\
41 str2bool
41 str2bool
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.vcs.backends.base import EmptyChangeset
44 from rhodecode.lib.vcs.backends.base 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 NodeDoesNotExistError, ChangesetError
49 NodeDoesNotExistError, ChangesetError, NodeError
50 from rhodecode.lib.vcs.nodes import FileNode
50 from rhodecode.lib.vcs.nodes import FileNode
51
51
52 from rhodecode.model.repo import RepoModel
52 from rhodecode.model.repo import RepoModel
53 from rhodecode.model.scm import ScmModel
53 from rhodecode.model.scm import ScmModel
54 from rhodecode.model.db import Repository
54 from rhodecode.model.db import Repository
55
55
56 from rhodecode.controllers.changeset import anchor_url, _ignorews_url,\
56 from rhodecode.controllers.changeset import anchor_url, _ignorews_url,\
57 _context_url, get_line_ctx, get_ignore_ws
57 _context_url, get_line_ctx, get_ignore_ws
58
58
59
59
60 log = logging.getLogger(__name__)
60 log = logging.getLogger(__name__)
61
61
62
62
63 class FilesController(BaseRepoController):
63 class FilesController(BaseRepoController):
64
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_, _('click here to add new file'))
86 add_new = '<a href="%s">[%s]</a>' % (url_, _('click here to add new file'))
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 @LoginRequired()
117 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
117 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
118 'repository.admin')
118 'repository.admin')
119 def index(self, repo_name, revision, f_path, annotate=False):
119 def index(self, repo_name, revision, f_path, annotate=False):
120 # redirect to given revision from form if given
120 # redirect to given revision from form if given
121 post_revision = request.POST.get('at_rev', None)
121 post_revision = request.POST.get('at_rev', None)
122 if post_revision:
122 if post_revision:
123 cs = self.__get_cs_or_redirect(post_revision, repo_name)
123 cs = self.__get_cs_or_redirect(post_revision, repo_name)
124 redirect(url('files_home', repo_name=c.repo_name,
124 redirect(url('files_home', repo_name=c.repo_name,
125 revision=cs.raw_id, f_path=f_path))
125 revision=cs.raw_id, f_path=f_path))
126
126
127 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
127 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
128 c.branch = request.GET.get('branch', None)
128 c.branch = request.GET.get('branch', None)
129 c.f_path = f_path
129 c.f_path = f_path
130 c.annotate = annotate
130 c.annotate = annotate
131 cur_rev = c.changeset.revision
131 cur_rev = c.changeset.revision
132
132
133 # prev link
133 # prev link
134 try:
134 try:
135 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)
136 c.url_prev = url('files_home', repo_name=c.repo_name,
136 c.url_prev = url('files_home', repo_name=c.repo_name,
137 revision=prev_rev.raw_id, f_path=f_path)
137 revision=prev_rev.raw_id, f_path=f_path)
138 if c.branch:
138 if c.branch:
139 c.url_prev += '?branch=%s' % c.branch
139 c.url_prev += '?branch=%s' % c.branch
140 except (ChangesetDoesNotExistError, VCSError):
140 except (ChangesetDoesNotExistError, VCSError):
141 c.url_prev = '#'
141 c.url_prev = '#'
142
142
143 # next link
143 # next link
144 try:
144 try:
145 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)
146 c.url_next = url('files_home', repo_name=c.repo_name,
146 c.url_next = url('files_home', repo_name=c.repo_name,
147 revision=next_rev.raw_id, f_path=f_path)
147 revision=next_rev.raw_id, f_path=f_path)
148 if c.branch:
148 if c.branch:
149 c.url_next += '?branch=%s' % c.branch
149 c.url_next += '?branch=%s' % c.branch
150 except (ChangesetDoesNotExistError, VCSError):
150 except (ChangesetDoesNotExistError, VCSError):
151 c.url_next = '#'
151 c.url_next = '#'
152
152
153 # files or dirs
153 # files or dirs
154 try:
154 try:
155 c.file = c.changeset.get_node(f_path)
155 c.file = c.changeset.get_node(f_path)
156
156
157 if c.file.is_file():
157 if c.file.is_file():
158 c.load_full_history = False
158 c.load_full_history = False
159 file_last_cs = c.file.last_changeset
159 file_last_cs = c.file.last_changeset
160 c.file_changeset = (c.changeset
160 c.file_changeset = (c.changeset
161 if c.changeset.revision < file_last_cs.revision
161 if c.changeset.revision < file_last_cs.revision
162 else file_last_cs)
162 else file_last_cs)
163 _hist = []
163 _hist = []
164 c.file_history = []
164 c.file_history = []
165 if c.load_full_history:
165 if c.load_full_history:
166 c.file_history, _hist = self._get_node_history(c.changeset, f_path)
166 c.file_history, _hist = self._get_node_history(c.changeset, f_path)
167
167
168 c.authors = []
168 c.authors = []
169 for a in set([x.author for x in _hist]):
169 for a in set([x.author for x in _hist]):
170 c.authors.append((h.email(a), h.person(a)))
170 c.authors.append((h.email(a), h.person(a)))
171 else:
171 else:
172 c.authors = c.file_history = []
172 c.authors = c.file_history = []
173 except RepositoryError, e:
173 except RepositoryError, e:
174 h.flash(str(e), category='warning')
174 h.flash(str(e), category='warning')
175 redirect(h.url('files_home', repo_name=repo_name,
175 redirect(h.url('files_home', repo_name=repo_name,
176 revision='tip'))
176 revision='tip'))
177
177
178 if request.environ.get('HTTP_X_PARTIAL_XHR'):
178 if request.environ.get('HTTP_X_PARTIAL_XHR'):
179 return render('files/files_ypjax.html')
179 return render('files/files_ypjax.html')
180
180
181 return render('files/files.html')
181 return render('files/files.html')
182
182
183 def history(self, repo_name, revision, f_path, annotate=False):
183 def history(self, repo_name, revision, f_path, annotate=False):
184 if request.environ.get('HTTP_X_PARTIAL_XHR'):
184 if request.environ.get('HTTP_X_PARTIAL_XHR'):
185 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
185 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
186 c.f_path = f_path
186 c.f_path = f_path
187 c.annotate = annotate
187 c.annotate = annotate
188 c.file = c.changeset.get_node(f_path)
188 c.file = c.changeset.get_node(f_path)
189 if c.file.is_file():
189 if c.file.is_file():
190 file_last_cs = c.file.last_changeset
190 file_last_cs = c.file.last_changeset
191 c.file_changeset = (c.changeset
191 c.file_changeset = (c.changeset
192 if c.changeset.revision < file_last_cs.revision
192 if c.changeset.revision < file_last_cs.revision
193 else file_last_cs)
193 else file_last_cs)
194 c.file_history, _hist = self._get_node_history(c.changeset, f_path)
194 c.file_history, _hist = self._get_node_history(c.changeset, f_path)
195 c.authors = []
195 c.authors = []
196 for a in set([x.author for x in _hist]):
196 for a in set([x.author for x in _hist]):
197 c.authors.append((h.email(a), h.person(a)))
197 c.authors.append((h.email(a), h.person(a)))
198 return render('files/files_history_box.html')
198 return render('files/files_history_box.html')
199
199
200 @LoginRequired()
200 @LoginRequired()
201 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
201 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
202 'repository.admin')
202 'repository.admin')
203 def rawfile(self, repo_name, revision, f_path):
203 def rawfile(self, repo_name, revision, f_path):
204 cs = self.__get_cs_or_redirect(revision, repo_name)
204 cs = self.__get_cs_or_redirect(revision, repo_name)
205 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
205 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
206
206
207 response.content_disposition = 'attachment; filename=%s' % \
207 response.content_disposition = 'attachment; filename=%s' % \
208 safe_str(f_path.split(Repository.url_sep())[-1])
208 safe_str(f_path.split(Repository.url_sep())[-1])
209
209
210 response.content_type = file_node.mimetype
210 response.content_type = file_node.mimetype
211 return file_node.content
211 return file_node.content
212
212
213 @LoginRequired()
213 @LoginRequired()
214 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
214 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
215 'repository.admin')
215 'repository.admin')
216 def raw(self, repo_name, revision, f_path):
216 def raw(self, repo_name, revision, f_path):
217 cs = self.__get_cs_or_redirect(revision, repo_name)
217 cs = self.__get_cs_or_redirect(revision, repo_name)
218 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
218 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
219
219
220 raw_mimetype_mapping = {
220 raw_mimetype_mapping = {
221 # map original mimetype to a mimetype used for "show as raw"
221 # map original mimetype to a mimetype used for "show as raw"
222 # you can also provide a content-disposition to override the
222 # you can also provide a content-disposition to override the
223 # default "attachment" disposition.
223 # default "attachment" disposition.
224 # orig_type: (new_type, new_dispo)
224 # orig_type: (new_type, new_dispo)
225
225
226 # show images inline:
226 # show images inline:
227 'image/x-icon': ('image/x-icon', 'inline'),
227 'image/x-icon': ('image/x-icon', 'inline'),
228 'image/png': ('image/png', 'inline'),
228 'image/png': ('image/png', 'inline'),
229 'image/gif': ('image/gif', 'inline'),
229 'image/gif': ('image/gif', 'inline'),
230 'image/jpeg': ('image/jpeg', 'inline'),
230 'image/jpeg': ('image/jpeg', 'inline'),
231 'image/svg+xml': ('image/svg+xml', 'inline'),
231 'image/svg+xml': ('image/svg+xml', 'inline'),
232 }
232 }
233
233
234 mimetype = file_node.mimetype
234 mimetype = file_node.mimetype
235 try:
235 try:
236 mimetype, dispo = raw_mimetype_mapping[mimetype]
236 mimetype, dispo = raw_mimetype_mapping[mimetype]
237 except KeyError:
237 except KeyError:
238 # we don't know anything special about this, handle it safely
238 # we don't know anything special about this, handle it safely
239 if file_node.is_binary:
239 if file_node.is_binary:
240 # do same as download raw for binary files
240 # do same as download raw for binary files
241 mimetype, dispo = 'application/octet-stream', 'attachment'
241 mimetype, dispo = 'application/octet-stream', 'attachment'
242 else:
242 else:
243 # do not just use the original mimetype, but force text/plain,
243 # do not just use the original mimetype, but force text/plain,
244 # otherwise it would serve text/html and that might be unsafe.
244 # otherwise it would serve text/html and that might be unsafe.
245 # Note: underlying vcs library fakes text/plain mimetype if the
245 # Note: underlying vcs library fakes text/plain mimetype if the
246 # mimetype can not be determined and it thinks it is not
246 # mimetype can not be determined and it thinks it is not
247 # binary.This might lead to erroneous text display in some
247 # binary.This might lead to erroneous text display in some
248 # cases, but helps in other cases, like with text files
248 # cases, but helps in other cases, like with text files
249 # without extension.
249 # without extension.
250 mimetype, dispo = 'text/plain', 'inline'
250 mimetype, dispo = 'text/plain', 'inline'
251
251
252 if dispo == 'attachment':
252 if dispo == 'attachment':
253 dispo = 'attachment; filename=%s' % \
253 dispo = 'attachment; filename=%s' % \
254 safe_str(f_path.split(os.sep)[-1])
254 safe_str(f_path.split(os.sep)[-1])
255
255
256 response.content_disposition = dispo
256 response.content_disposition = dispo
257 response.content_type = mimetype
257 response.content_type = mimetype
258 return file_node.content
258 return file_node.content
259
259
260 @LoginRequired()
260 @LoginRequired()
261 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
261 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
262 def edit(self, repo_name, revision, f_path):
262 def edit(self, repo_name, revision, f_path):
263 repo = Repository.get_by_repo_name(repo_name)
263 repo = Repository.get_by_repo_name(repo_name)
264 if repo.enable_locking and repo.locked[0]:
264 if repo.enable_locking and repo.locked[0]:
265 h.flash(_('This repository is has been locked by %s on %s')
265 h.flash(_('This repository is has been locked by %s on %s')
266 % (h.person_by_id(repo.locked[0]),
266 % (h.person_by_id(repo.locked[0]),
267 h.fmt_date(h.time_to_datetime(repo.locked[1]))),
267 h.fmt_date(h.time_to_datetime(repo.locked[1]))),
268 'warning')
268 'warning')
269 return redirect(h.url('files_home',
269 return redirect(h.url('files_home',
270 repo_name=repo_name, revision='tip'))
270 repo_name=repo_name, revision='tip'))
271
271
272 r_post = request.POST
272 r_post = request.POST
273
273
274 c.cs = self.__get_cs_or_redirect(revision, repo_name)
274 c.cs = self.__get_cs_or_redirect(revision, repo_name)
275 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
275 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
276
276
277 if c.file.is_binary:
277 if c.file.is_binary:
278 return redirect(url('files_home', repo_name=c.repo_name,
278 return redirect(url('files_home', repo_name=c.repo_name,
279 revision=c.cs.raw_id, f_path=f_path))
279 revision=c.cs.raw_id, f_path=f_path))
280
280
281 c.f_path = f_path
281 c.f_path = f_path
282
282
283 if r_post:
283 if r_post:
284
284
285 old_content = c.file.content
285 old_content = c.file.content
286 sl = old_content.splitlines(1)
286 sl = old_content.splitlines(1)
287 first_line = sl[0] if sl else ''
287 first_line = sl[0] if sl else ''
288 # modes: 0 - Unix, 1 - Mac, 2 - DOS
288 # modes: 0 - Unix, 1 - Mac, 2 - DOS
289 mode = detect_mode(first_line, 0)
289 mode = detect_mode(first_line, 0)
290 content = convert_line_endings(r_post.get('content'), mode)
290 content = convert_line_endings(r_post.get('content'), mode)
291
291
292 message = r_post.get('message') or (_('Edited %s via RhodeCode')
292 message = r_post.get('message') or (_('Edited %s via RhodeCode')
293 % (f_path))
293 % (f_path))
294 author = self.rhodecode_user.full_contact
294 author = self.rhodecode_user.full_contact
295
295
296 if content == old_content:
296 if content == old_content:
297 h.flash(_('No changes'),
297 h.flash(_('No changes'),
298 category='warning')
298 category='warning')
299 return redirect(url('changeset_home', repo_name=c.repo_name,
299 return redirect(url('changeset_home', repo_name=c.repo_name,
300 revision='tip'))
300 revision='tip'))
301
301
302 try:
302 try:
303 self.scm_model.commit_change(repo=c.rhodecode_repo,
303 self.scm_model.commit_change(repo=c.rhodecode_repo,
304 repo_name=repo_name, cs=c.cs,
304 repo_name=repo_name, cs=c.cs,
305 user=self.rhodecode_user,
305 user=self.rhodecode_user,
306 author=author, message=message,
306 author=author, message=message,
307 content=content, f_path=f_path)
307 content=content, f_path=f_path)
308 h.flash(_('Successfully committed to %s') % f_path,
308 h.flash(_('Successfully committed to %s') % f_path,
309 category='success')
309 category='success')
310
310
311 except Exception:
311 except Exception:
312 log.error(traceback.format_exc())
312 log.error(traceback.format_exc())
313 h.flash(_('Error occurred during commit'), category='error')
313 h.flash(_('Error occurred during commit'), category='error')
314 return redirect(url('changeset_home',
314 return redirect(url('changeset_home',
315 repo_name=c.repo_name, revision='tip'))
315 repo_name=c.repo_name, revision='tip'))
316
316
317 return render('files/files_edit.html')
317 return render('files/files_edit.html')
318
318
319 @LoginRequired()
319 @LoginRequired()
320 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
320 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
321 def add(self, repo_name, revision, f_path):
321 def add(self, repo_name, revision, f_path):
322
322
323 repo = Repository.get_by_repo_name(repo_name)
323 repo = Repository.get_by_repo_name(repo_name)
324 if repo.enable_locking and repo.locked[0]:
324 if repo.enable_locking and repo.locked[0]:
325 h.flash(_('This repository is has been locked by %s on %s')
325 h.flash(_('This repository is has been locked by %s on %s')
326 % (h.person_by_id(repo.locked[0]),
326 % (h.person_by_id(repo.locked[0]),
327 h.fmt_date(h.time_to_datetime(repo.locked[1]))),
327 h.fmt_date(h.time_to_datetime(repo.locked[1]))),
328 'warning')
328 'warning')
329 return redirect(h.url('files_home',
329 return redirect(h.url('files_home',
330 repo_name=repo_name, revision='tip'))
330 repo_name=repo_name, revision='tip'))
331
331
332 r_post = request.POST
332 r_post = request.POST
333 c.cs = self.__get_cs_or_redirect(revision, repo_name,
333 c.cs = self.__get_cs_or_redirect(revision, repo_name,
334 redirect_after=False)
334 redirect_after=False)
335 if c.cs is None:
335 if c.cs is None:
336 c.cs = EmptyChangeset(alias=c.rhodecode_repo.alias)
336 c.cs = EmptyChangeset(alias=c.rhodecode_repo.alias)
337
337
338 c.f_path = f_path
338 c.f_path = f_path
339
339
340 if r_post:
340 if r_post:
341 unix_mode = 0
341 unix_mode = 0
342 content = convert_line_endings(r_post.get('content'), unix_mode)
342 content = convert_line_endings(r_post.get('content'), unix_mode)
343
343
344 message = r_post.get('message') or (_('Added %s via RhodeCode')
344 message = r_post.get('message') or (_('Added %s via RhodeCode')
345 % (f_path))
345 % (f_path))
346 location = r_post.get('location')
346 location = r_post.get('location')
347 filename = r_post.get('filename')
347 filename = r_post.get('filename')
348 file_obj = r_post.get('upload_file', None)
348 file_obj = r_post.get('upload_file', None)
349
349
350 if file_obj is not None and hasattr(file_obj, 'filename'):
350 if file_obj is not None and hasattr(file_obj, 'filename'):
351 filename = file_obj.filename
351 filename = file_obj.filename
352 content = file_obj.file
352 content = file_obj.file
353
353
354 node_path = os.path.join(location, filename)
354 node_path = os.path.join(location, filename)
355 author = self.rhodecode_user.full_contact
355 author = self.rhodecode_user.full_contact
356
356
357 if not content:
357 if not content:
358 h.flash(_('No content'), category='warning')
358 h.flash(_('No content'), category='warning')
359 return redirect(url('changeset_home', repo_name=c.repo_name,
359 return redirect(url('changeset_home', repo_name=c.repo_name,
360 revision='tip'))
360 revision='tip'))
361 if not filename:
361 if not filename:
362 h.flash(_('No filename'), category='warning')
362 h.flash(_('No filename'), category='warning')
363 return redirect(url('changeset_home', repo_name=c.repo_name,
363 return redirect(url('changeset_home', repo_name=c.repo_name,
364 revision='tip'))
364 revision='tip'))
365
365
366 try:
366 try:
367 self.scm_model.create_node(repo=c.rhodecode_repo,
367 self.scm_model.create_node(repo=c.rhodecode_repo,
368 repo_name=repo_name, cs=c.cs,
368 repo_name=repo_name, cs=c.cs,
369 user=self.rhodecode_user,
369 user=self.rhodecode_user,
370 author=author, message=message,
370 author=author, message=message,
371 content=content, f_path=node_path)
371 content=content, f_path=node_path)
372 h.flash(_('Successfully committed to %s') % node_path,
372 h.flash(_('Successfully committed to %s') % node_path,
373 category='success')
373 category='success')
374 except NodeAlreadyExistsError, e:
374 except NodeAlreadyExistsError, e:
375 h.flash(_(e), category='error')
375 h.flash(_(e), category='error')
376 except Exception:
376 except Exception:
377 log.error(traceback.format_exc())
377 log.error(traceback.format_exc())
378 h.flash(_('Error occurred during commit'), category='error')
378 h.flash(_('Error occurred during commit'), category='error')
379 return redirect(url('changeset_home',
379 return redirect(url('changeset_home',
380 repo_name=c.repo_name, revision='tip'))
380 repo_name=c.repo_name, revision='tip'))
381
381
382 return render('files/files_add.html')
382 return render('files/files_add.html')
383
383
384 @LoginRequired()
384 @LoginRequired()
385 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
385 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
386 'repository.admin')
386 'repository.admin')
387 def archivefile(self, repo_name, fname):
387 def archivefile(self, repo_name, fname):
388
388
389 fileformat = None
389 fileformat = None
390 revision = None
390 revision = None
391 ext = None
391 ext = None
392 subrepos = request.GET.get('subrepos') == 'true'
392 subrepos = request.GET.get('subrepos') == 'true'
393
393
394 for a_type, ext_data in settings.ARCHIVE_SPECS.items():
394 for a_type, ext_data in settings.ARCHIVE_SPECS.items():
395 archive_spec = fname.split(ext_data[1])
395 archive_spec = fname.split(ext_data[1])
396 if len(archive_spec) == 2 and archive_spec[1] == '':
396 if len(archive_spec) == 2 and archive_spec[1] == '':
397 fileformat = a_type or ext_data[1]
397 fileformat = a_type or ext_data[1]
398 revision = archive_spec[0]
398 revision = archive_spec[0]
399 ext = ext_data[1]
399 ext = ext_data[1]
400
400
401 try:
401 try:
402 dbrepo = RepoModel().get_by_repo_name(repo_name)
402 dbrepo = RepoModel().get_by_repo_name(repo_name)
403 if dbrepo.enable_downloads is False:
403 if dbrepo.enable_downloads is False:
404 return _('downloads disabled')
404 return _('downloads disabled')
405
405
406 if c.rhodecode_repo.alias == 'hg':
406 if c.rhodecode_repo.alias == 'hg':
407 # patch and reset hooks section of UI config to not run any
407 # patch and reset hooks section of UI config to not run any
408 # hooks on fetching archives with subrepos
408 # hooks on fetching archives with subrepos
409 for k, v in c.rhodecode_repo._repo.ui.configitems('hooks'):
409 for k, v in c.rhodecode_repo._repo.ui.configitems('hooks'):
410 c.rhodecode_repo._repo.ui.setconfig('hooks', k, None)
410 c.rhodecode_repo._repo.ui.setconfig('hooks', k, None)
411
411
412 cs = c.rhodecode_repo.get_changeset(revision)
412 cs = c.rhodecode_repo.get_changeset(revision)
413 content_type = settings.ARCHIVE_SPECS[fileformat][0]
413 content_type = settings.ARCHIVE_SPECS[fileformat][0]
414 except ChangesetDoesNotExistError:
414 except ChangesetDoesNotExistError:
415 return _('Unknown revision %s') % revision
415 return _('Unknown revision %s') % revision
416 except EmptyRepositoryError:
416 except EmptyRepositoryError:
417 return _('Empty repository')
417 return _('Empty repository')
418 except (ImproperArchiveTypeError, KeyError):
418 except (ImproperArchiveTypeError, KeyError):
419 return _('Unknown archive type')
419 return _('Unknown archive type')
420
420
421 fd, archive = tempfile.mkstemp()
421 fd, archive = tempfile.mkstemp()
422 t = open(archive, 'wb')
422 t = open(archive, 'wb')
423 cs.fill_archive(stream=t, kind=fileformat, subrepos=subrepos)
423 cs.fill_archive(stream=t, kind=fileformat, subrepos=subrepos)
424 t.close()
424 t.close()
425
425
426 def get_chunked_archive(archive):
426 def get_chunked_archive(archive):
427 stream = open(archive, 'rb')
427 stream = open(archive, 'rb')
428 while True:
428 while True:
429 data = stream.read(16 * 1024)
429 data = stream.read(16 * 1024)
430 if not data:
430 if not data:
431 stream.close()
431 stream.close()
432 os.close(fd)
432 os.close(fd)
433 os.remove(archive)
433 os.remove(archive)
434 break
434 break
435 yield data
435 yield data
436
436
437 response.content_disposition = str('attachment; filename=%s-%s%s' \
437 response.content_disposition = str('attachment; filename=%s-%s%s' \
438 % (repo_name, revision[:12], ext))
438 % (repo_name, revision[:12], ext))
439 response.content_type = str(content_type)
439 response.content_type = str(content_type)
440 return get_chunked_archive(archive)
440 return get_chunked_archive(archive)
441
441
442 @LoginRequired()
442 @LoginRequired()
443 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
443 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
444 'repository.admin')
444 'repository.admin')
445 def diff(self, repo_name, f_path):
445 def diff(self, repo_name, f_path):
446 ignore_whitespace = request.GET.get('ignorews') == '1'
446 ignore_whitespace = request.GET.get('ignorews') == '1'
447 line_context = request.GET.get('context', 3)
447 line_context = request.GET.get('context', 3)
448 diff1 = request.GET.get('diff1', '')
448 diff1 = request.GET.get('diff1', '')
449 diff2 = request.GET.get('diff2', '')
449 diff2 = request.GET.get('diff2', '')
450 c.action = request.GET.get('diff')
450 c.action = request.GET.get('diff')
451 c.no_changes = diff1 == diff2
451 c.no_changes = diff1 == diff2
452 c.f_path = f_path
452 c.f_path = f_path
453 c.big_diff = False
453 c.big_diff = False
454 c.anchor_url = anchor_url
454 c.anchor_url = anchor_url
455 c.ignorews_url = _ignorews_url
455 c.ignorews_url = _ignorews_url
456 c.context_url = _context_url
456 c.context_url = _context_url
457 c.changes = OrderedDict()
457 c.changes = OrderedDict()
458 c.changes[diff2] = []
458 c.changes[diff2] = []
459
459
460 #special case if we want a show rev only, it's impl here
460 #special case if we want a show rev only, it's impl here
461 #to reduce JS and callbacks
461 #to reduce JS and callbacks
462
462
463 if request.GET.get('show_rev'):
463 if request.GET.get('show_rev'):
464 if str2bool(request.GET.get('annotate', 'False')):
464 if str2bool(request.GET.get('annotate', 'False')):
465 _url = url('files_annotate_home', repo_name=c.repo_name,
465 _url = url('files_annotate_home', repo_name=c.repo_name,
466 revision=diff1, f_path=c.f_path)
466 revision=diff1, f_path=c.f_path)
467 else:
467 else:
468 _url = url('files_home', repo_name=c.repo_name,
468 _url = url('files_home', repo_name=c.repo_name,
469 revision=diff1, f_path=c.f_path)
469 revision=diff1, f_path=c.f_path)
470
470
471 return redirect(_url)
471 return redirect(_url)
472 try:
472 try:
473 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
473 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
474 c.changeset_1 = c.rhodecode_repo.get_changeset(diff1)
474 c.changeset_1 = c.rhodecode_repo.get_changeset(diff1)
475 try:
475 try:
476 node1 = c.changeset_1.get_node(f_path)
476 node1 = c.changeset_1.get_node(f_path)
477 except NodeDoesNotExistError:
477 except NodeDoesNotExistError:
478 c.changeset_1 = EmptyChangeset(cs=diff1,
478 c.changeset_1 = EmptyChangeset(cs=diff1,
479 revision=c.changeset_1.revision,
479 revision=c.changeset_1.revision,
480 repo=c.rhodecode_repo)
480 repo=c.rhodecode_repo)
481 node1 = FileNode(f_path, '', changeset=c.changeset_1)
481 node1 = FileNode(f_path, '', changeset=c.changeset_1)
482 else:
482 else:
483 c.changeset_1 = EmptyChangeset(repo=c.rhodecode_repo)
483 c.changeset_1 = EmptyChangeset(repo=c.rhodecode_repo)
484 node1 = FileNode(f_path, '', changeset=c.changeset_1)
484 node1 = FileNode(f_path, '', changeset=c.changeset_1)
485
485
486 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
486 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
487 c.changeset_2 = c.rhodecode_repo.get_changeset(diff2)
487 c.changeset_2 = c.rhodecode_repo.get_changeset(diff2)
488 try:
488 try:
489 node2 = c.changeset_2.get_node(f_path)
489 node2 = c.changeset_2.get_node(f_path)
490 except NodeDoesNotExistError:
490 except NodeDoesNotExistError:
491 c.changeset_2 = EmptyChangeset(cs=diff2,
491 c.changeset_2 = EmptyChangeset(cs=diff2,
492 revision=c.changeset_2.revision,
492 revision=c.changeset_2.revision,
493 repo=c.rhodecode_repo)
493 repo=c.rhodecode_repo)
494 node2 = FileNode(f_path, '', changeset=c.changeset_2)
494 node2 = FileNode(f_path, '', changeset=c.changeset_2)
495 else:
495 else:
496 c.changeset_2 = EmptyChangeset(repo=c.rhodecode_repo)
496 c.changeset_2 = EmptyChangeset(repo=c.rhodecode_repo)
497 node2 = FileNode(f_path, '', changeset=c.changeset_2)
497 node2 = FileNode(f_path, '', changeset=c.changeset_2)
498 except RepositoryError:
498 except (RepositoryError, NodeError):
499 log.error(traceback.format_exc())
499 log.error(traceback.format_exc())
500 return redirect(url('files_home', repo_name=c.repo_name,
500 return redirect(url('files_home', repo_name=c.repo_name,
501 f_path=f_path))
501 f_path=f_path))
502
502
503 if c.action == 'download':
503 if c.action == 'download':
504 _diff = diffs.get_gitdiff(node1, node2,
504 _diff = diffs.get_gitdiff(node1, node2,
505 ignore_whitespace=ignore_whitespace,
505 ignore_whitespace=ignore_whitespace,
506 context=line_context)
506 context=line_context)
507 diff = diffs.DiffProcessor(_diff, format='gitdiff')
507 diff = diffs.DiffProcessor(_diff, format='gitdiff')
508
508
509 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
509 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
510 response.content_type = 'text/plain'
510 response.content_type = 'text/plain'
511 response.content_disposition = (
511 response.content_disposition = (
512 'attachment; filename=%s' % diff_name
512 'attachment; filename=%s' % diff_name
513 )
513 )
514 return diff.as_raw()
514 return diff.as_raw()
515
515
516 elif c.action == 'raw':
516 elif c.action == 'raw':
517 _diff = diffs.get_gitdiff(node1, node2,
517 _diff = diffs.get_gitdiff(node1, node2,
518 ignore_whitespace=ignore_whitespace,
518 ignore_whitespace=ignore_whitespace,
519 context=line_context)
519 context=line_context)
520 diff = diffs.DiffProcessor(_diff, format='gitdiff')
520 diff = diffs.DiffProcessor(_diff, format='gitdiff')
521 response.content_type = 'text/plain'
521 response.content_type = 'text/plain'
522 return diff.as_raw()
522 return diff.as_raw()
523
523
524 else:
524 else:
525 fid = h.FID(diff2, node2.path)
525 fid = h.FID(diff2, node2.path)
526 line_context_lcl = get_line_ctx(fid, request.GET)
526 line_context_lcl = get_line_ctx(fid, request.GET)
527 ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
527 ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
528
528
529 lim = request.GET.get('fulldiff') or self.cut_off_limit
529 lim = request.GET.get('fulldiff') or self.cut_off_limit
530 _, cs1, cs2, diff, st = diffs.wrapped_diff(filenode_old=node1,
530 _, cs1, cs2, diff, st = diffs.wrapped_diff(filenode_old=node1,
531 filenode_new=node2,
531 filenode_new=node2,
532 cut_off_limit=lim,
532 cut_off_limit=lim,
533 ignore_whitespace=ign_whitespace_lcl,
533 ignore_whitespace=ign_whitespace_lcl,
534 line_context=line_context_lcl,
534 line_context=line_context_lcl,
535 enable_comments=False)
535 enable_comments=False)
536 op = ''
536 op = ''
537 filename = node1.path
537 filename = node1.path
538 cs_changes = {
538 cs_changes = {
539 'fid': [cs1, cs2, op, filename, diff, st]
539 'fid': [cs1, cs2, op, filename, diff, st]
540 }
540 }
541 c.changes = cs_changes
541 c.changes = cs_changes
542
542
543 return render('files/file_diff.html')
543 return render('files/file_diff.html')
544
544
545 def _get_node_history(self, cs, f_path, changesets=None):
545 def _get_node_history(self, cs, f_path, changesets=None):
546 """
546 """
547 get changesets history for given node
547 get changesets history for given node
548
548
549 :param cs: changeset to calculate history
549 :param cs: changeset to calculate history
550 :param f_path: path for node to calculate history for
550 :param f_path: path for node to calculate history for
551 :param changesets: if passed don't calculate history and take
551 :param changesets: if passed don't calculate history and take
552 changesets defined in this list
552 changesets defined in this list
553 """
553 """
554 # calculate history based on tip
554 # calculate history based on tip
555 tip_cs = c.rhodecode_repo.get_changeset()
555 tip_cs = c.rhodecode_repo.get_changeset()
556 if changesets is None:
556 if changesets is None:
557 try:
557 try:
558 changesets = tip_cs.get_file_history(f_path)
558 changesets = tip_cs.get_file_history(f_path)
559 except (NodeDoesNotExistError, ChangesetError):
559 except (NodeDoesNotExistError, ChangesetError):
560 #this node is not present at tip !
560 #this node is not present at tip !
561 changesets = cs.get_file_history(f_path)
561 changesets = cs.get_file_history(f_path)
562 hist_l = []
562 hist_l = []
563
563
564 changesets_group = ([], _("Changesets"))
564 changesets_group = ([], _("Changesets"))
565 branches_group = ([], _("Branches"))
565 branches_group = ([], _("Branches"))
566 tags_group = ([], _("Tags"))
566 tags_group = ([], _("Tags"))
567 _hg = cs.repository.alias == 'hg'
567 _hg = cs.repository.alias == 'hg'
568 for chs in changesets:
568 for chs in changesets:
569 #_branch = '(%s)' % chs.branch if _hg else ''
569 #_branch = '(%s)' % chs.branch if _hg else ''
570 _branch = chs.branch
570 _branch = chs.branch
571 n_desc = 'r%s:%s (%s)' % (chs.revision, chs.short_id, _branch)
571 n_desc = 'r%s:%s (%s)' % (chs.revision, chs.short_id, _branch)
572 changesets_group[0].append((chs.raw_id, n_desc,))
572 changesets_group[0].append((chs.raw_id, n_desc,))
573 hist_l.append(changesets_group)
573 hist_l.append(changesets_group)
574
574
575 for name, chs in c.rhodecode_repo.branches.items():
575 for name, chs in c.rhodecode_repo.branches.items():
576 branches_group[0].append((chs, name),)
576 branches_group[0].append((chs, name),)
577 hist_l.append(branches_group)
577 hist_l.append(branches_group)
578
578
579 for name, chs in c.rhodecode_repo.tags.items():
579 for name, chs in c.rhodecode_repo.tags.items():
580 tags_group[0].append((chs, name),)
580 tags_group[0].append((chs, name),)
581 hist_l.append(tags_group)
581 hist_l.append(tags_group)
582
582
583 return hist_l, changesets
583 return hist_l, changesets
584
584
585 @LoginRequired()
585 @LoginRequired()
586 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
586 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
587 'repository.admin')
587 'repository.admin')
588 @jsonify
588 @jsonify
589 def nodelist(self, repo_name, revision, f_path):
589 def nodelist(self, repo_name, revision, f_path):
590 if request.environ.get('HTTP_X_PARTIAL_XHR'):
590 if request.environ.get('HTTP_X_PARTIAL_XHR'):
591 cs = self.__get_cs_or_redirect(revision, repo_name)
591 cs = self.__get_cs_or_redirect(revision, repo_name)
592 _d, _f = ScmModel().get_nodes(repo_name, cs.raw_id, f_path,
592 _d, _f = ScmModel().get_nodes(repo_name, cs.raw_id, f_path,
593 flat=False)
593 flat=False)
594 return {'nodes': _d + _f}
594 return {'nodes': _d + _f}
General Comments 0
You need to be logged in to leave comments. Login now