##// END OF EJS Templates
fixed small issues with adding new filenodes
marcink -
r1484:1db451a4 beta
parent child Browse files
Show More
@@ -1,496 +1,500 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) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 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 os.path import join as jn
30 from os.path import join as jn
31
31
32 from pylons import request, response, session, tmpl_context as c, url
32 from pylons import request, response, session, 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 pylons.decorators import jsonify
35 from pylons.decorators import jsonify
36
36
37 from vcs.conf import settings
37 from vcs.conf import settings
38 from vcs.exceptions import RepositoryError, ChangesetDoesNotExistError, \
38 from vcs.exceptions import RepositoryError, ChangesetDoesNotExistError, \
39 EmptyRepositoryError, ImproperArchiveTypeError, VCSError
39 EmptyRepositoryError, ImproperArchiveTypeError, VCSError
40 from vcs.nodes import FileNode, NodeKind
40 from vcs.nodes import FileNode, NodeKind
41 from vcs.utils import diffs as differ
41 from vcs.utils import diffs as differ
42
42
43 from rhodecode.lib import convert_line_endings, detect_mode, safe_str
43 from rhodecode.lib import convert_line_endings, detect_mode, safe_str
44 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
44 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
45 from rhodecode.lib.base import BaseRepoController, render
45 from rhodecode.lib.base import BaseRepoController, render
46 from rhodecode.lib.utils import EmptyChangeset
46 from rhodecode.lib.utils import EmptyChangeset
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
49
50 log = logging.getLogger(__name__)
50 log = logging.getLogger(__name__)
51
51
52
52
53 class FilesController(BaseRepoController):
53 class FilesController(BaseRepoController):
54
54
55 @LoginRequired()
55 @LoginRequired()
56 def __before__(self):
56 def __before__(self):
57 super(FilesController, self).__before__()
57 super(FilesController, self).__before__()
58 c.cut_off_limit = self.cut_off_limit
58 c.cut_off_limit = self.cut_off_limit
59
59
60 def __get_cs_or_redirect(self, rev, repo_name, redirect_after=True):
60 def __get_cs_or_redirect(self, rev, repo_name, redirect_after=True):
61 """
61 """
62 Safe way to get changeset if error occur it redirects to tip with
62 Safe way to get changeset if error occur it redirects to tip with
63 proper message
63 proper message
64
64
65 :param rev: revision to fetch
65 :param rev: revision to fetch
66 :param repo_name: repo name to redirect after
66 :param repo_name: repo name to redirect after
67 """
67 """
68
68
69 try:
69 try:
70 return c.rhodecode_repo.get_changeset(rev)
70 return c.rhodecode_repo.get_changeset(rev)
71 except EmptyRepositoryError, e:
71 except EmptyRepositoryError, e:
72 if not redirect_after:
72 if not redirect_after:
73 return None
73 return None
74 url_ = url('files_add_home',
74 url_ = url('files_add_home',
75 repo_name=c.repo_name,
75 repo_name=c.repo_name,
76 revision=0,f_path='')
76 revision=0,f_path='')
77 add_new = '<a href="%s">[%s]</a>' % (url_,_('add new'))
77 add_new = '<a href="%s">[%s]</a>' % (url_,_('add new'))
78 h.flash(h.literal(_('There are no files yet %s' % add_new)),
78 h.flash(h.literal(_('There are no files yet %s' % add_new)),
79 category='warning')
79 category='warning')
80 redirect(h.url('summary_home', repo_name=repo_name))
80 redirect(h.url('summary_home', repo_name=repo_name))
81
81
82 except RepositoryError, e:
82 except RepositoryError, e:
83 h.flash(str(e), category='warning')
83 h.flash(str(e), category='warning')
84 redirect(h.url('files_home', repo_name=repo_name, revision='tip'))
84 redirect(h.url('files_home', repo_name=repo_name, revision='tip'))
85
85
86 def __get_filenode_or_redirect(self, repo_name, cs, path):
86 def __get_filenode_or_redirect(self, repo_name, cs, path):
87 """
87 """
88 Returns file_node, if error occurs or given path is directory,
88 Returns file_node, if error occurs or given path is directory,
89 it'll redirect to top level path
89 it'll redirect to top level path
90
90
91 :param repo_name: repo_name
91 :param repo_name: repo_name
92 :param cs: given changeset
92 :param cs: given changeset
93 :param path: path to lookup
93 :param path: path to lookup
94 """
94 """
95
95
96 try:
96 try:
97 file_node = cs.get_node(path)
97 file_node = cs.get_node(path)
98 if file_node.is_dir():
98 if file_node.is_dir():
99 raise RepositoryError('given path is a directory')
99 raise RepositoryError('given path is a directory')
100 except RepositoryError, e:
100 except RepositoryError, e:
101 h.flash(str(e), category='warning')
101 h.flash(str(e), category='warning')
102 redirect(h.url('files_home', repo_name=repo_name,
102 redirect(h.url('files_home', repo_name=repo_name,
103 revision=cs.raw_id))
103 revision=cs.raw_id))
104
104
105 return file_node
105 return file_node
106
106
107
107
108 def __get_paths(self, changeset, starting_path):
108 def __get_paths(self, changeset, starting_path):
109 """recursive walk in root dir and return a set of all path in that dir
109 """recursive walk in root dir and return a set of all path in that dir
110 based on repository walk function
110 based on repository walk function
111 """
111 """
112 _files = list()
112 _files = list()
113 _dirs = list()
113 _dirs = list()
114
114
115 try:
115 try:
116 tip = changeset
116 tip = changeset
117 for topnode, dirs, files in tip.walk(starting_path):
117 for topnode, dirs, files in tip.walk(starting_path):
118 for f in files:
118 for f in files:
119 _files.append(f.path)
119 _files.append(f.path)
120 for d in dirs:
120 for d in dirs:
121 _dirs.append(d.path)
121 _dirs.append(d.path)
122 except RepositoryError, e:
122 except RepositoryError, e:
123 log.debug(traceback.format_exc())
123 log.debug(traceback.format_exc())
124 pass
124 pass
125 return _dirs, _files
125 return _dirs, _files
126
126
127 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
127 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
128 'repository.admin')
128 'repository.admin')
129 def index(self, repo_name, revision, f_path):
129 def index(self, repo_name, revision, f_path):
130 #reditect to given revision from form if given
130 #reditect to given revision from form if given
131 post_revision = request.POST.get('at_rev', None)
131 post_revision = request.POST.get('at_rev', None)
132 if post_revision:
132 if post_revision:
133 cs = self.__get_cs_or_redirect(post_revision, repo_name)
133 cs = self.__get_cs_or_redirect(post_revision, repo_name)
134 redirect(url('files_home', repo_name=c.repo_name,
134 redirect(url('files_home', repo_name=c.repo_name,
135 revision=cs.raw_id, f_path=f_path))
135 revision=cs.raw_id, f_path=f_path))
136
136
137 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
137 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
138 c.branch = request.GET.get('branch', None)
138 c.branch = request.GET.get('branch', None)
139 c.f_path = f_path
139 c.f_path = f_path
140
140
141 cur_rev = c.changeset.revision
141 cur_rev = c.changeset.revision
142
142
143 #prev link
143 #prev link
144 try:
144 try:
145 prev_rev = c.rhodecode_repo.get_changeset(cur_rev).prev(c.branch)
145 prev_rev = c.rhodecode_repo.get_changeset(cur_rev).prev(c.branch)
146 c.url_prev = url('files_home', repo_name=c.repo_name,
146 c.url_prev = url('files_home', repo_name=c.repo_name,
147 revision=prev_rev.raw_id, f_path=f_path)
147 revision=prev_rev.raw_id, f_path=f_path)
148 if c.branch:
148 if c.branch:
149 c.url_prev += '?branch=%s' % c.branch
149 c.url_prev += '?branch=%s' % c.branch
150 except (ChangesetDoesNotExistError, VCSError):
150 except (ChangesetDoesNotExistError, VCSError):
151 c.url_prev = '#'
151 c.url_prev = '#'
152
152
153 #next link
153 #next link
154 try:
154 try:
155 next_rev = c.rhodecode_repo.get_changeset(cur_rev).next(c.branch)
155 next_rev = c.rhodecode_repo.get_changeset(cur_rev).next(c.branch)
156 c.url_next = url('files_home', repo_name=c.repo_name,
156 c.url_next = url('files_home', repo_name=c.repo_name,
157 revision=next_rev.raw_id, f_path=f_path)
157 revision=next_rev.raw_id, f_path=f_path)
158 if c.branch:
158 if c.branch:
159 c.url_next += '?branch=%s' % c.branch
159 c.url_next += '?branch=%s' % c.branch
160 except (ChangesetDoesNotExistError, VCSError):
160 except (ChangesetDoesNotExistError, VCSError):
161 c.url_next = '#'
161 c.url_next = '#'
162
162
163 #files or dirs
163 #files or dirs
164 try:
164 try:
165 c.files_list = c.changeset.get_node(f_path)
165 c.files_list = c.changeset.get_node(f_path)
166
166
167 if c.files_list.is_file():
167 if c.files_list.is_file():
168 c.file_history = self._get_node_history(c.changeset, f_path)
168 c.file_history = self._get_node_history(c.changeset, f_path)
169 else:
169 else:
170 c.file_history = []
170 c.file_history = []
171 except RepositoryError, e:
171 except RepositoryError, e:
172 h.flash(str(e), category='warning')
172 h.flash(str(e), category='warning')
173 redirect(h.url('files_home', repo_name=repo_name,
173 redirect(h.url('files_home', repo_name=repo_name,
174 revision=revision))
174 revision=revision))
175
175
176 return render('files/files.html')
176 return render('files/files.html')
177
177
178 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
178 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
179 'repository.admin')
179 'repository.admin')
180 def rawfile(self, repo_name, revision, f_path):
180 def rawfile(self, repo_name, revision, f_path):
181 cs = self.__get_cs_or_redirect(revision, repo_name)
181 cs = self.__get_cs_or_redirect(revision, repo_name)
182 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
182 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
183
183
184 response.content_disposition = 'attachment; filename=%s' % \
184 response.content_disposition = 'attachment; filename=%s' % \
185 safe_str(f_path.split(os.sep)[-1])
185 safe_str(f_path.split(os.sep)[-1])
186
186
187 response.content_type = file_node.mimetype
187 response.content_type = file_node.mimetype
188 return file_node.content
188 return file_node.content
189
189
190 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
190 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
191 'repository.admin')
191 'repository.admin')
192 def raw(self, repo_name, revision, f_path):
192 def raw(self, repo_name, revision, f_path):
193 cs = self.__get_cs_or_redirect(revision, repo_name)
193 cs = self.__get_cs_or_redirect(revision, repo_name)
194 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
194 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
195
195
196 raw_mimetype_mapping = {
196 raw_mimetype_mapping = {
197 # map original mimetype to a mimetype used for "show as raw"
197 # map original mimetype to a mimetype used for "show as raw"
198 # you can also provide a content-disposition to override the
198 # you can also provide a content-disposition to override the
199 # default "attachment" disposition.
199 # default "attachment" disposition.
200 # orig_type: (new_type, new_dispo)
200 # orig_type: (new_type, new_dispo)
201
201
202 # show images inline:
202 # show images inline:
203 'image/x-icon': ('image/x-icon', 'inline'),
203 'image/x-icon': ('image/x-icon', 'inline'),
204 'image/png': ('image/png', 'inline'),
204 'image/png': ('image/png', 'inline'),
205 'image/gif': ('image/gif', 'inline'),
205 'image/gif': ('image/gif', 'inline'),
206 'image/jpeg': ('image/jpeg', 'inline'),
206 'image/jpeg': ('image/jpeg', 'inline'),
207 'image/svg+xml': ('image/svg+xml', 'inline'),
207 'image/svg+xml': ('image/svg+xml', 'inline'),
208 }
208 }
209
209
210 mimetype = file_node.mimetype
210 mimetype = file_node.mimetype
211 try:
211 try:
212 mimetype, dispo = raw_mimetype_mapping[mimetype]
212 mimetype, dispo = raw_mimetype_mapping[mimetype]
213 except KeyError:
213 except KeyError:
214 # we don't know anything special about this, handle it safely
214 # we don't know anything special about this, handle it safely
215 if file_node.is_binary:
215 if file_node.is_binary:
216 # do same as download raw for binary files
216 # do same as download raw for binary files
217 mimetype, dispo = 'application/octet-stream', 'attachment'
217 mimetype, dispo = 'application/octet-stream', 'attachment'
218 else:
218 else:
219 # do not just use the original mimetype, but force text/plain,
219 # do not just use the original mimetype, but force text/plain,
220 # otherwise it would serve text/html and that might be unsafe.
220 # otherwise it would serve text/html and that might be unsafe.
221 # Note: underlying vcs library fakes text/plain mimetype if the
221 # Note: underlying vcs library fakes text/plain mimetype if the
222 # mimetype can not be determined and it thinks it is not
222 # mimetype can not be determined and it thinks it is not
223 # binary.This might lead to erroneous text display in some
223 # binary.This might lead to erroneous text display in some
224 # cases, but helps in other cases, like with text files
224 # cases, but helps in other cases, like with text files
225 # without extension.
225 # without extension.
226 mimetype, dispo = 'text/plain', 'inline'
226 mimetype, dispo = 'text/plain', 'inline'
227
227
228 if dispo == 'attachment':
228 if dispo == 'attachment':
229 dispo = 'attachment; filename=%s' % \
229 dispo = 'attachment; filename=%s' % \
230 safe_str(f_path.split(os.sep)[-1])
230 safe_str(f_path.split(os.sep)[-1])
231
231
232 response.content_disposition = dispo
232 response.content_disposition = dispo
233 response.content_type = mimetype
233 response.content_type = mimetype
234 return file_node.content
234 return file_node.content
235
235
236 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
236 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
237 'repository.admin')
237 'repository.admin')
238 def annotate(self, repo_name, revision, f_path):
238 def annotate(self, repo_name, revision, f_path):
239 c.cs = self.__get_cs_or_redirect(revision, repo_name)
239 c.cs = self.__get_cs_or_redirect(revision, repo_name)
240 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
240 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
241
241
242 c.file_history = self._get_node_history(c.cs, f_path)
242 c.file_history = self._get_node_history(c.cs, f_path)
243 c.f_path = f_path
243 c.f_path = f_path
244 return render('files/files_annotate.html')
244 return render('files/files_annotate.html')
245
245
246 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
246 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
247 def edit(self, repo_name, revision, f_path):
247 def edit(self, repo_name, revision, f_path):
248 r_post = request.POST
248 r_post = request.POST
249
249
250 c.cs = self.__get_cs_or_redirect(revision, repo_name)
250 c.cs = self.__get_cs_or_redirect(revision, repo_name)
251 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
251 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
252
252
253 if c.file.is_binary:
253 if c.file.is_binary:
254 return redirect(url('files_home', repo_name=c.repo_name,
254 return redirect(url('files_home', repo_name=c.repo_name,
255 revision=c.cs.raw_id, f_path=f_path))
255 revision=c.cs.raw_id, f_path=f_path))
256
256
257 c.f_path = f_path
257 c.f_path = f_path
258
258
259 if r_post:
259 if r_post:
260
260
261 old_content = c.file.content
261 old_content = c.file.content
262 sl = old_content.splitlines(1)
262 sl = old_content.splitlines(1)
263 first_line = sl[0] if sl else ''
263 first_line = sl[0] if sl else ''
264 # modes: 0 - Unix, 1 - Mac, 2 - DOS
264 # modes: 0 - Unix, 1 - Mac, 2 - DOS
265 mode = detect_mode(first_line, 0)
265 mode = detect_mode(first_line, 0)
266 content = convert_line_endings(r_post.get('content'), mode)
266 content = convert_line_endings(r_post.get('content'), mode)
267
267
268 message = r_post.get('message') or (_('Edited %s via RhodeCode')
268 message = r_post.get('message') or (_('Edited %s via RhodeCode')
269 % (f_path))
269 % (f_path))
270 author = self.rhodecode_user.full_contact
270 author = self.rhodecode_user.full_contact
271
271
272 if content == old_content:
272 if content == old_content:
273 h.flash(_('No changes'),
273 h.flash(_('No changes'),
274 category='warning')
274 category='warning')
275 return redirect(url('changeset_home', repo_name=c.repo_name,
275 return redirect(url('changeset_home', repo_name=c.repo_name,
276 revision='tip'))
276 revision='tip'))
277
277
278 try:
278 try:
279 self.scm_model.commit_change(repo=c.rhodecode_repo,
279 self.scm_model.commit_change(repo=c.rhodecode_repo,
280 repo_name=repo_name, cs=c.cs,
280 repo_name=repo_name, cs=c.cs,
281 user=self.rhodecode_user,
281 user=self.rhodecode_user,
282 author=author, message=message,
282 author=author, message=message,
283 content=content, f_path=f_path)
283 content=content, f_path=f_path)
284 h.flash(_('Successfully committed to %s' % f_path),
284 h.flash(_('Successfully committed to %s' % f_path),
285 category='success')
285 category='success')
286
286
287 except Exception:
287 except Exception:
288 log.error(traceback.format_exc())
288 log.error(traceback.format_exc())
289 h.flash(_('Error occurred during commit'), category='error')
289 h.flash(_('Error occurred during commit'), category='error')
290 return redirect(url('changeset_home',
290 return redirect(url('changeset_home',
291 repo_name=c.repo_name, revision='tip'))
291 repo_name=c.repo_name, revision='tip'))
292
292
293 return render('files/files_edit.html')
293 return render('files/files_edit.html')
294
294
295 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
295 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
296 def add(self, repo_name, revision, f_path):
296 def add(self, repo_name, revision, f_path):
297 r_post = request.POST
297 r_post = request.POST
298 c.cs = self.__get_cs_or_redirect(revision, repo_name,
298 c.cs = self.__get_cs_or_redirect(revision, repo_name,
299 redirect_after=False)
299 redirect_after=False)
300 if c.cs is None:
300 if c.cs is None:
301 c.cs = EmptyChangeset(alias=c.rhodecode_repo.alias)
301 c.cs = EmptyChangeset(alias=c.rhodecode_repo.alias)
302
302
303 c.f_path = f_path
303 c.f_path = f_path
304
304
305 if r_post:
305 if r_post:
306 unix_mode = 0
306 unix_mode = 0
307 content = convert_line_endings(r_post.get('content'), unix_mode)
307 content = convert_line_endings(r_post.get('content'), unix_mode)
308
308
309 message = r_post.get('message') or (_('Added %s via RhodeCode')
309 message = r_post.get('message') or (_('Added %s via RhodeCode')
310 % (f_path))
310 % (f_path))
311 location = r_post.get('location')
311 location = r_post.get('location')
312 filename = r_post.get('filename')
312 filename = r_post.get('filename')
313 node_path = os.path.join(location, filename)
313 node_path = os.path.join(location, filename)
314 author = self.rhodecode_user.full_contact
314 author = self.rhodecode_user.full_contact
315
315
316 if not content:
316 if not content:
317 h.flash(_('No content'), category='warning')
317 h.flash(_('No content'), category='warning')
318 return redirect(url('changeset_home', repo_name=c.repo_name,
318 return redirect(url('changeset_home', repo_name=c.repo_name,
319 revision='tip'))
319 revision='tip'))
320 if not filename:
321 h.flash(_('No filename'), category='warning')
322 return redirect(url('changeset_home', repo_name=c.repo_name,
323 revision='tip'))
320
324
321 try:
325 try:
322 self.scm_model.create_node(repo=c.rhodecode_repo,
326 self.scm_model.create_node(repo=c.rhodecode_repo,
323 repo_name=repo_name, cs=c.cs,
327 repo_name=repo_name, cs=c.cs,
324 user=self.rhodecode_user,
328 user=self.rhodecode_user,
325 author=author, message=message,
329 author=author, message=message,
326 content=content, f_path=node_path)
330 content=content, f_path=node_path)
327 h.flash(_('Successfully committed to %s' % node_path),
331 h.flash(_('Successfully committed to %s' % node_path),
328 category='success')
332 category='success')
329
333
330 except Exception:
334 except Exception:
331 log.error(traceback.format_exc())
335 log.error(traceback.format_exc())
332 h.flash(_('Error occurred during commit'), category='error')
336 h.flash(_('Error occurred during commit'), category='error')
333 return redirect(url('changeset_home',
337 return redirect(url('changeset_home',
334 repo_name=c.repo_name, revision='tip'))
338 repo_name=c.repo_name, revision='tip'))
335
339
336 return render('files/files_add.html')
340 return render('files/files_add.html')
337
341
338 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
342 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
339 'repository.admin')
343 'repository.admin')
340 def archivefile(self, repo_name, fname):
344 def archivefile(self, repo_name, fname):
341
345
342 fileformat = None
346 fileformat = None
343 revision = None
347 revision = None
344 ext = None
348 ext = None
345 subrepos = request.GET.get('subrepos') == 'true'
349 subrepos = request.GET.get('subrepos') == 'true'
346
350
347 for a_type, ext_data in settings.ARCHIVE_SPECS.items():
351 for a_type, ext_data in settings.ARCHIVE_SPECS.items():
348 archive_spec = fname.split(ext_data[1])
352 archive_spec = fname.split(ext_data[1])
349 if len(archive_spec) == 2 and archive_spec[1] == '':
353 if len(archive_spec) == 2 and archive_spec[1] == '':
350 fileformat = a_type or ext_data[1]
354 fileformat = a_type or ext_data[1]
351 revision = archive_spec[0]
355 revision = archive_spec[0]
352 ext = ext_data[1]
356 ext = ext_data[1]
353
357
354 try:
358 try:
355 dbrepo = RepoModel().get_by_repo_name(repo_name)
359 dbrepo = RepoModel().get_by_repo_name(repo_name)
356 if dbrepo.enable_downloads is False:
360 if dbrepo.enable_downloads is False:
357 return _('downloads disabled')
361 return _('downloads disabled')
358
362
359 cs = c.rhodecode_repo.get_changeset(revision)
363 cs = c.rhodecode_repo.get_changeset(revision)
360 content_type = settings.ARCHIVE_SPECS[fileformat][0]
364 content_type = settings.ARCHIVE_SPECS[fileformat][0]
361 except ChangesetDoesNotExistError:
365 except ChangesetDoesNotExistError:
362 return _('Unknown revision %s') % revision
366 return _('Unknown revision %s') % revision
363 except EmptyRepositoryError:
367 except EmptyRepositoryError:
364 return _('Empty repository')
368 return _('Empty repository')
365 except (ImproperArchiveTypeError, KeyError):
369 except (ImproperArchiveTypeError, KeyError):
366 return _('Unknown archive type')
370 return _('Unknown archive type')
367
371
368 response.content_type = content_type
372 response.content_type = content_type
369 response.content_disposition = 'attachment; filename=%s-%s%s' \
373 response.content_disposition = 'attachment; filename=%s-%s%s' \
370 % (repo_name, revision, ext)
374 % (repo_name, revision, ext)
371
375
372 import tempfile
376 import tempfile
373 archive = tempfile.mkstemp()[1]
377 archive = tempfile.mkstemp()[1]
374 t = open(archive, 'wb')
378 t = open(archive, 'wb')
375 cs.fill_archive(stream=t, kind=fileformat, subrepos=subrepos)
379 cs.fill_archive(stream=t, kind=fileformat, subrepos=subrepos)
376
380
377 def get_chunked_archive(archive):
381 def get_chunked_archive(archive):
378 stream = open(archive, 'rb')
382 stream = open(archive, 'rb')
379 while True:
383 while True:
380 data = stream.read(4096)
384 data = stream.read(4096)
381 if not data:
385 if not data:
382 os.remove(archive)
386 os.remove(archive)
383 break
387 break
384 yield data
388 yield data
385
389
386 return get_chunked_archive(archive)
390 return get_chunked_archive(archive)
387
391
388 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
392 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
389 'repository.admin')
393 'repository.admin')
390 def diff(self, repo_name, f_path):
394 def diff(self, repo_name, f_path):
391 diff1 = request.GET.get('diff1')
395 diff1 = request.GET.get('diff1')
392 diff2 = request.GET.get('diff2')
396 diff2 = request.GET.get('diff2')
393 c.action = request.GET.get('diff')
397 c.action = request.GET.get('diff')
394 c.no_changes = diff1 == diff2
398 c.no_changes = diff1 == diff2
395 c.f_path = f_path
399 c.f_path = f_path
396 c.big_diff = False
400 c.big_diff = False
397
401
398 try:
402 try:
399 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
403 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
400 c.changeset_1 = c.rhodecode_repo.get_changeset(diff1)
404 c.changeset_1 = c.rhodecode_repo.get_changeset(diff1)
401 node1 = c.changeset_1.get_node(f_path)
405 node1 = c.changeset_1.get_node(f_path)
402 else:
406 else:
403 c.changeset_1 = EmptyChangeset(repo=c.rhodecode_repo)
407 c.changeset_1 = EmptyChangeset(repo=c.rhodecode_repo)
404 node1 = FileNode('.', '', changeset=c.changeset_1)
408 node1 = FileNode('.', '', changeset=c.changeset_1)
405
409
406 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
410 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
407 c.changeset_2 = c.rhodecode_repo.get_changeset(diff2)
411 c.changeset_2 = c.rhodecode_repo.get_changeset(diff2)
408 node2 = c.changeset_2.get_node(f_path)
412 node2 = c.changeset_2.get_node(f_path)
409 else:
413 else:
410 c.changeset_2 = EmptyChangeset(repo=c.rhodecode_repo)
414 c.changeset_2 = EmptyChangeset(repo=c.rhodecode_repo)
411 node2 = FileNode('.', '', changeset=c.changeset_2)
415 node2 = FileNode('.', '', changeset=c.changeset_2)
412 except RepositoryError:
416 except RepositoryError:
413 return redirect(url('files_home',
417 return redirect(url('files_home',
414 repo_name=c.repo_name, f_path=f_path))
418 repo_name=c.repo_name, f_path=f_path))
415
419
416 if c.action == 'download':
420 if c.action == 'download':
417 diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2),
421 diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2),
418 format='gitdiff')
422 format='gitdiff')
419
423
420 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
424 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
421 response.content_type = 'text/plain'
425 response.content_type = 'text/plain'
422 response.content_disposition = 'attachment; filename=%s' \
426 response.content_disposition = 'attachment; filename=%s' \
423 % diff_name
427 % diff_name
424 return diff.raw_diff()
428 return diff.raw_diff()
425
429
426 elif c.action == 'raw':
430 elif c.action == 'raw':
427 diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2),
431 diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2),
428 format='gitdiff')
432 format='gitdiff')
429 response.content_type = 'text/plain'
433 response.content_type = 'text/plain'
430 return diff.raw_diff()
434 return diff.raw_diff()
431
435
432 elif c.action == 'diff':
436 elif c.action == 'diff':
433 if node1.is_binary or node2.is_binary:
437 if node1.is_binary or node2.is_binary:
434 c.cur_diff = _('Binary file')
438 c.cur_diff = _('Binary file')
435 elif node1.size > self.cut_off_limit or \
439 elif node1.size > self.cut_off_limit or \
436 node2.size > self.cut_off_limit:
440 node2.size > self.cut_off_limit:
437 c.cur_diff = ''
441 c.cur_diff = ''
438 c.big_diff = True
442 c.big_diff = True
439 else:
443 else:
440 diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2),
444 diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2),
441 format='gitdiff')
445 format='gitdiff')
442 c.cur_diff = diff.as_html()
446 c.cur_diff = diff.as_html()
443 else:
447 else:
444
448
445 #default option
449 #default option
446 if node1.is_binary or node2.is_binary:
450 if node1.is_binary or node2.is_binary:
447 c.cur_diff = _('Binary file')
451 c.cur_diff = _('Binary file')
448 elif node1.size > self.cut_off_limit or \
452 elif node1.size > self.cut_off_limit or \
449 node2.size > self.cut_off_limit:
453 node2.size > self.cut_off_limit:
450 c.cur_diff = ''
454 c.cur_diff = ''
451 c.big_diff = True
455 c.big_diff = True
452
456
453 else:
457 else:
454 diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2),
458 diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2),
455 format='gitdiff')
459 format='gitdiff')
456 c.cur_diff = diff.as_html()
460 c.cur_diff = diff.as_html()
457
461
458 if not c.cur_diff and not c.big_diff:
462 if not c.cur_diff and not c.big_diff:
459 c.no_changes = True
463 c.no_changes = True
460 return render('files/file_diff.html')
464 return render('files/file_diff.html')
461
465
462 def _get_node_history(self, cs, f_path):
466 def _get_node_history(self, cs, f_path):
463 changesets = cs.get_file_history(f_path)
467 changesets = cs.get_file_history(f_path)
464 hist_l = []
468 hist_l = []
465
469
466 changesets_group = ([], _("Changesets"))
470 changesets_group = ([], _("Changesets"))
467 branches_group = ([], _("Branches"))
471 branches_group = ([], _("Branches"))
468 tags_group = ([], _("Tags"))
472 tags_group = ([], _("Tags"))
469
473
470 for chs in changesets:
474 for chs in changesets:
471 n_desc = 'r%s:%s' % (chs.revision, chs.short_id)
475 n_desc = 'r%s:%s' % (chs.revision, chs.short_id)
472 changesets_group[0].append((chs.raw_id, n_desc,))
476 changesets_group[0].append((chs.raw_id, n_desc,))
473
477
474 hist_l.append(changesets_group)
478 hist_l.append(changesets_group)
475
479
476 for name, chs in c.rhodecode_repo.branches.items():
480 for name, chs in c.rhodecode_repo.branches.items():
477 #chs = chs.split(':')[-1]
481 #chs = chs.split(':')[-1]
478 branches_group[0].append((chs, name),)
482 branches_group[0].append((chs, name),)
479 hist_l.append(branches_group)
483 hist_l.append(branches_group)
480
484
481 for name, chs in c.rhodecode_repo.tags.items():
485 for name, chs in c.rhodecode_repo.tags.items():
482 #chs = chs.split(':')[-1]
486 #chs = chs.split(':')[-1]
483 tags_group[0].append((chs, name),)
487 tags_group[0].append((chs, name),)
484 hist_l.append(tags_group)
488 hist_l.append(tags_group)
485
489
486 return hist_l
490 return hist_l
487
491
488 @jsonify
492 @jsonify
489 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
493 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
490 'repository.admin')
494 'repository.admin')
491 def nodelist(self, repo_name, revision, f_path):
495 def nodelist(self, repo_name, revision, f_path):
492 if request.environ.get('HTTP_X_PARTIAL_XHR'):
496 if request.environ.get('HTTP_X_PARTIAL_XHR'):
493 cs = self.__get_cs_or_redirect(revision, repo_name)
497 cs = self.__get_cs_or_redirect(revision, repo_name)
494 _d, _f = self.__get_paths(cs, f_path)
498 _d, _f = self.__get_paths(cs, f_path)
495 return _d + _f
499 return _d + _f
496
500
@@ -1,85 +1,86 b''
1 <%inherit file="/base/base.html"/>
1 <%inherit file="/base/base.html"/>
2
2
3 <%def name="title()">
3 <%def name="title()">
4 ${c.repo_name} ${_('Edit file')} - ${c.rhodecode_name}
4 ${c.repo_name} ${_('Edit file')} - ${c.rhodecode_name}
5 </%def>
5 </%def>
6
6
7 <%def name="js_extra()">
7 <%def name="js_extra()">
8 <script type="text/javascript" src="${h.url('/js/codemirror.js')}"></script>
8 <script type="text/javascript" src="${h.url('/js/codemirror.js')}"></script>
9 </%def>
9 </%def>
10 <%def name="css_extra()">
10 <%def name="css_extra()">
11 <link rel="stylesheet" type="text/css" href="${h.url('/css/codemirror.css')}"/>
11 <link rel="stylesheet" type="text/css" href="${h.url('/css/codemirror.css')}"/>
12 </%def>
12 </%def>
13
13
14 <%def name="breadcrumbs_links()">
14 <%def name="breadcrumbs_links()">
15 ${h.link_to(u'Home',h.url('/'))}
15 ${h.link_to(u'Home',h.url('/'))}
16 &raquo;
16 &raquo;
17 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
17 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
18 &raquo;
18 &raquo;
19 ${_('add file')} @ R${c.cs.revision}:${h.short_id(c.cs.raw_id)}
19 ${_('add file')} @ R${c.cs.revision}:${h.short_id(c.cs.raw_id)}
20 </%def>
20 </%def>
21
21
22 <%def name="page_nav()">
22 <%def name="page_nav()">
23 ${self.menu('files')}
23 ${self.menu('files')}
24 </%def>
24 </%def>
25 <%def name="main()">
25 <%def name="main()">
26 <div class="box">
26 <div class="box">
27 <!-- box / title -->
27 <!-- box / title -->
28 <div class="title">
28 <div class="title">
29 ${self.breadcrumbs()}
29 ${self.breadcrumbs()}
30 <ul class="links">
30 <ul class="links">
31 <li>
31 <li>
32 <span style="text-transform: uppercase;">
32 <span style="text-transform: uppercase;">
33 <a href="#">${_('branch')}: ${c.cs.branch}</a></span>
33 <a href="#">${_('branch')}: ${c.cs.branch}</a></span>
34 </li>
34 </li>
35 </ul>
35 </ul>
36 </div>
36 </div>
37 <div class="table">
37 <div class="table">
38 <div id="files_data">
38 <div id="files_data">
39 ${h.form(h.url.current(),method='post',id='eform')}
39 ${h.form(h.url.current(),method='post',id='eform')}
40 <h3>${_('Add new file')}</h3>
40 <h3>${_('Add new file')}</h3>
41 <div class="form">
41 <div class="form">
42 <div class="fields">
42 <div class="fields">
43 <div class="field">
43 <div class="field">
44 <div class="label">
44 <div class="label">
45 <label for="location">${_('Location')}</label>
45 <label for="location">${_('Location')}</label>
46 </div>
46 </div>
47 <div class="input">
47 <div class="input">
48 <input type="text" value="${c.f_path}" size="30" name="location" id="location">
48 <input type="text" value="${c.f_path}" size="30" name="location" id="location">
49 ${_('use / to separate directories')}
49 </div>
50 </div>
50 </div>
51 </div>
51
52
52 <div class="field">
53 <div class="field">
53 <div class="label">
54 <div class="label">
54 <label for="filename">${_('File Name')}:</label>
55 <label for="filename">${_('File Name')}:</label>
55 </div>
56 </div>
56 <div class="input">
57 <div class="input">
57 <input type="text" value="" size="30" name="filename" id="filename">
58 <input type="text" value="" size="30" name="filename" id="filename">
58 </div>
59 </div>
59 </div>
60 </div>
60 </div>
61 </div>
61 </div>
62 </div>
62 <div id="body" class="codeblock">
63 <div id="body" class="codeblock">
63 <pre id="editor_pre"></pre>
64 <pre id="editor_pre"></pre>
64 <textarea id="editor" name="content" style="display:none"></textarea>
65 <textarea id="editor" name="content" style="display:none"></textarea>
65 <div style="padding-top: 10px;">${_('commit message')}</div>
66 <div style="padding: 10px;color:#666666">${_('commit message')}</div>
66 <textarea id="commit" name="message" style="height: 100px;width: 99%"></textarea>
67 <textarea id="commit" name="message" style="height: 100px;width: 99%"></textarea>
67 </div>
68 </div>
68 <div style="text-align: right;padding-top: 5px">
69 <div style="text-align: right;padding-top: 5px">
69 <input id="reset" type="button" value="${_('Reset')}" class="ui-button-small" />
70 <input id="reset" type="button" value="${_('Reset')}" class="ui-button-small" />
70 ${h.submit('commit',_('Commit changes'),class_="ui-button-small-blue")}
71 ${h.submit('commit',_('Commit changes'),class_="ui-button-small-blue")}
71 </div>
72 </div>
72 ${h.end_form()}
73 ${h.end_form()}
73 <script type="text/javascript">
74 <script type="text/javascript">
74 var myCodeMirror = CodeMirror.fromTextArea(YUD.get('editor'),{
75 var myCodeMirror = CodeMirror.fromTextArea(YUD.get('editor'),{
75 mode: "null",
76 mode: "null",
76 lineNumbers:true
77 lineNumbers:true
77 });
78 });
78 YUE.on('reset','click',function(){
79 YUE.on('reset','click',function(){
79 window.location="${h.url('files_home',repo_name=c.repo_name,revision=c.cs.revision,f_path=c.f_path)}";
80 window.location="${h.url('files_home',repo_name=c.repo_name,revision=c.cs.revision,f_path=c.f_path)}";
80 })
81 })
81 </script>
82 </script>
82 </div>
83 </div>
83 </div>
84 </div>
84 </div>
85 </div>
85 </%def> No newline at end of file
86 </%def>
@@ -1,66 +1,64 b''
1 <%inherit file="/base/base.html"/>
1 <%inherit file="/base/base.html"/>
2
2
3 <%def name="title()">
3 <%def name="title()">
4 ${c.repo_name} ${_('Edit file')} - ${c.rhodecode_name}
4 ${c.repo_name} ${_('Edit file')} - ${c.rhodecode_name}
5 </%def>
5 </%def>
6
6
7 <%def name="js_extra()">
7 <%def name="js_extra()">
8 <script type="text/javascript" src="${h.url('/js/codemirror.js')}"></script>
8 <script type="text/javascript" src="${h.url('/js/codemirror.js')}"></script>
9 </%def>
9 </%def>
10 <%def name="css_extra()">
10 <%def name="css_extra()">
11 <link rel="stylesheet" type="text/css" href="${h.url('/css/codemirror.css')}"/>
11 <link rel="stylesheet" type="text/css" href="${h.url('/css/codemirror.css')}"/>
12 </%def>
12 </%def>
13
13
14 <%def name="breadcrumbs_links()">
14 <%def name="breadcrumbs_links()">
15 ${h.link_to(u'Home',h.url('/'))}
15 ${h.link_to(u'Home',h.url('/'))}
16 &raquo;
16 &raquo;
17 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
17 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
18 &raquo;
18 &raquo;
19 ${_('edit file')} @ R${c.cs.revision}:${h.short_id(c.cs.raw_id)}
19 ${_('edit file')} @ R${c.cs.revision}:${h.short_id(c.cs.raw_id)}
20 </%def>
20 </%def>
21
21
22 <%def name="page_nav()">
22 <%def name="page_nav()">
23 ${self.menu('files')}
23 ${self.menu('files')}
24 </%def>
24 </%def>
25 <%def name="main()">
25 <%def name="main()">
26 <div class="box">
26 <div class="box">
27 <!-- box / title -->
27 <!-- box / title -->
28 <div class="title">
28 <div class="title">
29 ${self.breadcrumbs()}
29 ${self.breadcrumbs()}
30 <ul class="links">
30 <ul class="links">
31 <li>
31 <li>
32 <span style="text-transform: uppercase;">
32 <span style="text-transform: uppercase;">
33 <a href="#">${_('branch')}: ${c.cs.branch}</a></span>
33 <a href="#">${_('branch')}: ${c.cs.branch}</a></span>
34 </li>
34 </li>
35 </ul>
35 </ul>
36 </div>
36 </div>
37 <div class="table">
37 <div class="table">
38 <div id="files_data">
38 <div id="files_data">
39 <h3 class="files_location">${_('Location')}: ${h.files_breadcrumbs(c.repo_name,c.cs.revision,c.file.path)}</h3>
39 <h3 class="files_location">${_('Location')}: ${h.files_breadcrumbs(c.repo_name,c.cs.revision,c.file.path)}</h3>
40 ${h.form(h.url.current(),method='post',id='eform')}
40 ${h.form(h.url.current(),method='post',id='eform')}
41 <div id="body" class="codeblock">
41 <div id="body" class="codeblock">
42 <pre id="editor_pre"></pre>
42 <pre id="editor_pre"></pre>
43 <textarea id="editor" name="content" style="display:none">${c.file.content|n}</textarea>
43 <textarea id="editor" name="content" style="display:none">${c.file.content|n}</textarea>
44
44 <div style="padding: 10px;color:#666666">${_('commit message')}</div>
45 <div style="padding-top: 10px;">${_('commit message')}</div>
46 <textarea id="commit" name="message" style="height: 100px;width: 99%"></textarea>
45 <textarea id="commit" name="message" style="height: 100px;width: 99%"></textarea>
47
48 </div>
46 </div>
49 <div style="text-align: right;padding-top: 5px">
47 <div style="text-align: right;padding-top: 5px">
50 <input id="reset" type="button" value="${_('Reset')}" class="ui-button-small" />
48 <input id="reset" type="button" value="${_('Reset')}" class="ui-button-small" />
51 ${h.submit('commit',_('Commit changes'),class_="ui-button-small-blue")}
49 ${h.submit('commit',_('Commit changes'),class_="ui-button-small-blue")}
52 </div>
50 </div>
53 ${h.end_form()}
51 ${h.end_form()}
54 <script type="text/javascript">
52 <script type="text/javascript">
55 var myCodeMirror = CodeMirror.fromTextArea(YUD.get('editor'),{
53 var myCodeMirror = CodeMirror.fromTextArea(YUD.get('editor'),{
56 mode: "null",
54 mode: "null",
57 lineNumbers:true
55 lineNumbers:true
58 });
56 });
59 YUE.on('reset','click',function(){
57 YUE.on('reset','click',function(){
60 window.location="${h.url('files_home',repo_name=c.repo_name,revision=c.cs.revision,f_path=c.file.path)}";
58 window.location="${h.url('files_home',repo_name=c.repo_name,revision=c.cs.revision,f_path=c.file.path)}";
61 })
59 })
62 </script>
60 </script>
63 </div>
61 </div>
64 </div>
62 </div>
65 </div>
63 </div>
66 </%def> No newline at end of file
64 </%def>
General Comments 0
You need to be logged in to leave comments. Login now