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