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