##// END OF EJS Templates
- added branch to history list for file...
marcink -
r1983:8fa8d0d0 beta
parent child Browse files
Show More
@@ -1,492 +1,492 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
25
26 import os
26 import os
27 import logging
27 import logging
28 import traceback
28 import traceback
29
29
30 from pylons import request, response, tmpl_context as c, url
30 from pylons import request, response, tmpl_context as c, url
31 from pylons.i18n.translation import _
31 from pylons.i18n.translation import _
32 from pylons.controllers.util import redirect
32 from pylons.controllers.util import redirect
33 from pylons.decorators import jsonify
33 from pylons.decorators import jsonify
34
34
35 from vcs.conf import settings
35 from vcs.conf import settings
36 from vcs.exceptions import RepositoryError, ChangesetDoesNotExistError, \
36 from vcs.exceptions import RepositoryError, ChangesetDoesNotExistError, \
37 EmptyRepositoryError, ImproperArchiveTypeError, VCSError, \
37 EmptyRepositoryError, ImproperArchiveTypeError, VCSError, \
38 NodeAlreadyExistsError
38 NodeAlreadyExistsError
39 from vcs.nodes import FileNode
39 from vcs.nodes import FileNode
40
40
41 from rhodecode.lib.compat import OrderedDict
41 from rhodecode.lib.compat import OrderedDict
42 from rhodecode.lib import convert_line_endings, detect_mode, safe_str
42 from rhodecode.lib import convert_line_endings, detect_mode, safe_str
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.utils import EmptyChangeset
45 from rhodecode.lib.utils import EmptyChangeset
46 from rhodecode.lib import diffs
46 from rhodecode.lib import diffs
47 import rhodecode.lib.helpers as h
47 import rhodecode.lib.helpers as h
48 from rhodecode.model.repo import RepoModel
48 from rhodecode.model.repo import RepoModel
49 from rhodecode.controllers.changeset import anchor_url, _ignorews_url,\
49 from rhodecode.controllers.changeset import anchor_url, _ignorews_url,\
50 _context_url, get_line_ctx, get_ignore_ws
50 _context_url, get_line_ctx, get_ignore_ws
51 from rhodecode.lib.diffs import wrapped_diff
51 from rhodecode.lib.diffs import wrapped_diff
52 from rhodecode.model.scm import ScmModel
52 from rhodecode.model.scm import ScmModel
53
53
54 log = logging.getLogger(__name__)
54 log = logging.getLogger(__name__)
55
55
56
56
57 class FilesController(BaseRepoController):
57 class FilesController(BaseRepoController):
58
58
59 @LoginRequired()
59 @LoginRequired()
60 def __before__(self):
60 def __before__(self):
61 super(FilesController, self).__before__()
61 super(FilesController, self).__before__()
62 c.cut_off_limit = self.cut_off_limit
62 c.cut_off_limit = self.cut_off_limit
63
63
64 def __get_cs_or_redirect(self, rev, repo_name, redirect_after=True):
64 def __get_cs_or_redirect(self, rev, repo_name, redirect_after=True):
65 """
65 """
66 Safe way to get changeset if error occur it redirects to tip with
66 Safe way to get changeset if error occur it redirects to tip with
67 proper message
67 proper message
68
68
69 :param rev: revision to fetch
69 :param rev: revision to fetch
70 :param repo_name: repo name to redirect after
70 :param repo_name: repo name to redirect after
71 """
71 """
72
72
73 try:
73 try:
74 return c.rhodecode_repo.get_changeset(rev)
74 return c.rhodecode_repo.get_changeset(rev)
75 except EmptyRepositoryError, e:
75 except EmptyRepositoryError, e:
76 if not redirect_after:
76 if not redirect_after:
77 return None
77 return None
78 url_ = url('files_add_home',
78 url_ = url('files_add_home',
79 repo_name=c.repo_name,
79 repo_name=c.repo_name,
80 revision=0, f_path='')
80 revision=0, f_path='')
81 add_new = '<a href="%s">[%s]</a>' % (url_, _('add new'))
81 add_new = '<a href="%s">[%s]</a>' % (url_, _('add new'))
82 h.flash(h.literal(_('There are no files yet %s' % add_new)),
82 h.flash(h.literal(_('There are no files yet %s' % add_new)),
83 category='warning')
83 category='warning')
84 redirect(h.url('summary_home', repo_name=repo_name))
84 redirect(h.url('summary_home', repo_name=repo_name))
85
85
86 except RepositoryError, e:
86 except RepositoryError, e:
87 h.flash(str(e), category='warning')
87 h.flash(str(e), category='warning')
88 redirect(h.url('files_home', repo_name=repo_name, revision='tip'))
88 redirect(h.url('files_home', repo_name=repo_name, revision='tip'))
89
89
90 def __get_filenode_or_redirect(self, repo_name, cs, path):
90 def __get_filenode_or_redirect(self, repo_name, cs, path):
91 """
91 """
92 Returns file_node, if error occurs or given path is directory,
92 Returns file_node, if error occurs or given path is directory,
93 it'll redirect to top level path
93 it'll redirect to top level path
94
94
95 :param repo_name: repo_name
95 :param repo_name: repo_name
96 :param cs: given changeset
96 :param cs: given changeset
97 :param path: path to lookup
97 :param path: path to lookup
98 """
98 """
99
99
100 try:
100 try:
101 file_node = cs.get_node(path)
101 file_node = cs.get_node(path)
102 if file_node.is_dir():
102 if file_node.is_dir():
103 raise RepositoryError('given path is a directory')
103 raise RepositoryError('given path is a directory')
104 except RepositoryError, e:
104 except RepositoryError, e:
105 h.flash(str(e), category='warning')
105 h.flash(str(e), category='warning')
106 redirect(h.url('files_home', repo_name=repo_name,
106 redirect(h.url('files_home', repo_name=repo_name,
107 revision=cs.raw_id))
107 revision=cs.raw_id))
108
108
109 return file_node
109 return file_node
110
110
111 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
111 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
112 'repository.admin')
112 'repository.admin')
113 def index(self, repo_name, revision, f_path):
113 def index(self, repo_name, revision, f_path):
114 # redirect to given revision from form if given
114 # redirect to given revision from form if given
115 post_revision = request.POST.get('at_rev', None)
115 post_revision = request.POST.get('at_rev', None)
116 if post_revision:
116 if post_revision:
117 cs = self.__get_cs_or_redirect(post_revision, repo_name)
117 cs = self.__get_cs_or_redirect(post_revision, repo_name)
118 redirect(url('files_home', repo_name=c.repo_name,
118 redirect(url('files_home', repo_name=c.repo_name,
119 revision=cs.raw_id, f_path=f_path))
119 revision=cs.raw_id, f_path=f_path))
120
120
121 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
121 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
122 c.branch = request.GET.get('branch', None)
122 c.branch = request.GET.get('branch', None)
123 c.f_path = f_path
123 c.f_path = f_path
124
124
125 cur_rev = c.changeset.revision
125 cur_rev = c.changeset.revision
126
126
127 # prev link
127 # prev link
128 try:
128 try:
129 prev_rev = c.rhodecode_repo.get_changeset(cur_rev).prev(c.branch)
129 prev_rev = c.rhodecode_repo.get_changeset(cur_rev).prev(c.branch)
130 c.url_prev = url('files_home', repo_name=c.repo_name,
130 c.url_prev = url('files_home', repo_name=c.repo_name,
131 revision=prev_rev.raw_id, f_path=f_path)
131 revision=prev_rev.raw_id, f_path=f_path)
132 if c.branch:
132 if c.branch:
133 c.url_prev += '?branch=%s' % c.branch
133 c.url_prev += '?branch=%s' % c.branch
134 except (ChangesetDoesNotExistError, VCSError):
134 except (ChangesetDoesNotExistError, VCSError):
135 c.url_prev = '#'
135 c.url_prev = '#'
136
136
137 # next link
137 # next link
138 try:
138 try:
139 next_rev = c.rhodecode_repo.get_changeset(cur_rev).next(c.branch)
139 next_rev = c.rhodecode_repo.get_changeset(cur_rev).next(c.branch)
140 c.url_next = url('files_home', repo_name=c.repo_name,
140 c.url_next = url('files_home', repo_name=c.repo_name,
141 revision=next_rev.raw_id, f_path=f_path)
141 revision=next_rev.raw_id, f_path=f_path)
142 if c.branch:
142 if c.branch:
143 c.url_next += '?branch=%s' % c.branch
143 c.url_next += '?branch=%s' % c.branch
144 except (ChangesetDoesNotExistError, VCSError):
144 except (ChangesetDoesNotExistError, VCSError):
145 c.url_next = '#'
145 c.url_next = '#'
146
146
147 # files or dirs
147 # files or dirs
148 try:
148 try:
149 c.file = c.changeset.get_node(f_path)
149 c.file = c.changeset.get_node(f_path)
150
150
151 if c.file.is_file():
151 if c.file.is_file():
152 c.file_history = self._get_node_history(c.changeset, f_path)
152 c.file_history = self._get_node_history(c.changeset, f_path)
153 else:
153 else:
154 c.file_history = []
154 c.file_history = []
155 except RepositoryError, e:
155 except RepositoryError, e:
156 h.flash(str(e), category='warning')
156 h.flash(str(e), category='warning')
157 redirect(h.url('files_home', repo_name=repo_name,
157 redirect(h.url('files_home', repo_name=repo_name,
158 revision=revision))
158 revision=revision))
159
159
160 return render('files/files.html')
160 return render('files/files.html')
161
161
162 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
162 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
163 'repository.admin')
163 'repository.admin')
164 def rawfile(self, repo_name, revision, f_path):
164 def rawfile(self, repo_name, revision, f_path):
165 cs = self.__get_cs_or_redirect(revision, repo_name)
165 cs = self.__get_cs_or_redirect(revision, repo_name)
166 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
166 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
167
167
168 response.content_disposition = 'attachment; filename=%s' % \
168 response.content_disposition = 'attachment; filename=%s' % \
169 safe_str(f_path.split(os.sep)[-1])
169 safe_str(f_path.split(os.sep)[-1])
170
170
171 response.content_type = file_node.mimetype
171 response.content_type = file_node.mimetype
172 return file_node.content
172 return file_node.content
173
173
174 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
174 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
175 'repository.admin')
175 'repository.admin')
176 def raw(self, repo_name, revision, f_path):
176 def raw(self, repo_name, revision, f_path):
177 cs = self.__get_cs_or_redirect(revision, repo_name)
177 cs = self.__get_cs_or_redirect(revision, repo_name)
178 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)
179
179
180 raw_mimetype_mapping = {
180 raw_mimetype_mapping = {
181 # map original mimetype to a mimetype used for "show as raw"
181 # map original mimetype to a mimetype used for "show as raw"
182 # you can also provide a content-disposition to override the
182 # you can also provide a content-disposition to override the
183 # default "attachment" disposition.
183 # default "attachment" disposition.
184 # orig_type: (new_type, new_dispo)
184 # orig_type: (new_type, new_dispo)
185
185
186 # show images inline:
186 # show images inline:
187 'image/x-icon': ('image/x-icon', 'inline'),
187 'image/x-icon': ('image/x-icon', 'inline'),
188 'image/png': ('image/png', 'inline'),
188 'image/png': ('image/png', 'inline'),
189 'image/gif': ('image/gif', 'inline'),
189 'image/gif': ('image/gif', 'inline'),
190 'image/jpeg': ('image/jpeg', 'inline'),
190 'image/jpeg': ('image/jpeg', 'inline'),
191 'image/svg+xml': ('image/svg+xml', 'inline'),
191 'image/svg+xml': ('image/svg+xml', 'inline'),
192 }
192 }
193
193
194 mimetype = file_node.mimetype
194 mimetype = file_node.mimetype
195 try:
195 try:
196 mimetype, dispo = raw_mimetype_mapping[mimetype]
196 mimetype, dispo = raw_mimetype_mapping[mimetype]
197 except KeyError:
197 except KeyError:
198 # we don't know anything special about this, handle it safely
198 # we don't know anything special about this, handle it safely
199 if file_node.is_binary:
199 if file_node.is_binary:
200 # do same as download raw for binary files
200 # do same as download raw for binary files
201 mimetype, dispo = 'application/octet-stream', 'attachment'
201 mimetype, dispo = 'application/octet-stream', 'attachment'
202 else:
202 else:
203 # do not just use the original mimetype, but force text/plain,
203 # do not just use the original mimetype, but force text/plain,
204 # otherwise it would serve text/html and that might be unsafe.
204 # otherwise it would serve text/html and that might be unsafe.
205 # Note: underlying vcs library fakes text/plain mimetype if the
205 # Note: underlying vcs library fakes text/plain mimetype if the
206 # mimetype can not be determined and it thinks it is not
206 # mimetype can not be determined and it thinks it is not
207 # binary.This might lead to erroneous text display in some
207 # binary.This might lead to erroneous text display in some
208 # cases, but helps in other cases, like with text files
208 # cases, but helps in other cases, like with text files
209 # without extension.
209 # without extension.
210 mimetype, dispo = 'text/plain', 'inline'
210 mimetype, dispo = 'text/plain', 'inline'
211
211
212 if dispo == 'attachment':
212 if dispo == 'attachment':
213 dispo = 'attachment; filename=%s' % \
213 dispo = 'attachment; filename=%s' % \
214 safe_str(f_path.split(os.sep)[-1])
214 safe_str(f_path.split(os.sep)[-1])
215
215
216 response.content_disposition = dispo
216 response.content_disposition = dispo
217 response.content_type = mimetype
217 response.content_type = mimetype
218 return file_node.content
218 return file_node.content
219
219
220 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
220 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
221 'repository.admin')
221 'repository.admin')
222 def annotate(self, repo_name, revision, f_path):
222 def annotate(self, repo_name, revision, f_path):
223 c.cs = self.__get_cs_or_redirect(revision, repo_name)
223 c.cs = self.__get_cs_or_redirect(revision, repo_name)
224 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
224 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
225
225
226 c.file_history = self._get_node_history(c.cs, f_path)
226 c.file_history = self._get_node_history(c.cs, f_path)
227 c.f_path = f_path
227 c.f_path = f_path
228 return render('files/files_annotate.html')
228 return render('files/files_annotate.html')
229
229
230 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
230 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
231 def edit(self, repo_name, revision, f_path):
231 def edit(self, repo_name, revision, f_path):
232 r_post = request.POST
232 r_post = request.POST
233
233
234 c.cs = self.__get_cs_or_redirect(revision, repo_name)
234 c.cs = self.__get_cs_or_redirect(revision, repo_name)
235 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
235 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
236
236
237 if c.file.is_binary:
237 if c.file.is_binary:
238 return redirect(url('files_home', repo_name=c.repo_name,
238 return redirect(url('files_home', repo_name=c.repo_name,
239 revision=c.cs.raw_id, f_path=f_path))
239 revision=c.cs.raw_id, f_path=f_path))
240
240
241 c.f_path = f_path
241 c.f_path = f_path
242
242
243 if r_post:
243 if r_post:
244
244
245 old_content = c.file.content
245 old_content = c.file.content
246 sl = old_content.splitlines(1)
246 sl = old_content.splitlines(1)
247 first_line = sl[0] if sl else ''
247 first_line = sl[0] if sl else ''
248 # modes: 0 - Unix, 1 - Mac, 2 - DOS
248 # modes: 0 - Unix, 1 - Mac, 2 - DOS
249 mode = detect_mode(first_line, 0)
249 mode = detect_mode(first_line, 0)
250 content = convert_line_endings(r_post.get('content'), mode)
250 content = convert_line_endings(r_post.get('content'), mode)
251
251
252 message = r_post.get('message') or (_('Edited %s via RhodeCode')
252 message = r_post.get('message') or (_('Edited %s via RhodeCode')
253 % (f_path))
253 % (f_path))
254 author = self.rhodecode_user.full_contact
254 author = self.rhodecode_user.full_contact
255
255
256 if content == old_content:
256 if content == old_content:
257 h.flash(_('No changes'),
257 h.flash(_('No changes'),
258 category='warning')
258 category='warning')
259 return redirect(url('changeset_home', repo_name=c.repo_name,
259 return redirect(url('changeset_home', repo_name=c.repo_name,
260 revision='tip'))
260 revision='tip'))
261
261
262 try:
262 try:
263 self.scm_model.commit_change(repo=c.rhodecode_repo,
263 self.scm_model.commit_change(repo=c.rhodecode_repo,
264 repo_name=repo_name, cs=c.cs,
264 repo_name=repo_name, cs=c.cs,
265 user=self.rhodecode_user,
265 user=self.rhodecode_user,
266 author=author, message=message,
266 author=author, message=message,
267 content=content, f_path=f_path)
267 content=content, f_path=f_path)
268 h.flash(_('Successfully committed to %s' % f_path),
268 h.flash(_('Successfully committed to %s' % f_path),
269 category='success')
269 category='success')
270
270
271 except Exception:
271 except Exception:
272 log.error(traceback.format_exc())
272 log.error(traceback.format_exc())
273 h.flash(_('Error occurred during commit'), category='error')
273 h.flash(_('Error occurred during commit'), category='error')
274 return redirect(url('changeset_home',
274 return redirect(url('changeset_home',
275 repo_name=c.repo_name, revision='tip'))
275 repo_name=c.repo_name, revision='tip'))
276
276
277 return render('files/files_edit.html')
277 return render('files/files_edit.html')
278
278
279 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
279 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
280 def add(self, repo_name, revision, f_path):
280 def add(self, repo_name, revision, f_path):
281 r_post = request.POST
281 r_post = request.POST
282 c.cs = self.__get_cs_or_redirect(revision, repo_name,
282 c.cs = self.__get_cs_or_redirect(revision, repo_name,
283 redirect_after=False)
283 redirect_after=False)
284 if c.cs is None:
284 if c.cs is None:
285 c.cs = EmptyChangeset(alias=c.rhodecode_repo.alias)
285 c.cs = EmptyChangeset(alias=c.rhodecode_repo.alias)
286
286
287 c.f_path = f_path
287 c.f_path = f_path
288
288
289 if r_post:
289 if r_post:
290 unix_mode = 0
290 unix_mode = 0
291 content = convert_line_endings(r_post.get('content'), unix_mode)
291 content = convert_line_endings(r_post.get('content'), unix_mode)
292
292
293 message = r_post.get('message') or (_('Added %s via RhodeCode')
293 message = r_post.get('message') or (_('Added %s via RhodeCode')
294 % (f_path))
294 % (f_path))
295 location = r_post.get('location')
295 location = r_post.get('location')
296 filename = r_post.get('filename')
296 filename = r_post.get('filename')
297 file_obj = r_post.get('upload_file', None)
297 file_obj = r_post.get('upload_file', None)
298
298
299 if file_obj is not None and hasattr(file_obj, 'filename'):
299 if file_obj is not None and hasattr(file_obj, 'filename'):
300 filename = file_obj.filename
300 filename = file_obj.filename
301 content = file_obj.file
301 content = file_obj.file
302
302
303 node_path = os.path.join(location, filename)
303 node_path = os.path.join(location, filename)
304 author = self.rhodecode_user.full_contact
304 author = self.rhodecode_user.full_contact
305
305
306 if not content:
306 if not content:
307 h.flash(_('No content'), category='warning')
307 h.flash(_('No content'), category='warning')
308 return redirect(url('changeset_home', repo_name=c.repo_name,
308 return redirect(url('changeset_home', repo_name=c.repo_name,
309 revision='tip'))
309 revision='tip'))
310 if not filename:
310 if not filename:
311 h.flash(_('No filename'), category='warning')
311 h.flash(_('No filename'), category='warning')
312 return redirect(url('changeset_home', repo_name=c.repo_name,
312 return redirect(url('changeset_home', repo_name=c.repo_name,
313 revision='tip'))
313 revision='tip'))
314
314
315 try:
315 try:
316 self.scm_model.create_node(repo=c.rhodecode_repo,
316 self.scm_model.create_node(repo=c.rhodecode_repo,
317 repo_name=repo_name, cs=c.cs,
317 repo_name=repo_name, cs=c.cs,
318 user=self.rhodecode_user,
318 user=self.rhodecode_user,
319 author=author, message=message,
319 author=author, message=message,
320 content=content, f_path=node_path)
320 content=content, f_path=node_path)
321 h.flash(_('Successfully committed to %s' % node_path),
321 h.flash(_('Successfully committed to %s' % node_path),
322 category='success')
322 category='success')
323 except NodeAlreadyExistsError, e:
323 except NodeAlreadyExistsError, e:
324 h.flash(_(e), category='error')
324 h.flash(_(e), category='error')
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_add.html')
331 return render('files/files_add.html')
332
332
333 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
333 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
334 'repository.admin')
334 'repository.admin')
335 def archivefile(self, repo_name, fname):
335 def archivefile(self, repo_name, fname):
336
336
337 fileformat = None
337 fileformat = None
338 revision = None
338 revision = None
339 ext = None
339 ext = None
340 subrepos = request.GET.get('subrepos') == 'true'
340 subrepos = request.GET.get('subrepos') == 'true'
341
341
342 for a_type, ext_data in settings.ARCHIVE_SPECS.items():
342 for a_type, ext_data in settings.ARCHIVE_SPECS.items():
343 archive_spec = fname.split(ext_data[1])
343 archive_spec = fname.split(ext_data[1])
344 if len(archive_spec) == 2 and archive_spec[1] == '':
344 if len(archive_spec) == 2 and archive_spec[1] == '':
345 fileformat = a_type or ext_data[1]
345 fileformat = a_type or ext_data[1]
346 revision = archive_spec[0]
346 revision = archive_spec[0]
347 ext = ext_data[1]
347 ext = ext_data[1]
348
348
349 try:
349 try:
350 dbrepo = RepoModel().get_by_repo_name(repo_name)
350 dbrepo = RepoModel().get_by_repo_name(repo_name)
351 if dbrepo.enable_downloads is False:
351 if dbrepo.enable_downloads is False:
352 return _('downloads disabled')
352 return _('downloads disabled')
353
353
354 if c.rhodecode_repo.alias == 'hg':
354 if c.rhodecode_repo.alias == 'hg':
355 # patch and reset hooks section of UI config to not run any
355 # patch and reset hooks section of UI config to not run any
356 # hooks on fetching archives with subrepos
356 # hooks on fetching archives with subrepos
357 for k, v in c.rhodecode_repo._repo.ui.configitems('hooks'):
357 for k, v in c.rhodecode_repo._repo.ui.configitems('hooks'):
358 c.rhodecode_repo._repo.ui.setconfig('hooks', k, None)
358 c.rhodecode_repo._repo.ui.setconfig('hooks', k, None)
359
359
360 cs = c.rhodecode_repo.get_changeset(revision)
360 cs = c.rhodecode_repo.get_changeset(revision)
361 content_type = settings.ARCHIVE_SPECS[fileformat][0]
361 content_type = settings.ARCHIVE_SPECS[fileformat][0]
362 except ChangesetDoesNotExistError:
362 except ChangesetDoesNotExistError:
363 return _('Unknown revision %s') % revision
363 return _('Unknown revision %s') % revision
364 except EmptyRepositoryError:
364 except EmptyRepositoryError:
365 return _('Empty repository')
365 return _('Empty repository')
366 except (ImproperArchiveTypeError, KeyError):
366 except (ImproperArchiveTypeError, KeyError):
367 return _('Unknown archive type')
367 return _('Unknown archive type')
368
368
369 response.content_type = content_type
369 response.content_type = content_type
370 response.content_disposition = 'attachment; filename=%s-%s%s' \
370 response.content_disposition = 'attachment; filename=%s-%s%s' \
371 % (repo_name, revision, ext)
371 % (repo_name, revision, ext)
372
372
373 import tempfile
373 import tempfile
374 archive = tempfile.mkstemp()[1]
374 archive = tempfile.mkstemp()[1]
375 t = open(archive, 'wb')
375 t = open(archive, 'wb')
376 cs.fill_archive(stream=t, kind=fileformat, subrepos=subrepos)
376 cs.fill_archive(stream=t, kind=fileformat, subrepos=subrepos)
377
377
378 def get_chunked_archive(archive):
378 def get_chunked_archive(archive):
379 stream = open(archive, 'rb')
379 stream = open(archive, 'rb')
380 while True:
380 while True:
381 data = stream.read(4096)
381 data = stream.read(4096)
382 if not data:
382 if not data:
383 os.remove(archive)
383 os.remove(archive)
384 break
384 break
385 yield data
385 yield data
386
386
387 return get_chunked_archive(archive)
387 return get_chunked_archive(archive)
388
388
389 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
389 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
390 'repository.admin')
390 'repository.admin')
391 def diff(self, repo_name, f_path):
391 def diff(self, repo_name, f_path):
392 ignore_whitespace = request.GET.get('ignorews') == '1'
392 ignore_whitespace = request.GET.get('ignorews') == '1'
393 line_context = request.GET.get('context', 3)
393 line_context = request.GET.get('context', 3)
394 diff1 = request.GET.get('diff1', '')
394 diff1 = request.GET.get('diff1', '')
395 diff2 = request.GET.get('diff2', '')
395 diff2 = request.GET.get('diff2', '')
396 c.action = request.GET.get('diff')
396 c.action = request.GET.get('diff')
397 c.no_changes = diff1 == diff2
397 c.no_changes = diff1 == diff2
398 c.f_path = f_path
398 c.f_path = f_path
399 c.big_diff = False
399 c.big_diff = False
400 c.anchor_url = anchor_url
400 c.anchor_url = anchor_url
401 c.ignorews_url = _ignorews_url
401 c.ignorews_url = _ignorews_url
402 c.context_url = _context_url
402 c.context_url = _context_url
403 c.changes = OrderedDict()
403 c.changes = OrderedDict()
404 c.changes[diff2] = []
404 c.changes[diff2] = []
405 try:
405 try:
406 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
406 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
407 c.changeset_1 = c.rhodecode_repo.get_changeset(diff1)
407 c.changeset_1 = c.rhodecode_repo.get_changeset(diff1)
408 node1 = c.changeset_1.get_node(f_path)
408 node1 = c.changeset_1.get_node(f_path)
409 else:
409 else:
410 c.changeset_1 = EmptyChangeset(repo=c.rhodecode_repo)
410 c.changeset_1 = EmptyChangeset(repo=c.rhodecode_repo)
411 node1 = FileNode('.', '', changeset=c.changeset_1)
411 node1 = FileNode('.', '', changeset=c.changeset_1)
412
412
413 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
413 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
414 c.changeset_2 = c.rhodecode_repo.get_changeset(diff2)
414 c.changeset_2 = c.rhodecode_repo.get_changeset(diff2)
415 node2 = c.changeset_2.get_node(f_path)
415 node2 = c.changeset_2.get_node(f_path)
416 else:
416 else:
417 c.changeset_2 = EmptyChangeset(repo=c.rhodecode_repo)
417 c.changeset_2 = EmptyChangeset(repo=c.rhodecode_repo)
418 node2 = FileNode('.', '', changeset=c.changeset_2)
418 node2 = FileNode('.', '', changeset=c.changeset_2)
419 except RepositoryError:
419 except RepositoryError:
420 return redirect(url('files_home', repo_name=c.repo_name,
420 return redirect(url('files_home', repo_name=c.repo_name,
421 f_path=f_path))
421 f_path=f_path))
422
422
423 if c.action == 'download':
423 if c.action == 'download':
424 _diff = diffs.get_gitdiff(node1, node2,
424 _diff = diffs.get_gitdiff(node1, node2,
425 ignore_whitespace=ignore_whitespace,
425 ignore_whitespace=ignore_whitespace,
426 context=line_context)
426 context=line_context)
427 diff = diffs.DiffProcessor(_diff, format='gitdiff')
427 diff = diffs.DiffProcessor(_diff, format='gitdiff')
428
428
429 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
429 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
430 response.content_type = 'text/plain'
430 response.content_type = 'text/plain'
431 response.content_disposition = 'attachment; filename=%s' \
431 response.content_disposition = 'attachment; filename=%s' \
432 % diff_name
432 % diff_name
433 return diff.raw_diff()
433 return diff.raw_diff()
434
434
435 elif c.action == 'raw':
435 elif c.action == 'raw':
436 _diff = diffs.get_gitdiff(node1, node2,
436 _diff = diffs.get_gitdiff(node1, node2,
437 ignore_whitespace=ignore_whitespace,
437 ignore_whitespace=ignore_whitespace,
438 context=line_context)
438 context=line_context)
439 diff = diffs.DiffProcessor(_diff, format='gitdiff')
439 diff = diffs.DiffProcessor(_diff, format='gitdiff')
440 response.content_type = 'text/plain'
440 response.content_type = 'text/plain'
441 return diff.raw_diff()
441 return diff.raw_diff()
442
442
443 else:
443 else:
444 fid = h.FID(diff2, node2.path)
444 fid = h.FID(diff2, node2.path)
445 line_context_lcl = get_line_ctx(fid, request.GET)
445 line_context_lcl = get_line_ctx(fid, request.GET)
446 ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
446 ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
447
447
448 lim = request.GET.get('fulldiff') or self.cut_off_limit
448 lim = request.GET.get('fulldiff') or self.cut_off_limit
449 _, cs1, cs2, diff, st = wrapped_diff(filenode_old=node1,
449 _, cs1, cs2, diff, st = wrapped_diff(filenode_old=node1,
450 filenode_new=node2,
450 filenode_new=node2,
451 cut_off_limit=lim,
451 cut_off_limit=lim,
452 ignore_whitespace=ign_whitespace_lcl,
452 ignore_whitespace=ign_whitespace_lcl,
453 line_context=line_context_lcl,
453 line_context=line_context_lcl,
454 enable_comments=False)
454 enable_comments=False)
455
455
456 c.changes = [('', node2, diff, cs1, cs2, st,)]
456 c.changes = [('', node2, diff, cs1, cs2, st,)]
457
457
458 return render('files/file_diff.html')
458 return render('files/file_diff.html')
459
459
460 def _get_node_history(self, cs, f_path):
460 def _get_node_history(self, cs, f_path):
461 changesets = cs.get_file_history(f_path)
461 changesets = cs.get_file_history(f_path)
462 hist_l = []
462 hist_l = []
463
463
464 changesets_group = ([], _("Changesets"))
464 changesets_group = ([], _("Changesets"))
465 branches_group = ([], _("Branches"))
465 branches_group = ([], _("Branches"))
466 tags_group = ([], _("Tags"))
466 tags_group = ([], _("Tags"))
467
467
468 for chs in changesets:
468 for chs in changesets:
469 n_desc = 'r%s:%s' % (chs.revision, chs.short_id)
469 n_desc = 'r%s:%s (%s)' % (chs.revision, chs.short_id, chs.branch)
470 changesets_group[0].append((chs.raw_id, n_desc,))
470 changesets_group[0].append((chs.raw_id, n_desc,))
471
471
472 hist_l.append(changesets_group)
472 hist_l.append(changesets_group)
473
473
474 for name, chs in c.rhodecode_repo.branches.items():
474 for name, chs in c.rhodecode_repo.branches.items():
475 branches_group[0].append((chs, name),)
475 branches_group[0].append((chs, name),)
476 hist_l.append(branches_group)
476 hist_l.append(branches_group)
477
477
478 for name, chs in c.rhodecode_repo.tags.items():
478 for name, chs in c.rhodecode_repo.tags.items():
479 tags_group[0].append((chs, name),)
479 tags_group[0].append((chs, name),)
480 hist_l.append(tags_group)
480 hist_l.append(tags_group)
481
481
482 return hist_l
482 return hist_l
483
483
484 @jsonify
484 @jsonify
485 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
485 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
486 'repository.admin')
486 'repository.admin')
487 def nodelist(self, repo_name, revision, f_path):
487 def nodelist(self, repo_name, revision, f_path):
488 if request.environ.get('HTTP_X_PARTIAL_XHR'):
488 if request.environ.get('HTTP_X_PARTIAL_XHR'):
489 cs = self.__get_cs_or_redirect(revision, repo_name)
489 cs = self.__get_cs_or_redirect(revision, repo_name)
490 _d, _f = ScmModel().get_nodes(repo_name, cs.raw_id, f_path,
490 _d, _f = ScmModel().get_nodes(repo_name, cs.raw_id, f_path,
491 flat=False)
491 flat=False)
492 return _d + _f
492 return _d + _f
@@ -1,104 +1,104 b''
1 <dl>
1 <dl>
2 <dt style="padding-top:10px;font-size:16px">${_('History')}</dt>
2 <dt style="padding-top:10px;font-size:16px">${_('History')}</dt>
3 <dd>
3 <dd>
4 <div>
4 <div>
5 ${h.form(h.url('files_diff_home',repo_name=c.repo_name,f_path=c.f_path),method='get')}
5 ${h.form(h.url('files_diff_home',repo_name=c.repo_name,f_path=c.f_path),method='get')}
6 ${h.hidden('diff2',c.file.last_changeset.raw_id)}
6 ${h.hidden('diff2',c.file.last_changeset.raw_id)}
7 ${h.select('diff1',c.file.last_changeset.raw_id,c.file_history)}
7 ${h.select('diff1',c.file.last_changeset.raw_id,c.file_history)}
8 ${h.submit('diff','diff to revision',class_="ui-btn")}
8 ${h.submit('diff','diff to revision',class_="ui-btn")}
9 ${h.submit('show_rev','show at revision',class_="ui-btn")}
9 ${h.submit('show_rev','show at revision',class_="ui-btn")}
10 ${h.end_form()}
10 ${h.end_form()}
11 </div>
11 </div>
12 </dd>
12 </dd>
13 </dl>
13 </dl>
14
14
15 <div id="body" class="codeblock">
15 <div id="body" class="codeblock">
16 <div class="code-header">
16 <div class="code-header">
17 <div class="stats">
17 <div class="stats">
18 <div class="left"><img src="${h.url('/images/icons/file.png')}"/></div>
18 <div class="left"><img src="${h.url('/images/icons/file.png')}"/></div>
19 <div class="left item"><pre>${h.link_to("r%s:%s" % (c.file.last_changeset.revision,h.short_id(c.file.last_changeset.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=c.file.last_changeset.raw_id))}</pre></div>
19 <div class="left item"><pre>${h.link_to("r%s:%s" % (c.file.last_changeset.revision,h.short_id(c.file.last_changeset.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=c.file.last_changeset.raw_id))}</pre></div>
20 <div class="left item">${h.format_byte_size(c.file.size,binary=True)}</div>
20 <div class="left item">${h.format_byte_size(c.file.size,binary=True)}</div>
21 <div class="left item last">${c.file.mimetype}</div>
21 <div class="left item last">${c.file.mimetype}</div>
22 <div class="buttons">
22 <div class="buttons">
23 ${h.link_to(_('show annotation'),h.url('files_annotate_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
23 ${h.link_to(_('show annotation'),h.url('files_annotate_home',repo_name=c.repo_name,revision=c.file.last_changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
24 ${h.link_to(_('show as raw'),h.url('files_raw_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
24 ${h.link_to(_('show as raw'),h.url('files_raw_home',repo_name=c.repo_name,revision=c.file.last_changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
25 ${h.link_to(_('download as raw'),h.url('files_rawfile_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
25 ${h.link_to(_('download as raw'),h.url('files_rawfile_home',repo_name=c.repo_name,revision=c.file.last_changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
26 % if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
26 % if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
27 % if not c.file.is_binary:
27 % if not c.file.is_binary:
28 ${h.link_to(_('edit'),h.url('files_edit_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
28 ${h.link_to(_('edit'),h.url('files_edit_home',repo_name=c.repo_name,revision=c.file.last_changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
29 % endif
29 % endif
30 % endif
30 % endif
31 </div>
31 </div>
32 </div>
32 </div>
33 <div class="author">
33 <div class="author">
34 <div class="gravatar">
34 <div class="gravatar">
35 <img alt="gravatar" src="${h.gravatar_url(h.email(c.changeset.author),16)}"/>
35 <img alt="gravatar" src="${h.gravatar_url(h.email(c.file.last_changeset.author),16)}"/>
36 </div>
36 </div>
37 <div title="${c.changeset.author}" class="user">${h.person(c.changeset.author)}</div>
37 <div title="${c.file.last_changeset.author}" class="user">${h.person(c.file.last_changeset.author)}</div>
38 </div>
38 </div>
39 <div class="commit">${c.file.last_changeset.message}</div>
39 <div class="commit">${c.file.last_changeset.message}</div>
40 </div>
40 </div>
41 <div class="code-body">
41 <div class="code-body">
42 %if c.file.is_binary:
42 %if c.file.is_binary:
43 ${_('Binary file (%s)') % c.file.mimetype}
43 ${_('Binary file (%s)') % c.file.mimetype}
44 %else:
44 %else:
45 % if c.file.size < c.cut_off_limit:
45 % if c.file.size < c.cut_off_limit:
46 ${h.pygmentize(c.file,linenos=True,anchorlinenos=True,lineanchors='L',cssclass="code-highlight")}
46 ${h.pygmentize(c.file,linenos=True,anchorlinenos=True,lineanchors='L',cssclass="code-highlight")}
47 %else:
47 %else:
48 ${_('File is too big to display')} ${h.link_to(_('show as raw'),
48 ${_('File is too big to display')} ${h.link_to(_('show as raw'),
49 h.url('files_raw_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path))}
49 h.url('files_raw_home',repo_name=c.repo_name,revision=c.file.last_changeset.raw_id,f_path=c.f_path))}
50 %endif
50 %endif
51 <script type="text/javascript">
51 <script type="text/javascript">
52 function highlight_lines(lines){
52 function highlight_lines(lines){
53 for(pos in lines){
53 for(pos in lines){
54 YUD.setStyle('L'+lines[pos],'background-color','#FFFFBE');
54 YUD.setStyle('L'+lines[pos],'background-color','#FFFFBE');
55 }
55 }
56 }
56 }
57 page_highlights = location.href.substring(location.href.indexOf('#')+1).split('L');
57 page_highlights = location.href.substring(location.href.indexOf('#')+1).split('L');
58 if (page_highlights.length == 2){
58 if (page_highlights.length == 2){
59 highlight_ranges = page_highlights[1].split(",");
59 highlight_ranges = page_highlights[1].split(",");
60
60
61 var h_lines = [];
61 var h_lines = [];
62 for (pos in highlight_ranges){
62 for (pos in highlight_ranges){
63 var _range = highlight_ranges[pos].split('-');
63 var _range = highlight_ranges[pos].split('-');
64 if(_range.length == 2){
64 if(_range.length == 2){
65 var start = parseInt(_range[0]);
65 var start = parseInt(_range[0]);
66 var end = parseInt(_range[1]);
66 var end = parseInt(_range[1]);
67 if (start < end){
67 if (start < end){
68 for(var i=start;i<=end;i++){
68 for(var i=start;i<=end;i++){
69 h_lines.push(i);
69 h_lines.push(i);
70 }
70 }
71 }
71 }
72 }
72 }
73 else{
73 else{
74 h_lines.push(parseInt(highlight_ranges[pos]));
74 h_lines.push(parseInt(highlight_ranges[pos]));
75 }
75 }
76 }
76 }
77 highlight_lines(h_lines);
77 highlight_lines(h_lines);
78
78
79 //remember original location
79 //remember original location
80 var old_hash = location.href.substring(location.href.indexOf('#'));
80 var old_hash = location.href.substring(location.href.indexOf('#'));
81
81
82 // this makes a jump to anchor moved by 3 posstions for padding
82 // this makes a jump to anchor moved by 3 posstions for padding
83 window.location.hash = '#L'+Math.max(parseInt(h_lines[0])-3,1);
83 window.location.hash = '#L'+Math.max(parseInt(h_lines[0])-3,1);
84
84
85 //sets old anchor
85 //sets old anchor
86 window.location.hash = old_hash;
86 window.location.hash = old_hash;
87
87
88 }
88 }
89 </script>
89 </script>
90 %endif
90 %endif
91 </div>
91 </div>
92 </div>
92 </div>
93
93
94 <script type="text/javascript">
94 <script type="text/javascript">
95 YUE.onDOMReady(function(){
95 YUE.onDOMReady(function(){
96 YUE.on('show_rev','click',function(e){
96 YUE.on('show_rev','click',function(e){
97 YUE.preventDefault(e);
97 YUE.preventDefault(e);
98 var cs = YUD.get('diff1').value;
98 var cs = YUD.get('diff1').value;
99 var url = "${h.url('files_home',repo_name=c.repo_name,revision='__CS__',f_path=c.f_path)}".replace('__CS__',cs);
99 var url = "${h.url('files_home',repo_name=c.repo_name,revision='__CS__',f_path=c.f_path)}".replace('__CS__',cs);
100 window.location = url;
100 window.location = url;
101 });
101 });
102 YUE.on('hlcode','mouseup',getSelectionLink("${_('Selection link')}"))
102 YUE.on('hlcode','mouseup',getSelectionLink("${_('Selection link')}"))
103 });
103 });
104 </script>
104 </script>
General Comments 0
You need to be logged in to leave comments. Login now