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