##// END OF EJS Templates
2way compare: fixed missing "<" escape code
marcink -
r4060:ba7b8dfa default
parent child Browse files
Show More
@@ -1,732 +1,732 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 import shutil
30 import shutil
31
31
32 from pylons import request, response, tmpl_context as c, url
32 from pylons import request, response, tmpl_context as c, url
33 from pylons.i18n.translation import _
33 from pylons.i18n.translation import _
34 from pylons.controllers.util import redirect
34 from pylons.controllers.util import redirect
35 from rhodecode.lib.utils import jsonify, action_logger
35 from rhodecode.lib.utils import jsonify, action_logger
36
36
37 from rhodecode.lib import diffs
37 from rhodecode.lib import diffs
38 from rhodecode.lib import helpers as h
38 from rhodecode.lib import helpers as h
39
39
40 from rhodecode.lib.compat import OrderedDict, json
40 from rhodecode.lib.compat import OrderedDict, json
41 from rhodecode.lib.utils2 import convert_line_endings, detect_mode, safe_str,\
41 from rhodecode.lib.utils2 import convert_line_endings, detect_mode, safe_str,\
42 str2bool
42 str2bool
43 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
43 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
44 from rhodecode.lib.base import BaseRepoController, render
44 from rhodecode.lib.base import BaseRepoController, render
45 from rhodecode.lib.vcs.backends.base import EmptyChangeset
45 from rhodecode.lib.vcs.backends.base import EmptyChangeset
46 from rhodecode.lib.vcs.conf import settings
46 from rhodecode.lib.vcs.conf import settings
47 from rhodecode.lib.vcs.exceptions import RepositoryError, \
47 from rhodecode.lib.vcs.exceptions import RepositoryError, \
48 ChangesetDoesNotExistError, EmptyRepositoryError, \
48 ChangesetDoesNotExistError, EmptyRepositoryError, \
49 ImproperArchiveTypeError, VCSError, NodeAlreadyExistsError,\
49 ImproperArchiveTypeError, VCSError, NodeAlreadyExistsError,\
50 NodeDoesNotExistError, ChangesetError, NodeError
50 NodeDoesNotExistError, ChangesetError, NodeError
51 from rhodecode.lib.vcs.nodes import FileNode
51 from rhodecode.lib.vcs.nodes import FileNode
52
52
53 from rhodecode.model.repo import RepoModel
53 from rhodecode.model.repo import RepoModel
54 from rhodecode.model.scm import ScmModel
54 from rhodecode.model.scm import ScmModel
55 from rhodecode.model.db import Repository
55 from rhodecode.model.db import Repository
56
56
57 from rhodecode.controllers.changeset import anchor_url, _ignorews_url,\
57 from rhodecode.controllers.changeset import anchor_url, _ignorews_url,\
58 _context_url, get_line_ctx, get_ignore_ws
58 _context_url, get_line_ctx, get_ignore_ws
59 from webob.exc import HTTPNotFound
59 from webob.exc import HTTPNotFound
60 from rhodecode.lib.exceptions import NonRelativePathError
60 from rhodecode.lib.exceptions import NonRelativePathError
61
61
62
62
63 log = logging.getLogger(__name__)
63 log = logging.getLogger(__name__)
64
64
65
65
66 class FilesController(BaseRepoController):
66 class FilesController(BaseRepoController):
67
67
68 def __before__(self):
68 def __before__(self):
69 super(FilesController, self).__before__()
69 super(FilesController, self).__before__()
70 c.cut_off_limit = self.cut_off_limit
70 c.cut_off_limit = self.cut_off_limit
71
71
72 def __get_cs_or_redirect(self, rev, repo_name, redirect_after=True):
72 def __get_cs_or_redirect(self, rev, repo_name, redirect_after=True):
73 """
73 """
74 Safe way to get changeset if error occur it redirects to tip with
74 Safe way to get changeset if error occur it redirects to tip with
75 proper message
75 proper message
76
76
77 :param rev: revision to fetch
77 :param rev: revision to fetch
78 :param repo_name: repo name to redirect after
78 :param repo_name: repo name to redirect after
79 """
79 """
80
80
81 try:
81 try:
82 return c.rhodecode_repo.get_changeset(rev)
82 return c.rhodecode_repo.get_changeset(rev)
83 except EmptyRepositoryError, e:
83 except EmptyRepositoryError, e:
84 if not redirect_after:
84 if not redirect_after:
85 return None
85 return None
86 url_ = url('files_add_home',
86 url_ = url('files_add_home',
87 repo_name=c.repo_name,
87 repo_name=c.repo_name,
88 revision=0, f_path='')
88 revision=0, f_path='')
89 add_new = h.link_to(_('Click here to add new file'), url_)
89 add_new = h.link_to(_('Click here to add new file'), url_)
90 h.flash(h.literal(_('There are no files yet %s') % add_new),
90 h.flash(h.literal(_('There are no files yet %s') % add_new),
91 category='warning')
91 category='warning')
92 redirect(h.url('summary_home', repo_name=repo_name))
92 redirect(h.url('summary_home', repo_name=repo_name))
93
93
94 except RepositoryError, e: # including ChangesetDoesNotExistError
94 except RepositoryError, e: # including ChangesetDoesNotExistError
95 h.flash(str(e), category='error')
95 h.flash(str(e), category='error')
96 raise HTTPNotFound()
96 raise HTTPNotFound()
97
97
98 def __get_filenode_or_redirect(self, repo_name, cs, path):
98 def __get_filenode_or_redirect(self, repo_name, cs, path):
99 """
99 """
100 Returns file_node, if error occurs or given path is directory,
100 Returns file_node, if error occurs or given path is directory,
101 it'll redirect to top level path
101 it'll redirect to top level path
102
102
103 :param repo_name: repo_name
103 :param repo_name: repo_name
104 :param cs: given changeset
104 :param cs: given changeset
105 :param path: path to lookup
105 :param path: path to lookup
106 """
106 """
107
107
108 try:
108 try:
109 file_node = cs.get_node(path)
109 file_node = cs.get_node(path)
110 if file_node.is_dir():
110 if file_node.is_dir():
111 raise RepositoryError('given path is a directory')
111 raise RepositoryError('given path is a directory')
112 except RepositoryError, e:
112 except RepositoryError, e:
113 h.flash(str(e), category='error')
113 h.flash(str(e), category='error')
114 raise HTTPNotFound()
114 raise HTTPNotFound()
115
115
116 return file_node
116 return file_node
117
117
118 @LoginRequired()
118 @LoginRequired()
119 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
119 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
120 'repository.admin')
120 'repository.admin')
121 def index(self, repo_name, revision, f_path, annotate=False):
121 def index(self, repo_name, revision, f_path, annotate=False):
122 # redirect to given revision from form if given
122 # redirect to given revision from form if given
123 post_revision = request.POST.get('at_rev', None)
123 post_revision = request.POST.get('at_rev', None)
124 if post_revision:
124 if post_revision:
125 cs = self.__get_cs_or_redirect(post_revision, repo_name)
125 cs = self.__get_cs_or_redirect(post_revision, repo_name)
126
126
127 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
127 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
128 c.branch = request.GET.get('branch', None)
128 c.branch = request.GET.get('branch', None)
129 c.f_path = f_path
129 c.f_path = f_path
130 c.annotate = annotate
130 c.annotate = annotate
131 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
131 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
132 cur_rev = c.changeset.revision
132 cur_rev = c.changeset.revision
133
133
134 # prev link
134 # prev link
135 try:
135 try:
136 prev_rev = c.rhodecode_repo.get_changeset(cur_rev).prev(c.branch)
136 prev_rev = c.rhodecode_repo.get_changeset(cur_rev).prev(c.branch)
137 c.url_prev = url('files_home', repo_name=c.repo_name,
137 c.url_prev = url('files_home', repo_name=c.repo_name,
138 revision=prev_rev.raw_id, f_path=f_path)
138 revision=prev_rev.raw_id, f_path=f_path)
139 if c.branch:
139 if c.branch:
140 c.url_prev += '?branch=%s' % c.branch
140 c.url_prev += '?branch=%s' % c.branch
141 except (ChangesetDoesNotExistError, VCSError):
141 except (ChangesetDoesNotExistError, VCSError):
142 c.url_prev = '#'
142 c.url_prev = '#'
143
143
144 # next link
144 # next link
145 try:
145 try:
146 next_rev = c.rhodecode_repo.get_changeset(cur_rev).next(c.branch)
146 next_rev = c.rhodecode_repo.get_changeset(cur_rev).next(c.branch)
147 c.url_next = url('files_home', repo_name=c.repo_name,
147 c.url_next = url('files_home', repo_name=c.repo_name,
148 revision=next_rev.raw_id, f_path=f_path)
148 revision=next_rev.raw_id, f_path=f_path)
149 if c.branch:
149 if c.branch:
150 c.url_next += '?branch=%s' % c.branch
150 c.url_next += '?branch=%s' % c.branch
151 except (ChangesetDoesNotExistError, VCSError):
151 except (ChangesetDoesNotExistError, VCSError):
152 c.url_next = '#'
152 c.url_next = '#'
153
153
154 # files or dirs
154 # files or dirs
155 try:
155 try:
156 c.file = c.changeset.get_node(f_path)
156 c.file = c.changeset.get_node(f_path)
157
157
158 if c.file.is_file():
158 if c.file.is_file():
159 c.load_full_history = False
159 c.load_full_history = False
160 file_last_cs = c.file.last_changeset
160 file_last_cs = c.file.last_changeset
161 c.file_changeset = (c.changeset
161 c.file_changeset = (c.changeset
162 if c.changeset.revision < file_last_cs.revision
162 if c.changeset.revision < file_last_cs.revision
163 else file_last_cs)
163 else file_last_cs)
164 #determine if we're on branch head
164 #determine if we're on branch head
165 _branches = c.rhodecode_repo.branches
165 _branches = c.rhodecode_repo.branches
166 c.on_branch_head = revision in _branches.keys() + _branches.values()
166 c.on_branch_head = revision in _branches.keys() + _branches.values()
167 _hist = []
167 _hist = []
168 c.file_history = []
168 c.file_history = []
169 if c.load_full_history:
169 if c.load_full_history:
170 c.file_history, _hist = self._get_node_history(c.changeset, f_path)
170 c.file_history, _hist = self._get_node_history(c.changeset, f_path)
171
171
172 c.authors = []
172 c.authors = []
173 for a in set([x.author for x in _hist]):
173 for a in set([x.author for x in _hist]):
174 c.authors.append((h.email(a), h.person(a)))
174 c.authors.append((h.email(a), h.person(a)))
175 else:
175 else:
176 c.authors = c.file_history = []
176 c.authors = c.file_history = []
177 except RepositoryError, e:
177 except RepositoryError, e:
178 h.flash(str(e), category='error')
178 h.flash(str(e), category='error')
179 raise HTTPNotFound()
179 raise HTTPNotFound()
180
180
181 if request.environ.get('HTTP_X_PARTIAL_XHR'):
181 if request.environ.get('HTTP_X_PARTIAL_XHR'):
182 return render('files/files_ypjax.html')
182 return render('files/files_ypjax.html')
183
183
184 return render('files/files.html')
184 return render('files/files.html')
185
185
186 @LoginRequired()
186 @LoginRequired()
187 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
187 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
188 'repository.admin')
188 'repository.admin')
189 def history(self, repo_name, revision, f_path, annotate=False):
189 def history(self, repo_name, revision, f_path, annotate=False):
190 if request.environ.get('HTTP_X_PARTIAL_XHR'):
190 if request.environ.get('HTTP_X_PARTIAL_XHR'):
191 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
191 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
192 c.f_path = f_path
192 c.f_path = f_path
193 c.annotate = annotate
193 c.annotate = annotate
194 c.file = c.changeset.get_node(f_path)
194 c.file = c.changeset.get_node(f_path)
195 if c.file.is_file():
195 if c.file.is_file():
196 file_last_cs = c.file.last_changeset
196 file_last_cs = c.file.last_changeset
197 c.file_changeset = (c.changeset
197 c.file_changeset = (c.changeset
198 if c.changeset.revision < file_last_cs.revision
198 if c.changeset.revision < file_last_cs.revision
199 else file_last_cs)
199 else file_last_cs)
200 c.file_history, _hist = self._get_node_history(c.changeset, f_path)
200 c.file_history, _hist = self._get_node_history(c.changeset, f_path)
201 c.authors = []
201 c.authors = []
202 for a in set([x.author for x in _hist]):
202 for a in set([x.author for x in _hist]):
203 c.authors.append((h.email(a), h.person(a)))
203 c.authors.append((h.email(a), h.person(a)))
204 return render('files/files_history_box.html')
204 return render('files/files_history_box.html')
205
205
206 @LoginRequired()
206 @LoginRequired()
207 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
207 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
208 'repository.admin')
208 'repository.admin')
209 def rawfile(self, repo_name, revision, f_path):
209 def rawfile(self, repo_name, revision, f_path):
210 cs = self.__get_cs_or_redirect(revision, repo_name)
210 cs = self.__get_cs_or_redirect(revision, repo_name)
211 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
211 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
212
212
213 response.content_disposition = 'attachment; filename=%s' % \
213 response.content_disposition = 'attachment; filename=%s' % \
214 safe_str(f_path.split(Repository.url_sep())[-1])
214 safe_str(f_path.split(Repository.url_sep())[-1])
215
215
216 response.content_type = file_node.mimetype
216 response.content_type = file_node.mimetype
217 return file_node.content
217 return file_node.content
218
218
219 @LoginRequired()
219 @LoginRequired()
220 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
220 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
221 'repository.admin')
221 'repository.admin')
222 def raw(self, repo_name, revision, f_path):
222 def raw(self, repo_name, revision, f_path):
223 cs = self.__get_cs_or_redirect(revision, repo_name)
223 cs = self.__get_cs_or_redirect(revision, repo_name)
224 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
224 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
225
225
226 raw_mimetype_mapping = {
226 raw_mimetype_mapping = {
227 # map original mimetype to a mimetype used for "show as raw"
227 # map original mimetype to a mimetype used for "show as raw"
228 # you can also provide a content-disposition to override the
228 # you can also provide a content-disposition to override the
229 # default "attachment" disposition.
229 # default "attachment" disposition.
230 # orig_type: (new_type, new_dispo)
230 # orig_type: (new_type, new_dispo)
231
231
232 # show images inline:
232 # show images inline:
233 'image/x-icon': ('image/x-icon', 'inline'),
233 'image/x-icon': ('image/x-icon', 'inline'),
234 'image/png': ('image/png', 'inline'),
234 'image/png': ('image/png', 'inline'),
235 'image/gif': ('image/gif', 'inline'),
235 'image/gif': ('image/gif', 'inline'),
236 'image/jpeg': ('image/jpeg', 'inline'),
236 'image/jpeg': ('image/jpeg', 'inline'),
237 'image/svg+xml': ('image/svg+xml', 'inline'),
237 'image/svg+xml': ('image/svg+xml', 'inline'),
238 }
238 }
239
239
240 mimetype = file_node.mimetype
240 mimetype = file_node.mimetype
241 try:
241 try:
242 mimetype, dispo = raw_mimetype_mapping[mimetype]
242 mimetype, dispo = raw_mimetype_mapping[mimetype]
243 except KeyError:
243 except KeyError:
244 # we don't know anything special about this, handle it safely
244 # we don't know anything special about this, handle it safely
245 if file_node.is_binary:
245 if file_node.is_binary:
246 # do same as download raw for binary files
246 # do same as download raw for binary files
247 mimetype, dispo = 'application/octet-stream', 'attachment'
247 mimetype, dispo = 'application/octet-stream', 'attachment'
248 else:
248 else:
249 # do not just use the original mimetype, but force text/plain,
249 # do not just use the original mimetype, but force text/plain,
250 # otherwise it would serve text/html and that might be unsafe.
250 # otherwise it would serve text/html and that might be unsafe.
251 # Note: underlying vcs library fakes text/plain mimetype if the
251 # Note: underlying vcs library fakes text/plain mimetype if the
252 # mimetype can not be determined and it thinks it is not
252 # mimetype can not be determined and it thinks it is not
253 # binary.This might lead to erroneous text display in some
253 # binary.This might lead to erroneous text display in some
254 # cases, but helps in other cases, like with text files
254 # cases, but helps in other cases, like with text files
255 # without extension.
255 # without extension.
256 mimetype, dispo = 'text/plain', 'inline'
256 mimetype, dispo = 'text/plain', 'inline'
257
257
258 if dispo == 'attachment':
258 if dispo == 'attachment':
259 dispo = 'attachment; filename=%s' % \
259 dispo = 'attachment; filename=%s' % \
260 safe_str(f_path.split(os.sep)[-1])
260 safe_str(f_path.split(os.sep)[-1])
261
261
262 response.content_disposition = dispo
262 response.content_disposition = dispo
263 response.content_type = mimetype
263 response.content_type = mimetype
264 return file_node.content
264 return file_node.content
265
265
266 @LoginRequired()
266 @LoginRequired()
267 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
267 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
268 def edit(self, repo_name, revision, f_path):
268 def edit(self, repo_name, revision, f_path):
269 repo = c.rhodecode_db_repo
269 repo = c.rhodecode_db_repo
270 if repo.enable_locking and repo.locked[0]:
270 if repo.enable_locking and repo.locked[0]:
271 h.flash(_('This repository is has been locked by %s on %s')
271 h.flash(_('This repository is has been locked by %s on %s')
272 % (h.person_by_id(repo.locked[0]),
272 % (h.person_by_id(repo.locked[0]),
273 h.fmt_date(h.time_to_datetime(repo.locked[1]))),
273 h.fmt_date(h.time_to_datetime(repo.locked[1]))),
274 'warning')
274 'warning')
275 return redirect(h.url('files_home',
275 return redirect(h.url('files_home',
276 repo_name=repo_name, revision='tip'))
276 repo_name=repo_name, revision='tip'))
277
277
278 # check if revision is a branch identifier- basically we cannot
278 # check if revision is a branch identifier- basically we cannot
279 # create multiple heads via file editing
279 # create multiple heads via file editing
280 _branches = repo.scm_instance.branches
280 _branches = repo.scm_instance.branches
281 # check if revision is a branch name or branch hash
281 # check if revision is a branch name or branch hash
282 if revision not in _branches.keys() + _branches.values():
282 if revision not in _branches.keys() + _branches.values():
283 h.flash(_('You can only edit files with revision '
283 h.flash(_('You can only edit files with revision '
284 'being a valid branch '), category='warning')
284 'being a valid branch '), category='warning')
285 return redirect(h.url('files_home',
285 return redirect(h.url('files_home',
286 repo_name=repo_name, revision='tip',
286 repo_name=repo_name, revision='tip',
287 f_path=f_path))
287 f_path=f_path))
288
288
289 r_post = request.POST
289 r_post = request.POST
290
290
291 c.cs = self.__get_cs_or_redirect(revision, repo_name)
291 c.cs = self.__get_cs_or_redirect(revision, repo_name)
292 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
292 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
293
293
294 if c.file.is_binary:
294 if c.file.is_binary:
295 return redirect(url('files_home', repo_name=c.repo_name,
295 return redirect(url('files_home', repo_name=c.repo_name,
296 revision=c.cs.raw_id, f_path=f_path))
296 revision=c.cs.raw_id, f_path=f_path))
297 c.default_message = _('Edited file %s via RhodeCode') % (f_path)
297 c.default_message = _('Edited file %s via RhodeCode') % (f_path)
298 c.f_path = f_path
298 c.f_path = f_path
299
299
300 if r_post:
300 if r_post:
301
301
302 old_content = c.file.content
302 old_content = c.file.content
303 sl = old_content.splitlines(1)
303 sl = old_content.splitlines(1)
304 first_line = sl[0] if sl else ''
304 first_line = sl[0] if sl else ''
305 # modes: 0 - Unix, 1 - Mac, 2 - DOS
305 # modes: 0 - Unix, 1 - Mac, 2 - DOS
306 mode = detect_mode(first_line, 0)
306 mode = detect_mode(first_line, 0)
307 content = convert_line_endings(r_post.get('content', ''), mode)
307 content = convert_line_endings(r_post.get('content', ''), mode)
308
308
309 message = r_post.get('message') or c.default_message
309 message = r_post.get('message') or c.default_message
310 author = self.rhodecode_user.full_contact
310 author = self.rhodecode_user.full_contact
311
311
312 if content == old_content:
312 if content == old_content:
313 h.flash(_('No changes'), category='warning')
313 h.flash(_('No changes'), category='warning')
314 return redirect(url('changeset_home', repo_name=c.repo_name,
314 return redirect(url('changeset_home', repo_name=c.repo_name,
315 revision='tip'))
315 revision='tip'))
316 try:
316 try:
317 self.scm_model.commit_change(repo=c.rhodecode_repo,
317 self.scm_model.commit_change(repo=c.rhodecode_repo,
318 repo_name=repo_name, cs=c.cs,
318 repo_name=repo_name, cs=c.cs,
319 user=self.rhodecode_user.user_id,
319 user=self.rhodecode_user.user_id,
320 author=author, message=message,
320 author=author, message=message,
321 content=content, f_path=f_path)
321 content=content, f_path=f_path)
322 h.flash(_('Successfully committed to %s') % f_path,
322 h.flash(_('Successfully committed to %s') % f_path,
323 category='success')
323 category='success')
324
324
325 except Exception:
325 except Exception:
326 log.error(traceback.format_exc())
326 log.error(traceback.format_exc())
327 h.flash(_('Error occurred during commit'), category='error')
327 h.flash(_('Error occurred during commit'), category='error')
328 return redirect(url('changeset_home',
328 return redirect(url('changeset_home',
329 repo_name=c.repo_name, revision='tip'))
329 repo_name=c.repo_name, revision='tip'))
330
330
331 return render('files/files_edit.html')
331 return render('files/files_edit.html')
332
332
333 @LoginRequired()
333 @LoginRequired()
334 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
334 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
335 def add(self, repo_name, revision, f_path):
335 def add(self, repo_name, revision, f_path):
336
336
337 repo = Repository.get_by_repo_name(repo_name)
337 repo = Repository.get_by_repo_name(repo_name)
338 if repo.enable_locking and repo.locked[0]:
338 if repo.enable_locking and repo.locked[0]:
339 h.flash(_('This repository is has been locked by %s on %s')
339 h.flash(_('This repository is has been locked by %s on %s')
340 % (h.person_by_id(repo.locked[0]),
340 % (h.person_by_id(repo.locked[0]),
341 h.fmt_date(h.time_to_datetime(repo.locked[1]))),
341 h.fmt_date(h.time_to_datetime(repo.locked[1]))),
342 'warning')
342 'warning')
343 return redirect(h.url('files_home',
343 return redirect(h.url('files_home',
344 repo_name=repo_name, revision='tip'))
344 repo_name=repo_name, revision='tip'))
345
345
346 r_post = request.POST
346 r_post = request.POST
347 c.cs = self.__get_cs_or_redirect(revision, repo_name,
347 c.cs = self.__get_cs_or_redirect(revision, repo_name,
348 redirect_after=False)
348 redirect_after=False)
349 if c.cs is None:
349 if c.cs is None:
350 c.cs = EmptyChangeset(alias=c.rhodecode_repo.alias)
350 c.cs = EmptyChangeset(alias=c.rhodecode_repo.alias)
351 c.default_message = (_('Added file via RhodeCode'))
351 c.default_message = (_('Added file via RhodeCode'))
352 c.f_path = f_path
352 c.f_path = f_path
353
353
354 if r_post:
354 if r_post:
355 unix_mode = 0
355 unix_mode = 0
356 content = convert_line_endings(r_post.get('content', ''), unix_mode)
356 content = convert_line_endings(r_post.get('content', ''), unix_mode)
357
357
358 message = r_post.get('message') or c.default_message
358 message = r_post.get('message') or c.default_message
359 filename = r_post.get('filename')
359 filename = r_post.get('filename')
360 location = r_post.get('location', '')
360 location = r_post.get('location', '')
361 file_obj = r_post.get('upload_file', None)
361 file_obj = r_post.get('upload_file', None)
362
362
363 if file_obj is not None and hasattr(file_obj, 'filename'):
363 if file_obj is not None and hasattr(file_obj, 'filename'):
364 filename = file_obj.filename
364 filename = file_obj.filename
365 content = file_obj.file
365 content = file_obj.file
366
366
367 if not content:
367 if not content:
368 h.flash(_('No content'), category='warning')
368 h.flash(_('No content'), category='warning')
369 return redirect(url('changeset_home', repo_name=c.repo_name,
369 return redirect(url('changeset_home', repo_name=c.repo_name,
370 revision='tip'))
370 revision='tip'))
371 if not filename:
371 if not filename:
372 h.flash(_('No filename'), category='warning')
372 h.flash(_('No filename'), category='warning')
373 return redirect(url('changeset_home', repo_name=c.repo_name,
373 return redirect(url('changeset_home', repo_name=c.repo_name,
374 revision='tip'))
374 revision='tip'))
375 #strip all crap out of file, just leave the basename
375 #strip all crap out of file, just leave the basename
376 filename = os.path.basename(filename)
376 filename = os.path.basename(filename)
377 node_path = os.path.join(location, filename)
377 node_path = os.path.join(location, filename)
378 author = self.rhodecode_user.full_contact
378 author = self.rhodecode_user.full_contact
379
379
380 try:
380 try:
381 nodes = {
381 nodes = {
382 node_path: {
382 node_path: {
383 'content': content
383 'content': content
384 }
384 }
385 }
385 }
386 self.scm_model.create_nodes(
386 self.scm_model.create_nodes(
387 user=c.rhodecode_user.user_id, repo=c.rhodecode_db_repo,
387 user=c.rhodecode_user.user_id, repo=c.rhodecode_db_repo,
388 message=message,
388 message=message,
389 nodes=nodes,
389 nodes=nodes,
390 parent_cs=c.cs,
390 parent_cs=c.cs,
391 author=author,
391 author=author,
392 )
392 )
393
393
394 h.flash(_('Successfully committed to %s') % node_path,
394 h.flash(_('Successfully committed to %s') % node_path,
395 category='success')
395 category='success')
396 except NonRelativePathError, e:
396 except NonRelativePathError, e:
397 h.flash(_('Location must be relative path and must not '
397 h.flash(_('Location must be relative path and must not '
398 'contain .. in path'), category='warning')
398 'contain .. in path'), category='warning')
399 return redirect(url('changeset_home', repo_name=c.repo_name,
399 return redirect(url('changeset_home', repo_name=c.repo_name,
400 revision='tip'))
400 revision='tip'))
401 except (NodeError, NodeAlreadyExistsError), e:
401 except (NodeError, NodeAlreadyExistsError), e:
402 h.flash(_(e), category='error')
402 h.flash(_(e), category='error')
403 except Exception:
403 except Exception:
404 log.error(traceback.format_exc())
404 log.error(traceback.format_exc())
405 h.flash(_('Error occurred during commit'), category='error')
405 h.flash(_('Error occurred during commit'), category='error')
406 return redirect(url('changeset_home',
406 return redirect(url('changeset_home',
407 repo_name=c.repo_name, revision='tip'))
407 repo_name=c.repo_name, revision='tip'))
408
408
409 return render('files/files_add.html')
409 return render('files/files_add.html')
410
410
411 @LoginRequired()
411 @LoginRequired()
412 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
412 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
413 'repository.admin')
413 'repository.admin')
414 def archivefile(self, repo_name, fname):
414 def archivefile(self, repo_name, fname):
415
415
416 fileformat = None
416 fileformat = None
417 revision = None
417 revision = None
418 ext = None
418 ext = None
419 subrepos = request.GET.get('subrepos') == 'true'
419 subrepos = request.GET.get('subrepos') == 'true'
420
420
421 for a_type, ext_data in settings.ARCHIVE_SPECS.items():
421 for a_type, ext_data in settings.ARCHIVE_SPECS.items():
422 archive_spec = fname.split(ext_data[1])
422 archive_spec = fname.split(ext_data[1])
423 if len(archive_spec) == 2 and archive_spec[1] == '':
423 if len(archive_spec) == 2 and archive_spec[1] == '':
424 fileformat = a_type or ext_data[1]
424 fileformat = a_type or ext_data[1]
425 revision = archive_spec[0]
425 revision = archive_spec[0]
426 ext = ext_data[1]
426 ext = ext_data[1]
427
427
428 try:
428 try:
429 dbrepo = RepoModel().get_by_repo_name(repo_name)
429 dbrepo = RepoModel().get_by_repo_name(repo_name)
430 if not dbrepo.enable_downloads:
430 if not dbrepo.enable_downloads:
431 return _('Downloads disabled')
431 return _('Downloads disabled')
432
432
433 if c.rhodecode_repo.alias == 'hg':
433 if c.rhodecode_repo.alias == 'hg':
434 # patch and reset hooks section of UI config to not run any
434 # patch and reset hooks section of UI config to not run any
435 # hooks on fetching archives with subrepos
435 # hooks on fetching archives with subrepos
436 for k, v in c.rhodecode_repo._repo.ui.configitems('hooks'):
436 for k, v in c.rhodecode_repo._repo.ui.configitems('hooks'):
437 c.rhodecode_repo._repo.ui.setconfig('hooks', k, None)
437 c.rhodecode_repo._repo.ui.setconfig('hooks', k, None)
438
438
439 cs = c.rhodecode_repo.get_changeset(revision)
439 cs = c.rhodecode_repo.get_changeset(revision)
440 content_type = settings.ARCHIVE_SPECS[fileformat][0]
440 content_type = settings.ARCHIVE_SPECS[fileformat][0]
441 except ChangesetDoesNotExistError:
441 except ChangesetDoesNotExistError:
442 return _('Unknown revision %s') % revision
442 return _('Unknown revision %s') % revision
443 except EmptyRepositoryError:
443 except EmptyRepositoryError:
444 return _('Empty repository')
444 return _('Empty repository')
445 except (ImproperArchiveTypeError, KeyError):
445 except (ImproperArchiveTypeError, KeyError):
446 return _('Unknown archive type')
446 return _('Unknown archive type')
447 # archive cache
447 # archive cache
448 from rhodecode import CONFIG
448 from rhodecode import CONFIG
449 rev_name = cs.raw_id[:12]
449 rev_name = cs.raw_id[:12]
450 archive_name = '%s-%s%s' % (safe_str(repo_name.replace('/', '_')),
450 archive_name = '%s-%s%s' % (safe_str(repo_name.replace('/', '_')),
451 safe_str(rev_name), ext)
451 safe_str(rev_name), ext)
452
452
453 use_cached_archive = False # defines if we use cached version of archive
453 use_cached_archive = False # defines if we use cached version of archive
454 archive_cache_enabled = CONFIG.get('archive_cache_dir')
454 archive_cache_enabled = CONFIG.get('archive_cache_dir')
455 if not subrepos and archive_cache_enabled:
455 if not subrepos and archive_cache_enabled:
456 #check if we it's ok to write
456 #check if we it's ok to write
457 if not os.path.isdir(CONFIG['archive_cache_dir']):
457 if not os.path.isdir(CONFIG['archive_cache_dir']):
458 os.makedirs(CONFIG['archive_cache_dir'])
458 os.makedirs(CONFIG['archive_cache_dir'])
459 cached_archive_path = os.path.join(CONFIG['archive_cache_dir'], archive_name)
459 cached_archive_path = os.path.join(CONFIG['archive_cache_dir'], archive_name)
460 if os.path.isfile(cached_archive_path):
460 if os.path.isfile(cached_archive_path):
461 log.debug('Found cached archive in %s' % cached_archive_path)
461 log.debug('Found cached archive in %s' % cached_archive_path)
462 fd, archive = None, cached_archive_path
462 fd, archive = None, cached_archive_path
463 use_cached_archive = True
463 use_cached_archive = True
464 else:
464 else:
465 log.debug('Archive %s is not yet cached' % (archive_name))
465 log.debug('Archive %s is not yet cached' % (archive_name))
466
466
467 if not use_cached_archive:
467 if not use_cached_archive:
468 #generate new archive
468 #generate new archive
469 try:
469 try:
470 fd, archive = tempfile.mkstemp()
470 fd, archive = tempfile.mkstemp()
471 t = open(archive, 'wb')
471 t = open(archive, 'wb')
472 log.debug('Creating new temp archive in %s' % archive)
472 log.debug('Creating new temp archive in %s' % archive)
473 cs.fill_archive(stream=t, kind=fileformat, subrepos=subrepos)
473 cs.fill_archive(stream=t, kind=fileformat, subrepos=subrepos)
474 if archive_cache_enabled:
474 if archive_cache_enabled:
475 #if we generated the archive and use cache rename that
475 #if we generated the archive and use cache rename that
476 log.debug('Storing new archive in %s' % cached_archive_path)
476 log.debug('Storing new archive in %s' % cached_archive_path)
477 shutil.move(archive, cached_archive_path)
477 shutil.move(archive, cached_archive_path)
478 archive = cached_archive_path
478 archive = cached_archive_path
479 finally:
479 finally:
480 t.close()
480 t.close()
481
481
482 def get_chunked_archive(archive):
482 def get_chunked_archive(archive):
483 stream = open(archive, 'rb')
483 stream = open(archive, 'rb')
484 while True:
484 while True:
485 data = stream.read(16 * 1024)
485 data = stream.read(16 * 1024)
486 if not data:
486 if not data:
487 stream.close()
487 stream.close()
488 if fd: # fd means we used temporary file
488 if fd: # fd means we used temporary file
489 os.close(fd)
489 os.close(fd)
490 if not archive_cache_enabled:
490 if not archive_cache_enabled:
491 log.debug('Destroing temp archive %s' % archive)
491 log.debug('Destroing temp archive %s' % archive)
492 os.remove(archive)
492 os.remove(archive)
493 break
493 break
494 yield data
494 yield data
495 # store download action
495 # store download action
496 action_logger(user=c.rhodecode_user,
496 action_logger(user=c.rhodecode_user,
497 action='user_downloaded_archive:%s' % (archive_name),
497 action='user_downloaded_archive:%s' % (archive_name),
498 repo=repo_name, ipaddr=self.ip_addr, commit=True)
498 repo=repo_name, ipaddr=self.ip_addr, commit=True)
499 response.content_disposition = str('attachment; filename=%s' % (archive_name))
499 response.content_disposition = str('attachment; filename=%s' % (archive_name))
500 response.content_type = str(content_type)
500 response.content_type = str(content_type)
501 return get_chunked_archive(archive)
501 return get_chunked_archive(archive)
502
502
503 @LoginRequired()
503 @LoginRequired()
504 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
504 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
505 'repository.admin')
505 'repository.admin')
506 def diff(self, repo_name, f_path):
506 def diff(self, repo_name, f_path):
507 ignore_whitespace = request.GET.get('ignorews') == '1'
507 ignore_whitespace = request.GET.get('ignorews') == '1'
508 line_context = request.GET.get('context', 3)
508 line_context = request.GET.get('context', 3)
509 diff1 = request.GET.get('diff1', '')
509 diff1 = request.GET.get('diff1', '')
510 diff2 = request.GET.get('diff2', '')
510 diff2 = request.GET.get('diff2', '')
511 c.action = request.GET.get('diff')
511 c.action = request.GET.get('diff')
512 c.no_changes = diff1 == diff2
512 c.no_changes = diff1 == diff2
513 c.f_path = f_path
513 c.f_path = f_path
514 c.big_diff = False
514 c.big_diff = False
515 c.anchor_url = anchor_url
515 c.anchor_url = anchor_url
516 c.ignorews_url = _ignorews_url
516 c.ignorews_url = _ignorews_url
517 c.context_url = _context_url
517 c.context_url = _context_url
518 c.changes = OrderedDict()
518 c.changes = OrderedDict()
519 c.changes[diff2] = []
519 c.changes[diff2] = []
520
520
521 #special case if we want a show rev only, it's impl here
521 #special case if we want a show rev only, it's impl here
522 #to reduce JS and callbacks
522 #to reduce JS and callbacks
523
523
524 if request.GET.get('show_rev'):
524 if request.GET.get('show_rev'):
525 if str2bool(request.GET.get('annotate', 'False')):
525 if str2bool(request.GET.get('annotate', 'False')):
526 _url = url('files_annotate_home', repo_name=c.repo_name,
526 _url = url('files_annotate_home', repo_name=c.repo_name,
527 revision=diff1, f_path=c.f_path)
527 revision=diff1, f_path=c.f_path)
528 else:
528 else:
529 _url = url('files_home', repo_name=c.repo_name,
529 _url = url('files_home', repo_name=c.repo_name,
530 revision=diff1, f_path=c.f_path)
530 revision=diff1, f_path=c.f_path)
531
531
532 return redirect(_url)
532 return redirect(_url)
533 try:
533 try:
534 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
534 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
535 c.changeset_1 = c.rhodecode_repo.get_changeset(diff1)
535 c.changeset_1 = c.rhodecode_repo.get_changeset(diff1)
536 try:
536 try:
537 node1 = c.changeset_1.get_node(f_path)
537 node1 = c.changeset_1.get_node(f_path)
538 if node1.is_dir():
538 if node1.is_dir():
539 raise NodeError('%s path is a %s not a file'
539 raise NodeError('%s path is a %s not a file'
540 % (node1, type(node1)))
540 % (node1, type(node1)))
541 except NodeDoesNotExistError:
541 except NodeDoesNotExistError:
542 c.changeset_1 = EmptyChangeset(cs=diff1,
542 c.changeset_1 = EmptyChangeset(cs=diff1,
543 revision=c.changeset_1.revision,
543 revision=c.changeset_1.revision,
544 repo=c.rhodecode_repo)
544 repo=c.rhodecode_repo)
545 node1 = FileNode(f_path, '', changeset=c.changeset_1)
545 node1 = FileNode(f_path, '', changeset=c.changeset_1)
546 else:
546 else:
547 c.changeset_1 = EmptyChangeset(repo=c.rhodecode_repo)
547 c.changeset_1 = EmptyChangeset(repo=c.rhodecode_repo)
548 node1 = FileNode(f_path, '', changeset=c.changeset_1)
548 node1 = FileNode(f_path, '', changeset=c.changeset_1)
549
549
550 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
550 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
551 c.changeset_2 = c.rhodecode_repo.get_changeset(diff2)
551 c.changeset_2 = c.rhodecode_repo.get_changeset(diff2)
552 try:
552 try:
553 node2 = c.changeset_2.get_node(f_path)
553 node2 = c.changeset_2.get_node(f_path)
554 if node2.is_dir():
554 if node2.is_dir():
555 raise NodeError('%s path is a %s not a file'
555 raise NodeError('%s path is a %s not a file'
556 % (node2, type(node2)))
556 % (node2, type(node2)))
557 except NodeDoesNotExistError:
557 except NodeDoesNotExistError:
558 c.changeset_2 = EmptyChangeset(cs=diff2,
558 c.changeset_2 = EmptyChangeset(cs=diff2,
559 revision=c.changeset_2.revision,
559 revision=c.changeset_2.revision,
560 repo=c.rhodecode_repo)
560 repo=c.rhodecode_repo)
561 node2 = FileNode(f_path, '', changeset=c.changeset_2)
561 node2 = FileNode(f_path, '', changeset=c.changeset_2)
562 else:
562 else:
563 c.changeset_2 = EmptyChangeset(repo=c.rhodecode_repo)
563 c.changeset_2 = EmptyChangeset(repo=c.rhodecode_repo)
564 node2 = FileNode(f_path, '', changeset=c.changeset_2)
564 node2 = FileNode(f_path, '', changeset=c.changeset_2)
565 except (RepositoryError, NodeError):
565 except (RepositoryError, NodeError):
566 log.error(traceback.format_exc())
566 log.error(traceback.format_exc())
567 return redirect(url('files_home', repo_name=c.repo_name,
567 return redirect(url('files_home', repo_name=c.repo_name,
568 f_path=f_path))
568 f_path=f_path))
569
569
570 if c.action == 'download':
570 if c.action == 'download':
571 _diff = diffs.get_gitdiff(node1, node2,
571 _diff = diffs.get_gitdiff(node1, node2,
572 ignore_whitespace=ignore_whitespace,
572 ignore_whitespace=ignore_whitespace,
573 context=line_context)
573 context=line_context)
574 diff = diffs.DiffProcessor(_diff, format='gitdiff')
574 diff = diffs.DiffProcessor(_diff, format='gitdiff')
575
575
576 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
576 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
577 response.content_type = 'text/plain'
577 response.content_type = 'text/plain'
578 response.content_disposition = (
578 response.content_disposition = (
579 'attachment; filename=%s' % diff_name
579 'attachment; filename=%s' % diff_name
580 )
580 )
581 return diff.as_raw()
581 return diff.as_raw()
582
582
583 elif c.action == 'raw':
583 elif c.action == 'raw':
584 _diff = diffs.get_gitdiff(node1, node2,
584 _diff = diffs.get_gitdiff(node1, node2,
585 ignore_whitespace=ignore_whitespace,
585 ignore_whitespace=ignore_whitespace,
586 context=line_context)
586 context=line_context)
587 diff = diffs.DiffProcessor(_diff, format='gitdiff')
587 diff = diffs.DiffProcessor(_diff, format='gitdiff')
588 response.content_type = 'text/plain'
588 response.content_type = 'text/plain'
589 return diff.as_raw()
589 return diff.as_raw()
590
590
591 else:
591 else:
592 fid = h.FID(diff2, node2.path)
592 fid = h.FID(diff2, node2.path)
593 line_context_lcl = get_line_ctx(fid, request.GET)
593 line_context_lcl = get_line_ctx(fid, request.GET)
594 ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
594 ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
595
595
596 lim = request.GET.get('fulldiff') or self.cut_off_limit
596 lim = request.GET.get('fulldiff') or self.cut_off_limit
597 _, cs1, cs2, diff, st = diffs.wrapped_diff(filenode_old=node1,
597 _, cs1, cs2, diff, st = diffs.wrapped_diff(filenode_old=node1,
598 filenode_new=node2,
598 filenode_new=node2,
599 cut_off_limit=lim,
599 cut_off_limit=lim,
600 ignore_whitespace=ign_whitespace_lcl,
600 ignore_whitespace=ign_whitespace_lcl,
601 line_context=line_context_lcl,
601 line_context=line_context_lcl,
602 enable_comments=False)
602 enable_comments=False)
603 op = ''
603 op = ''
604 filename = node1.path
604 filename = node1.path
605 cs_changes = {
605 cs_changes = {
606 'fid': [cs1, cs2, op, filename, diff, st]
606 'fid': [cs1, cs2, op, filename, diff, st]
607 }
607 }
608 c.changes = cs_changes
608 c.changes = cs_changes
609
609
610 return render('files/file_diff.html')
610 return render('files/file_diff.html')
611
611
612 @LoginRequired()
612 @LoginRequired()
613 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
613 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
614 'repository.admin')
614 'repository.admin')
615 def diff_2way(self, repo_name, f_path):
615 def diff_2way(self, repo_name, f_path):
616 diff1 = request.GET.get('diff1', '')
616 diff1 = request.GET.get('diff1', '')
617 diff2 = request.GET.get('diff2', '')
617 diff2 = request.GET.get('diff2', '')
618 try:
618 try:
619 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
619 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
620 c.changeset_1 = c.rhodecode_repo.get_changeset(diff1)
620 c.changeset_1 = c.rhodecode_repo.get_changeset(diff1)
621 try:
621 try:
622 node1 = c.changeset_1.get_node(f_path)
622 node1 = c.changeset_1.get_node(f_path)
623 if node1.is_dir():
623 if node1.is_dir():
624 raise NodeError('%s path is a %s not a file'
624 raise NodeError('%s path is a %s not a file'
625 % (node1, type(node1)))
625 % (node1, type(node1)))
626 except NodeDoesNotExistError:
626 except NodeDoesNotExistError:
627 c.changeset_1 = EmptyChangeset(cs=diff1,
627 c.changeset_1 = EmptyChangeset(cs=diff1,
628 revision=c.changeset_1.revision,
628 revision=c.changeset_1.revision,
629 repo=c.rhodecode_repo)
629 repo=c.rhodecode_repo)
630 node1 = FileNode(f_path, '', changeset=c.changeset_1)
630 node1 = FileNode(f_path, '', changeset=c.changeset_1)
631 else:
631 else:
632 c.changeset_1 = EmptyChangeset(repo=c.rhodecode_repo)
632 c.changeset_1 = EmptyChangeset(repo=c.rhodecode_repo)
633 node1 = FileNode(f_path, '', changeset=c.changeset_1)
633 node1 = FileNode(f_path, '', changeset=c.changeset_1)
634
634
635 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
635 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
636 c.changeset_2 = c.rhodecode_repo.get_changeset(diff2)
636 c.changeset_2 = c.rhodecode_repo.get_changeset(diff2)
637 try:
637 try:
638 node2 = c.changeset_2.get_node(f_path)
638 node2 = c.changeset_2.get_node(f_path)
639 if node2.is_dir():
639 if node2.is_dir():
640 raise NodeError('%s path is a %s not a file'
640 raise NodeError('%s path is a %s not a file'
641 % (node2, type(node2)))
641 % (node2, type(node2)))
642 except NodeDoesNotExistError:
642 except NodeDoesNotExistError:
643 c.changeset_2 = EmptyChangeset(cs=diff2,
643 c.changeset_2 = EmptyChangeset(cs=diff2,
644 revision=c.changeset_2.revision,
644 revision=c.changeset_2.revision,
645 repo=c.rhodecode_repo)
645 repo=c.rhodecode_repo)
646 node2 = FileNode(f_path, '', changeset=c.changeset_2)
646 node2 = FileNode(f_path, '', changeset=c.changeset_2)
647 else:
647 else:
648 c.changeset_2 = EmptyChangeset(repo=c.rhodecode_repo)
648 c.changeset_2 = EmptyChangeset(repo=c.rhodecode_repo)
649 node2 = FileNode(f_path, '', changeset=c.changeset_2)
649 node2 = FileNode(f_path, '', changeset=c.changeset_2)
650 except (RepositoryError, NodeError):
650 except (RepositoryError, NodeError):
651 log.error(traceback.format_exc())
651 log.error(traceback.format_exc())
652 return redirect(url('files_home', repo_name=c.repo_name,
652 return redirect(url('files_home', repo_name=c.repo_name,
653 f_path=f_path))
653 f_path=f_path))
654 if node2.is_binary:
654 if node2.is_binary:
655 node2_content = 'binary file'
655 node2_content = 'binary file'
656 else:
656 else:
657 node2_content = node2.content
657 node2_content = node2.content
658
658
659 if node1.is_binary:
659 if node1.is_binary:
660 node1_content = 'binary file'
660 node1_content = 'binary file'
661 else:
661 else:
662 node1_content = node1.content
662 node1_content = node1.content
663
663
664 html_escape_table = {
664 html_escape_table = {
665 "&": "\u0026",
665 "&": "\u0026",
666 '"': "\u0022",
666 '"': "\u0022",
667 "'": "\u0027",
667 "'": "\u0027",
668 ">": "\u003e",
668 ">": "\u003e",
669 "<": "\<",
669 "<": "\u003c",
670 '\\': "\u005c",
670 '\\': "\u005c",
671 '\n': '\\n'
671 '\n': '\\n'
672 }
672 }
673
673
674 c.orig1 = h.html_escape((node1_content), html_escape_table)
674 c.orig1 = h.html_escape((node1_content), html_escape_table)
675 c.orig2 = h.html_escape((node2_content), html_escape_table)
675 c.orig2 = h.html_escape((node2_content), html_escape_table)
676 c.node1 = node1
676 c.node1 = node1
677 c.node2 = node2
677 c.node2 = node2
678 c.cs1 = c.changeset_1
678 c.cs1 = c.changeset_1
679 c.cs2 = c.changeset_2
679 c.cs2 = c.changeset_2
680
680
681 return render('files/diff_2way.html')
681 return render('files/diff_2way.html')
682
682
683 def _get_node_history(self, cs, f_path, changesets=None):
683 def _get_node_history(self, cs, f_path, changesets=None):
684 """
684 """
685 get changesets history for given node
685 get changesets history for given node
686
686
687 :param cs: changeset to calculate history
687 :param cs: changeset to calculate history
688 :param f_path: path for node to calculate history for
688 :param f_path: path for node to calculate history for
689 :param changesets: if passed don't calculate history and take
689 :param changesets: if passed don't calculate history and take
690 changesets defined in this list
690 changesets defined in this list
691 """
691 """
692 # calculate history based on tip
692 # calculate history based on tip
693 tip_cs = c.rhodecode_repo.get_changeset()
693 tip_cs = c.rhodecode_repo.get_changeset()
694 if changesets is None:
694 if changesets is None:
695 try:
695 try:
696 changesets = tip_cs.get_file_history(f_path)
696 changesets = tip_cs.get_file_history(f_path)
697 except (NodeDoesNotExistError, ChangesetError):
697 except (NodeDoesNotExistError, ChangesetError):
698 #this node is not present at tip !
698 #this node is not present at tip !
699 changesets = cs.get_file_history(f_path)
699 changesets = cs.get_file_history(f_path)
700 hist_l = []
700 hist_l = []
701
701
702 changesets_group = ([], _("Changesets"))
702 changesets_group = ([], _("Changesets"))
703 branches_group = ([], _("Branches"))
703 branches_group = ([], _("Branches"))
704 tags_group = ([], _("Tags"))
704 tags_group = ([], _("Tags"))
705 _hg = cs.repository.alias == 'hg'
705 _hg = cs.repository.alias == 'hg'
706 for chs in changesets:
706 for chs in changesets:
707 #_branch = '(%s)' % chs.branch if _hg else ''
707 #_branch = '(%s)' % chs.branch if _hg else ''
708 _branch = chs.branch
708 _branch = chs.branch
709 n_desc = 'r%s:%s (%s)' % (chs.revision, chs.short_id, _branch)
709 n_desc = 'r%s:%s (%s)' % (chs.revision, chs.short_id, _branch)
710 changesets_group[0].append((chs.raw_id, n_desc,))
710 changesets_group[0].append((chs.raw_id, n_desc,))
711 hist_l.append(changesets_group)
711 hist_l.append(changesets_group)
712
712
713 for name, chs in c.rhodecode_repo.branches.items():
713 for name, chs in c.rhodecode_repo.branches.items():
714 branches_group[0].append((chs, name),)
714 branches_group[0].append((chs, name),)
715 hist_l.append(branches_group)
715 hist_l.append(branches_group)
716
716
717 for name, chs in c.rhodecode_repo.tags.items():
717 for name, chs in c.rhodecode_repo.tags.items():
718 tags_group[0].append((chs, name),)
718 tags_group[0].append((chs, name),)
719 hist_l.append(tags_group)
719 hist_l.append(tags_group)
720
720
721 return hist_l, changesets
721 return hist_l, changesets
722
722
723 @LoginRequired()
723 @LoginRequired()
724 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
724 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
725 'repository.admin')
725 'repository.admin')
726 @jsonify
726 @jsonify
727 def nodelist(self, repo_name, revision, f_path):
727 def nodelist(self, repo_name, revision, f_path):
728 if request.environ.get('HTTP_X_PARTIAL_XHR'):
728 if request.environ.get('HTTP_X_PARTIAL_XHR'):
729 cs = self.__get_cs_or_redirect(revision, repo_name)
729 cs = self.__get_cs_or_redirect(revision, repo_name)
730 _d, _f = ScmModel().get_nodes(repo_name, cs.raw_id, f_path,
730 _d, _f = ScmModel().get_nodes(repo_name, cs.raw_id, f_path,
731 flat=False)
731 flat=False)
732 return {'nodes': _d + _f}
732 return {'nodes': _d + _f}
General Comments 0
You need to be logged in to leave comments. Login now