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