##// END OF EJS Templates
added more strict checks for file path in add file controller
marcink -
r3559:328eb707 beta
parent child Browse files
Show More
@@ -1,641 +1,647 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
35 from rhodecode.lib.utils import jsonify
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
40 from rhodecode.lib.compat import OrderedDict
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
59
60
60
61 log = logging.getLogger(__name__)
61 log = logging.getLogger(__name__)
62
62
63
63
64 class FilesController(BaseRepoController):
64 class FilesController(BaseRepoController):
65
65
66 def __before__(self):
66 def __before__(self):
67 super(FilesController, self).__before__()
67 super(FilesController, self).__before__()
68 c.cut_off_limit = self.cut_off_limit
68 c.cut_off_limit = self.cut_off_limit
69
69
70 def __get_cs_or_redirect(self, rev, repo_name, redirect_after=True):
70 def __get_cs_or_redirect(self, rev, repo_name, redirect_after=True):
71 """
71 """
72 Safe way to get changeset if error occur it redirects to tip with
72 Safe way to get changeset if error occur it redirects to tip with
73 proper message
73 proper message
74
74
75 :param rev: revision to fetch
75 :param rev: revision to fetch
76 :param repo_name: repo name to redirect after
76 :param repo_name: repo name to redirect after
77 """
77 """
78
78
79 try:
79 try:
80 return c.rhodecode_repo.get_changeset(rev)
80 return c.rhodecode_repo.get_changeset(rev)
81 except EmptyRepositoryError, e:
81 except EmptyRepositoryError, e:
82 if not redirect_after:
82 if not redirect_after:
83 return None
83 return None
84 url_ = url('files_add_home',
84 url_ = url('files_add_home',
85 repo_name=c.repo_name,
85 repo_name=c.repo_name,
86 revision=0, f_path='')
86 revision=0, f_path='')
87 add_new = h.link_to(_('click here to add new file'), url_)
87 add_new = h.link_to(_('click here to add new file'), url_)
88 h.flash(h.literal(_('There are no files yet %s') % add_new),
88 h.flash(h.literal(_('There are no files yet %s') % add_new),
89 category='warning')
89 category='warning')
90 redirect(h.url('summary_home', repo_name=repo_name))
90 redirect(h.url('summary_home', repo_name=repo_name))
91
91
92 except RepositoryError, e:
92 except RepositoryError, e:
93 h.flash(str(e), category='warning')
93 h.flash(str(e), category='warning')
94 redirect(h.url('files_home', repo_name=repo_name, revision='tip'))
94 redirect(h.url('files_home', repo_name=repo_name, revision='tip'))
95
95
96 def __get_filenode_or_redirect(self, repo_name, cs, path):
96 def __get_filenode_or_redirect(self, repo_name, cs, path):
97 """
97 """
98 Returns file_node, if error occurs or given path is directory,
98 Returns file_node, if error occurs or given path is directory,
99 it'll redirect to top level path
99 it'll redirect to top level path
100
100
101 :param repo_name: repo_name
101 :param repo_name: repo_name
102 :param cs: given changeset
102 :param cs: given changeset
103 :param path: path to lookup
103 :param path: path to lookup
104 """
104 """
105
105
106 try:
106 try:
107 file_node = cs.get_node(path)
107 file_node = cs.get_node(path)
108 if file_node.is_dir():
108 if file_node.is_dir():
109 raise RepositoryError('given path is a directory')
109 raise RepositoryError('given path is a directory')
110 except RepositoryError, e:
110 except RepositoryError, e:
111 h.flash(str(e), category='warning')
111 h.flash(str(e), category='warning')
112 redirect(h.url('files_home', repo_name=repo_name,
112 redirect(h.url('files_home', repo_name=repo_name,
113 revision=cs.raw_id))
113 revision=cs.raw_id))
114
114
115 return file_node
115 return file_node
116
116
117 @LoginRequired()
117 @LoginRequired()
118 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
118 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
119 'repository.admin')
119 'repository.admin')
120 def index(self, repo_name, revision, f_path, annotate=False):
120 def index(self, repo_name, revision, f_path, annotate=False):
121 # redirect to given revision from form if given
121 # redirect to given revision from form if given
122 post_revision = request.POST.get('at_rev', None)
122 post_revision = request.POST.get('at_rev', None)
123 if post_revision:
123 if post_revision:
124 cs = self.__get_cs_or_redirect(post_revision, repo_name)
124 cs = self.__get_cs_or_redirect(post_revision, repo_name)
125 redirect(url('files_home', repo_name=c.repo_name,
125 redirect(url('files_home', repo_name=c.repo_name,
126 revision=cs.raw_id, f_path=f_path))
126 revision=cs.raw_id, f_path=f_path))
127
127
128 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
128 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
129 c.branch = request.GET.get('branch', None)
129 c.branch = request.GET.get('branch', None)
130 c.f_path = f_path
130 c.f_path = f_path
131 c.annotate = annotate
131 c.annotate = annotate
132 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
132 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
133 cur_rev = c.changeset.revision
133 cur_rev = c.changeset.revision
134
134
135 # prev link
135 # prev link
136 try:
136 try:
137 prev_rev = c.rhodecode_repo.get_changeset(cur_rev).prev(c.branch)
137 prev_rev = c.rhodecode_repo.get_changeset(cur_rev).prev(c.branch)
138 c.url_prev = url('files_home', repo_name=c.repo_name,
138 c.url_prev = url('files_home', repo_name=c.repo_name,
139 revision=prev_rev.raw_id, f_path=f_path)
139 revision=prev_rev.raw_id, f_path=f_path)
140 if c.branch:
140 if c.branch:
141 c.url_prev += '?branch=%s' % c.branch
141 c.url_prev += '?branch=%s' % c.branch
142 except (ChangesetDoesNotExistError, VCSError):
142 except (ChangesetDoesNotExistError, VCSError):
143 c.url_prev = '#'
143 c.url_prev = '#'
144
144
145 # next link
145 # next link
146 try:
146 try:
147 next_rev = c.rhodecode_repo.get_changeset(cur_rev).next(c.branch)
147 next_rev = c.rhodecode_repo.get_changeset(cur_rev).next(c.branch)
148 c.url_next = url('files_home', repo_name=c.repo_name,
148 c.url_next = url('files_home', repo_name=c.repo_name,
149 revision=next_rev.raw_id, f_path=f_path)
149 revision=next_rev.raw_id, f_path=f_path)
150 if c.branch:
150 if c.branch:
151 c.url_next += '?branch=%s' % c.branch
151 c.url_next += '?branch=%s' % c.branch
152 except (ChangesetDoesNotExistError, VCSError):
152 except (ChangesetDoesNotExistError, VCSError):
153 c.url_next = '#'
153 c.url_next = '#'
154
154
155 # files or dirs
155 # files or dirs
156 try:
156 try:
157 c.file = c.changeset.get_node(f_path)
157 c.file = c.changeset.get_node(f_path)
158
158
159 if c.file.is_file():
159 if c.file.is_file():
160 c.load_full_history = False
160 c.load_full_history = False
161 file_last_cs = c.file.last_changeset
161 file_last_cs = c.file.last_changeset
162 c.file_changeset = (c.changeset
162 c.file_changeset = (c.changeset
163 if c.changeset.revision < file_last_cs.revision
163 if c.changeset.revision < file_last_cs.revision
164 else file_last_cs)
164 else file_last_cs)
165 #determine if we're on branch head
165 #determine if we're on branch head
166 _branches = c.rhodecode_repo.branches
166 _branches = c.rhodecode_repo.branches
167 c.on_branch_head = revision in _branches.keys() + _branches.values()
167 c.on_branch_head = revision in _branches.keys() + _branches.values()
168 _hist = []
168 _hist = []
169 c.file_history = []
169 c.file_history = []
170 if c.load_full_history:
170 if c.load_full_history:
171 c.file_history, _hist = self._get_node_history(c.changeset, f_path)
171 c.file_history, _hist = self._get_node_history(c.changeset, f_path)
172
172
173 c.authors = []
173 c.authors = []
174 for a in set([x.author for x in _hist]):
174 for a in set([x.author for x in _hist]):
175 c.authors.append((h.email(a), h.person(a)))
175 c.authors.append((h.email(a), h.person(a)))
176 else:
176 else:
177 c.authors = c.file_history = []
177 c.authors = c.file_history = []
178 except RepositoryError, e:
178 except RepositoryError, e:
179 h.flash(str(e), category='warning')
179 h.flash(str(e), category='warning')
180 redirect(h.url('files_home', repo_name=repo_name,
180 redirect(h.url('files_home', repo_name=repo_name,
181 revision='tip'))
181 revision='tip'))
182
182
183 if request.environ.get('HTTP_X_PARTIAL_XHR'):
183 if request.environ.get('HTTP_X_PARTIAL_XHR'):
184 return render('files/files_ypjax.html')
184 return render('files/files_ypjax.html')
185
185
186 return render('files/files.html')
186 return render('files/files.html')
187
187
188 def history(self, repo_name, revision, f_path, annotate=False):
188 def history(self, repo_name, revision, f_path, annotate=False):
189 if request.environ.get('HTTP_X_PARTIAL_XHR'):
189 if request.environ.get('HTTP_X_PARTIAL_XHR'):
190 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
190 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
191 c.f_path = f_path
191 c.f_path = f_path
192 c.annotate = annotate
192 c.annotate = annotate
193 c.file = c.changeset.get_node(f_path)
193 c.file = c.changeset.get_node(f_path)
194 if c.file.is_file():
194 if c.file.is_file():
195 file_last_cs = c.file.last_changeset
195 file_last_cs = c.file.last_changeset
196 c.file_changeset = (c.changeset
196 c.file_changeset = (c.changeset
197 if c.changeset.revision < file_last_cs.revision
197 if c.changeset.revision < file_last_cs.revision
198 else file_last_cs)
198 else file_last_cs)
199 c.file_history, _hist = self._get_node_history(c.changeset, f_path)
199 c.file_history, _hist = self._get_node_history(c.changeset, f_path)
200 c.authors = []
200 c.authors = []
201 for a in set([x.author for x in _hist]):
201 for a in set([x.author for x in _hist]):
202 c.authors.append((h.email(a), h.person(a)))
202 c.authors.append((h.email(a), h.person(a)))
203 return render('files/files_history_box.html')
203 return render('files/files_history_box.html')
204
204
205 @LoginRequired()
205 @LoginRequired()
206 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
206 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
207 'repository.admin')
207 'repository.admin')
208 def rawfile(self, repo_name, revision, f_path):
208 def rawfile(self, repo_name, revision, f_path):
209 cs = self.__get_cs_or_redirect(revision, repo_name)
209 cs = self.__get_cs_or_redirect(revision, repo_name)
210 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
210 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
211
211
212 response.content_disposition = 'attachment; filename=%s' % \
212 response.content_disposition = 'attachment; filename=%s' % \
213 safe_str(f_path.split(Repository.url_sep())[-1])
213 safe_str(f_path.split(Repository.url_sep())[-1])
214
214
215 response.content_type = file_node.mimetype
215 response.content_type = file_node.mimetype
216 return file_node.content
216 return file_node.content
217
217
218 @LoginRequired()
218 @LoginRequired()
219 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
219 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
220 'repository.admin')
220 'repository.admin')
221 def raw(self, repo_name, revision, f_path):
221 def raw(self, repo_name, revision, f_path):
222 cs = self.__get_cs_or_redirect(revision, repo_name)
222 cs = self.__get_cs_or_redirect(revision, repo_name)
223 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
223 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
224
224
225 raw_mimetype_mapping = {
225 raw_mimetype_mapping = {
226 # map original mimetype to a mimetype used for "show as raw"
226 # map original mimetype to a mimetype used for "show as raw"
227 # you can also provide a content-disposition to override the
227 # you can also provide a content-disposition to override the
228 # default "attachment" disposition.
228 # default "attachment" disposition.
229 # orig_type: (new_type, new_dispo)
229 # orig_type: (new_type, new_dispo)
230
230
231 # show images inline:
231 # show images inline:
232 'image/x-icon': ('image/x-icon', 'inline'),
232 'image/x-icon': ('image/x-icon', 'inline'),
233 'image/png': ('image/png', 'inline'),
233 'image/png': ('image/png', 'inline'),
234 'image/gif': ('image/gif', 'inline'),
234 'image/gif': ('image/gif', 'inline'),
235 'image/jpeg': ('image/jpeg', 'inline'),
235 'image/jpeg': ('image/jpeg', 'inline'),
236 'image/svg+xml': ('image/svg+xml', 'inline'),
236 'image/svg+xml': ('image/svg+xml', 'inline'),
237 }
237 }
238
238
239 mimetype = file_node.mimetype
239 mimetype = file_node.mimetype
240 try:
240 try:
241 mimetype, dispo = raw_mimetype_mapping[mimetype]
241 mimetype, dispo = raw_mimetype_mapping[mimetype]
242 except KeyError:
242 except KeyError:
243 # we don't know anything special about this, handle it safely
243 # we don't know anything special about this, handle it safely
244 if file_node.is_binary:
244 if file_node.is_binary:
245 # do same as download raw for binary files
245 # do same as download raw for binary files
246 mimetype, dispo = 'application/octet-stream', 'attachment'
246 mimetype, dispo = 'application/octet-stream', 'attachment'
247 else:
247 else:
248 # do not just use the original mimetype, but force text/plain,
248 # do not just use the original mimetype, but force text/plain,
249 # otherwise it would serve text/html and that might be unsafe.
249 # otherwise it would serve text/html and that might be unsafe.
250 # Note: underlying vcs library fakes text/plain mimetype if the
250 # Note: underlying vcs library fakes text/plain mimetype if the
251 # mimetype can not be determined and it thinks it is not
251 # mimetype can not be determined and it thinks it is not
252 # binary.This might lead to erroneous text display in some
252 # binary.This might lead to erroneous text display in some
253 # cases, but helps in other cases, like with text files
253 # cases, but helps in other cases, like with text files
254 # without extension.
254 # without extension.
255 mimetype, dispo = 'text/plain', 'inline'
255 mimetype, dispo = 'text/plain', 'inline'
256
256
257 if dispo == 'attachment':
257 if dispo == 'attachment':
258 dispo = 'attachment; filename=%s' % \
258 dispo = 'attachment; filename=%s' % \
259 safe_str(f_path.split(os.sep)[-1])
259 safe_str(f_path.split(os.sep)[-1])
260
260
261 response.content_disposition = dispo
261 response.content_disposition = dispo
262 response.content_type = mimetype
262 response.content_type = mimetype
263 return file_node.content
263 return file_node.content
264
264
265 @LoginRequired()
265 @LoginRequired()
266 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
266 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
267 def edit(self, repo_name, revision, f_path):
267 def edit(self, repo_name, revision, f_path):
268 repo = c.rhodecode_db_repo
268 repo = c.rhodecode_db_repo
269 if repo.enable_locking and repo.locked[0]:
269 if repo.enable_locking and repo.locked[0]:
270 h.flash(_('This repository is has been locked by %s on %s')
270 h.flash(_('This repository is has been locked by %s on %s')
271 % (h.person_by_id(repo.locked[0]),
271 % (h.person_by_id(repo.locked[0]),
272 h.fmt_date(h.time_to_datetime(repo.locked[1]))),
272 h.fmt_date(h.time_to_datetime(repo.locked[1]))),
273 'warning')
273 'warning')
274 return redirect(h.url('files_home',
274 return redirect(h.url('files_home',
275 repo_name=repo_name, revision='tip'))
275 repo_name=repo_name, revision='tip'))
276
276
277 # check if revision is a branch identifier- basically we cannot
277 # check if revision is a branch identifier- basically we cannot
278 # create multiple heads via file editing
278 # create multiple heads via file editing
279 _branches = repo.scm_instance.branches
279 _branches = repo.scm_instance.branches
280 # check if revision is a branch name or branch hash
280 # check if revision is a branch name or branch hash
281 if revision not in _branches.keys() + _branches.values():
281 if revision not in _branches.keys() + _branches.values():
282 h.flash(_('You can only edit files with revision '
282 h.flash(_('You can only edit files with revision '
283 'being a valid branch '), category='warning')
283 'being a valid branch '), category='warning')
284 return redirect(h.url('files_home',
284 return redirect(h.url('files_home',
285 repo_name=repo_name, revision='tip',
285 repo_name=repo_name, revision='tip',
286 f_path=f_path))
286 f_path=f_path))
287
287
288 r_post = request.POST
288 r_post = request.POST
289
289
290 c.cs = self.__get_cs_or_redirect(revision, repo_name)
290 c.cs = self.__get_cs_or_redirect(revision, repo_name)
291 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
291 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
292
292
293 if c.file.is_binary:
293 if c.file.is_binary:
294 return redirect(url('files_home', repo_name=c.repo_name,
294 return redirect(url('files_home', repo_name=c.repo_name,
295 revision=c.cs.raw_id, f_path=f_path))
295 revision=c.cs.raw_id, f_path=f_path))
296 c.default_message = _('Edited file %s via RhodeCode') % (f_path)
296 c.default_message = _('Edited file %s via RhodeCode') % (f_path)
297 c.f_path = f_path
297 c.f_path = f_path
298
298
299 if r_post:
299 if r_post:
300
300
301 old_content = c.file.content
301 old_content = c.file.content
302 sl = old_content.splitlines(1)
302 sl = old_content.splitlines(1)
303 first_line = sl[0] if sl else ''
303 first_line = sl[0] if sl else ''
304 # modes: 0 - Unix, 1 - Mac, 2 - DOS
304 # modes: 0 - Unix, 1 - Mac, 2 - DOS
305 mode = detect_mode(first_line, 0)
305 mode = detect_mode(first_line, 0)
306 content = convert_line_endings(r_post.get('content'), mode)
306 content = convert_line_endings(r_post.get('content'), mode)
307
307
308 message = r_post.get('message') or c.default_message
308 message = r_post.get('message') or c.default_message
309 author = self.rhodecode_user.full_contact
309 author = self.rhodecode_user.full_contact
310
310
311 if content == old_content:
311 if content == old_content:
312 h.flash(_('No changes'),
312 h.flash(_('No changes'),
313 category='warning')
313 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 location = r_post.get('location')
360 location = r_post.get('location')
360 filename = r_post.get('filename')
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 node_path = os.path.join(location, filename)
368 author = self.rhodecode_user.full_contact
369
370 if not content:
367 if not content:
371 h.flash(_('No content'), category='warning')
368 h.flash(_('No content'), category='warning')
372 return redirect(url('changeset_home', repo_name=c.repo_name,
369 return redirect(url('changeset_home', repo_name=c.repo_name,
373 revision='tip'))
370 revision='tip'))
374 if not filename:
371 if not filename:
375 h.flash(_('No filename'), category='warning')
372 h.flash(_('No filename'), category='warning')
376 return redirect(url('changeset_home', repo_name=c.repo_name,
373 return redirect(url('changeset_home', repo_name=c.repo_name,
377 revision='tip'))
374 revision='tip'))
375 if location.startswith('/') or location.startswith('.') or '../' in location:
376 h.flash(_('location must be relative path and must not '
377 'contain .. in path'), category='warning')
378 return redirect(url('changeset_home', repo_name=c.repo_name,
379 revision='tip'))
380 location = os.path.normpath(location)
381 filename = os.path.basename(filename)
382 node_path = os.path.join(location, filename)
383 author = self.rhodecode_user.full_contact
378
384
379 try:
385 try:
380 self.scm_model.create_node(repo=c.rhodecode_repo,
386 self.scm_model.create_node(repo=c.rhodecode_repo,
381 repo_name=repo_name, cs=c.cs,
387 repo_name=repo_name, cs=c.cs,
382 user=self.rhodecode_user.user_id,
388 user=self.rhodecode_user.user_id,
383 author=author, message=message,
389 author=author, message=message,
384 content=content, f_path=node_path)
390 content=content, f_path=node_path)
385 h.flash(_('Successfully committed to %s') % node_path,
391 h.flash(_('Successfully committed to %s') % node_path,
386 category='success')
392 category='success')
387 except NodeAlreadyExistsError, e:
393 except (NodeError, NodeAlreadyExistsError), e:
388 h.flash(_(e), category='error')
394 h.flash(_(e), category='error')
389 except Exception:
395 except Exception:
390 log.error(traceback.format_exc())
396 log.error(traceback.format_exc())
391 h.flash(_('Error occurred during commit'), category='error')
397 h.flash(_('Error occurred during commit'), category='error')
392 return redirect(url('changeset_home',
398 return redirect(url('changeset_home',
393 repo_name=c.repo_name, revision='tip'))
399 repo_name=c.repo_name, revision='tip'))
394
400
395 return render('files/files_add.html')
401 return render('files/files_add.html')
396
402
397 @LoginRequired()
403 @LoginRequired()
398 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
404 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
399 'repository.admin')
405 'repository.admin')
400 def archivefile(self, repo_name, fname):
406 def archivefile(self, repo_name, fname):
401
407
402 fileformat = None
408 fileformat = None
403 revision = None
409 revision = None
404 ext = None
410 ext = None
405 subrepos = request.GET.get('subrepos') == 'true'
411 subrepos = request.GET.get('subrepos') == 'true'
406
412
407 for a_type, ext_data in settings.ARCHIVE_SPECS.items():
413 for a_type, ext_data in settings.ARCHIVE_SPECS.items():
408 archive_spec = fname.split(ext_data[1])
414 archive_spec = fname.split(ext_data[1])
409 if len(archive_spec) == 2 and archive_spec[1] == '':
415 if len(archive_spec) == 2 and archive_spec[1] == '':
410 fileformat = a_type or ext_data[1]
416 fileformat = a_type or ext_data[1]
411 revision = archive_spec[0]
417 revision = archive_spec[0]
412 ext = ext_data[1]
418 ext = ext_data[1]
413
419
414 try:
420 try:
415 dbrepo = RepoModel().get_by_repo_name(repo_name)
421 dbrepo = RepoModel().get_by_repo_name(repo_name)
416 if dbrepo.enable_downloads is False:
422 if dbrepo.enable_downloads is False:
417 return _('downloads disabled')
423 return _('downloads disabled')
418
424
419 if c.rhodecode_repo.alias == 'hg':
425 if c.rhodecode_repo.alias == 'hg':
420 # patch and reset hooks section of UI config to not run any
426 # patch and reset hooks section of UI config to not run any
421 # hooks on fetching archives with subrepos
427 # hooks on fetching archives with subrepos
422 for k, v in c.rhodecode_repo._repo.ui.configitems('hooks'):
428 for k, v in c.rhodecode_repo._repo.ui.configitems('hooks'):
423 c.rhodecode_repo._repo.ui.setconfig('hooks', k, None)
429 c.rhodecode_repo._repo.ui.setconfig('hooks', k, None)
424
430
425 cs = c.rhodecode_repo.get_changeset(revision)
431 cs = c.rhodecode_repo.get_changeset(revision)
426 content_type = settings.ARCHIVE_SPECS[fileformat][0]
432 content_type = settings.ARCHIVE_SPECS[fileformat][0]
427 except ChangesetDoesNotExistError:
433 except ChangesetDoesNotExistError:
428 return _('Unknown revision %s') % revision
434 return _('Unknown revision %s') % revision
429 except EmptyRepositoryError:
435 except EmptyRepositoryError:
430 return _('Empty repository')
436 return _('Empty repository')
431 except (ImproperArchiveTypeError, KeyError):
437 except (ImproperArchiveTypeError, KeyError):
432 return _('Unknown archive type')
438 return _('Unknown archive type')
433 # archive cache
439 # archive cache
434 from rhodecode import CONFIG
440 from rhodecode import CONFIG
435 rev_name = cs.raw_id[:12]
441 rev_name = cs.raw_id[:12]
436 archive_name = '%s-%s%s' % (safe_str(repo_name.replace('/', '_')),
442 archive_name = '%s-%s%s' % (safe_str(repo_name.replace('/', '_')),
437 safe_str(rev_name), ext)
443 safe_str(rev_name), ext)
438
444
439 use_cached_archive = False # defines if we use cached version of archive
445 use_cached_archive = False # defines if we use cached version of archive
440 archive_cache_enabled = CONFIG.get('archive_cache_dir')
446 archive_cache_enabled = CONFIG.get('archive_cache_dir')
441 if not subrepos and archive_cache_enabled:
447 if not subrepos and archive_cache_enabled:
442 #check if we it's ok to write
448 #check if we it's ok to write
443 if not os.path.isdir(CONFIG['archive_cache_dir']):
449 if not os.path.isdir(CONFIG['archive_cache_dir']):
444 os.makedirs(CONFIG['archive_cache_dir'])
450 os.makedirs(CONFIG['archive_cache_dir'])
445 cached_archive_path = os.path.join(CONFIG['archive_cache_dir'], archive_name)
451 cached_archive_path = os.path.join(CONFIG['archive_cache_dir'], archive_name)
446 if os.path.isfile(cached_archive_path):
452 if os.path.isfile(cached_archive_path):
447 log.debug('Found cached archive in %s' % cached_archive_path)
453 log.debug('Found cached archive in %s' % cached_archive_path)
448 fd, archive = None, cached_archive_path
454 fd, archive = None, cached_archive_path
449 use_cached_archive = True
455 use_cached_archive = True
450 else:
456 else:
451 log.debug('Archive %s is not yet cached' % (archive_name))
457 log.debug('Archive %s is not yet cached' % (archive_name))
452
458
453 if not use_cached_archive:
459 if not use_cached_archive:
454 #generate new archive
460 #generate new archive
455 try:
461 try:
456 fd, archive = tempfile.mkstemp()
462 fd, archive = tempfile.mkstemp()
457 t = open(archive, 'wb')
463 t = open(archive, 'wb')
458 log.debug('Creating new temp archive in %s' % archive)
464 log.debug('Creating new temp archive in %s' % archive)
459 cs.fill_archive(stream=t, kind=fileformat, subrepos=subrepos)
465 cs.fill_archive(stream=t, kind=fileformat, subrepos=subrepos)
460 if archive_cache_enabled:
466 if archive_cache_enabled:
461 #if we generated the archive and use cache rename that
467 #if we generated the archive and use cache rename that
462 log.debug('Storing new archive in %s' % cached_archive_path)
468 log.debug('Storing new archive in %s' % cached_archive_path)
463 shutil.move(archive, cached_archive_path)
469 shutil.move(archive, cached_archive_path)
464 archive = cached_archive_path
470 archive = cached_archive_path
465 finally:
471 finally:
466 t.close()
472 t.close()
467
473
468 def get_chunked_archive(archive):
474 def get_chunked_archive(archive):
469 stream = open(archive, 'rb')
475 stream = open(archive, 'rb')
470 while True:
476 while True:
471 data = stream.read(16 * 1024)
477 data = stream.read(16 * 1024)
472 if not data:
478 if not data:
473 stream.close()
479 stream.close()
474 if fd: # fd means we used temporary file
480 if fd: # fd means we used temporary file
475 os.close(fd)
481 os.close(fd)
476 if not archive_cache_enabled:
482 if not archive_cache_enabled:
477 log.debug('Destroing temp archive %s' % archive)
483 log.debug('Destroing temp archive %s' % archive)
478 os.remove(archive)
484 os.remove(archive)
479 break
485 break
480 yield data
486 yield data
481
487
482 response.content_disposition = str('attachment; filename=%s' % (archive_name))
488 response.content_disposition = str('attachment; filename=%s' % (archive_name))
483 response.content_type = str(content_type)
489 response.content_type = str(content_type)
484 return get_chunked_archive(archive)
490 return get_chunked_archive(archive)
485
491
486 @LoginRequired()
492 @LoginRequired()
487 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
493 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
488 'repository.admin')
494 'repository.admin')
489 def diff(self, repo_name, f_path):
495 def diff(self, repo_name, f_path):
490 ignore_whitespace = request.GET.get('ignorews') == '1'
496 ignore_whitespace = request.GET.get('ignorews') == '1'
491 line_context = request.GET.get('context', 3)
497 line_context = request.GET.get('context', 3)
492 diff1 = request.GET.get('diff1', '')
498 diff1 = request.GET.get('diff1', '')
493 diff2 = request.GET.get('diff2', '')
499 diff2 = request.GET.get('diff2', '')
494 c.action = request.GET.get('diff')
500 c.action = request.GET.get('diff')
495 c.no_changes = diff1 == diff2
501 c.no_changes = diff1 == diff2
496 c.f_path = f_path
502 c.f_path = f_path
497 c.big_diff = False
503 c.big_diff = False
498 c.anchor_url = anchor_url
504 c.anchor_url = anchor_url
499 c.ignorews_url = _ignorews_url
505 c.ignorews_url = _ignorews_url
500 c.context_url = _context_url
506 c.context_url = _context_url
501 c.changes = OrderedDict()
507 c.changes = OrderedDict()
502 c.changes[diff2] = []
508 c.changes[diff2] = []
503
509
504 #special case if we want a show rev only, it's impl here
510 #special case if we want a show rev only, it's impl here
505 #to reduce JS and callbacks
511 #to reduce JS and callbacks
506
512
507 if request.GET.get('show_rev'):
513 if request.GET.get('show_rev'):
508 if str2bool(request.GET.get('annotate', 'False')):
514 if str2bool(request.GET.get('annotate', 'False')):
509 _url = url('files_annotate_home', repo_name=c.repo_name,
515 _url = url('files_annotate_home', repo_name=c.repo_name,
510 revision=diff1, f_path=c.f_path)
516 revision=diff1, f_path=c.f_path)
511 else:
517 else:
512 _url = url('files_home', repo_name=c.repo_name,
518 _url = url('files_home', repo_name=c.repo_name,
513 revision=diff1, f_path=c.f_path)
519 revision=diff1, f_path=c.f_path)
514
520
515 return redirect(_url)
521 return redirect(_url)
516 try:
522 try:
517 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
523 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
518 c.changeset_1 = c.rhodecode_repo.get_changeset(diff1)
524 c.changeset_1 = c.rhodecode_repo.get_changeset(diff1)
519 try:
525 try:
520 node1 = c.changeset_1.get_node(f_path)
526 node1 = c.changeset_1.get_node(f_path)
521 if node1.is_dir():
527 if node1.is_dir():
522 raise NodeError('%s path is a %s not a file' % (node1, type(node1)))
528 raise NodeError('%s path is a %s not a file' % (node1, type(node1)))
523 except NodeDoesNotExistError:
529 except NodeDoesNotExistError:
524 c.changeset_1 = EmptyChangeset(cs=diff1,
530 c.changeset_1 = EmptyChangeset(cs=diff1,
525 revision=c.changeset_1.revision,
531 revision=c.changeset_1.revision,
526 repo=c.rhodecode_repo)
532 repo=c.rhodecode_repo)
527 node1 = FileNode(f_path, '', changeset=c.changeset_1)
533 node1 = FileNode(f_path, '', changeset=c.changeset_1)
528 else:
534 else:
529 c.changeset_1 = EmptyChangeset(repo=c.rhodecode_repo)
535 c.changeset_1 = EmptyChangeset(repo=c.rhodecode_repo)
530 node1 = FileNode(f_path, '', changeset=c.changeset_1)
536 node1 = FileNode(f_path, '', changeset=c.changeset_1)
531
537
532 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
538 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
533 c.changeset_2 = c.rhodecode_repo.get_changeset(diff2)
539 c.changeset_2 = c.rhodecode_repo.get_changeset(diff2)
534 try:
540 try:
535 node2 = c.changeset_2.get_node(f_path)
541 node2 = c.changeset_2.get_node(f_path)
536 raise NodeError('%s path is a %s not a file' % (node2, type(node2)))
542 raise NodeError('%s path is a %s not a file' % (node2, type(node2)))
537 except NodeDoesNotExistError:
543 except NodeDoesNotExistError:
538 c.changeset_2 = EmptyChangeset(cs=diff2,
544 c.changeset_2 = EmptyChangeset(cs=diff2,
539 revision=c.changeset_2.revision,
545 revision=c.changeset_2.revision,
540 repo=c.rhodecode_repo)
546 repo=c.rhodecode_repo)
541 node2 = FileNode(f_path, '', changeset=c.changeset_2)
547 node2 = FileNode(f_path, '', changeset=c.changeset_2)
542 else:
548 else:
543 c.changeset_2 = EmptyChangeset(repo=c.rhodecode_repo)
549 c.changeset_2 = EmptyChangeset(repo=c.rhodecode_repo)
544 node2 = FileNode(f_path, '', changeset=c.changeset_2)
550 node2 = FileNode(f_path, '', changeset=c.changeset_2)
545 except (RepositoryError, NodeError):
551 except (RepositoryError, NodeError):
546 log.error(traceback.format_exc())
552 log.error(traceback.format_exc())
547 return redirect(url('files_home', repo_name=c.repo_name,
553 return redirect(url('files_home', repo_name=c.repo_name,
548 f_path=f_path))
554 f_path=f_path))
549
555
550 if c.action == 'download':
556 if c.action == 'download':
551 _diff = diffs.get_gitdiff(node1, node2,
557 _diff = diffs.get_gitdiff(node1, node2,
552 ignore_whitespace=ignore_whitespace,
558 ignore_whitespace=ignore_whitespace,
553 context=line_context)
559 context=line_context)
554 diff = diffs.DiffProcessor(_diff, format='gitdiff')
560 diff = diffs.DiffProcessor(_diff, format='gitdiff')
555
561
556 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
562 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
557 response.content_type = 'text/plain'
563 response.content_type = 'text/plain'
558 response.content_disposition = (
564 response.content_disposition = (
559 'attachment; filename=%s' % diff_name
565 'attachment; filename=%s' % diff_name
560 )
566 )
561 return diff.as_raw()
567 return diff.as_raw()
562
568
563 elif c.action == 'raw':
569 elif c.action == 'raw':
564 _diff = diffs.get_gitdiff(node1, node2,
570 _diff = diffs.get_gitdiff(node1, node2,
565 ignore_whitespace=ignore_whitespace,
571 ignore_whitespace=ignore_whitespace,
566 context=line_context)
572 context=line_context)
567 diff = diffs.DiffProcessor(_diff, format='gitdiff')
573 diff = diffs.DiffProcessor(_diff, format='gitdiff')
568 response.content_type = 'text/plain'
574 response.content_type = 'text/plain'
569 return diff.as_raw()
575 return diff.as_raw()
570
576
571 else:
577 else:
572 fid = h.FID(diff2, node2.path)
578 fid = h.FID(diff2, node2.path)
573 line_context_lcl = get_line_ctx(fid, request.GET)
579 line_context_lcl = get_line_ctx(fid, request.GET)
574 ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
580 ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
575
581
576 lim = request.GET.get('fulldiff') or self.cut_off_limit
582 lim = request.GET.get('fulldiff') or self.cut_off_limit
577 _, cs1, cs2, diff, st = diffs.wrapped_diff(filenode_old=node1,
583 _, cs1, cs2, diff, st = diffs.wrapped_diff(filenode_old=node1,
578 filenode_new=node2,
584 filenode_new=node2,
579 cut_off_limit=lim,
585 cut_off_limit=lim,
580 ignore_whitespace=ign_whitespace_lcl,
586 ignore_whitespace=ign_whitespace_lcl,
581 line_context=line_context_lcl,
587 line_context=line_context_lcl,
582 enable_comments=False)
588 enable_comments=False)
583 op = ''
589 op = ''
584 filename = node1.path
590 filename = node1.path
585 cs_changes = {
591 cs_changes = {
586 'fid': [cs1, cs2, op, filename, diff, st]
592 'fid': [cs1, cs2, op, filename, diff, st]
587 }
593 }
588 c.changes = cs_changes
594 c.changes = cs_changes
589
595
590 return render('files/file_diff.html')
596 return render('files/file_diff.html')
591
597
592 def _get_node_history(self, cs, f_path, changesets=None):
598 def _get_node_history(self, cs, f_path, changesets=None):
593 """
599 """
594 get changesets history for given node
600 get changesets history for given node
595
601
596 :param cs: changeset to calculate history
602 :param cs: changeset to calculate history
597 :param f_path: path for node to calculate history for
603 :param f_path: path for node to calculate history for
598 :param changesets: if passed don't calculate history and take
604 :param changesets: if passed don't calculate history and take
599 changesets defined in this list
605 changesets defined in this list
600 """
606 """
601 # calculate history based on tip
607 # calculate history based on tip
602 tip_cs = c.rhodecode_repo.get_changeset()
608 tip_cs = c.rhodecode_repo.get_changeset()
603 if changesets is None:
609 if changesets is None:
604 try:
610 try:
605 changesets = tip_cs.get_file_history(f_path)
611 changesets = tip_cs.get_file_history(f_path)
606 except (NodeDoesNotExistError, ChangesetError):
612 except (NodeDoesNotExistError, ChangesetError):
607 #this node is not present at tip !
613 #this node is not present at tip !
608 changesets = cs.get_file_history(f_path)
614 changesets = cs.get_file_history(f_path)
609 hist_l = []
615 hist_l = []
610
616
611 changesets_group = ([], _("Changesets"))
617 changesets_group = ([], _("Changesets"))
612 branches_group = ([], _("Branches"))
618 branches_group = ([], _("Branches"))
613 tags_group = ([], _("Tags"))
619 tags_group = ([], _("Tags"))
614 _hg = cs.repository.alias == 'hg'
620 _hg = cs.repository.alias == 'hg'
615 for chs in changesets:
621 for chs in changesets:
616 #_branch = '(%s)' % chs.branch if _hg else ''
622 #_branch = '(%s)' % chs.branch if _hg else ''
617 _branch = chs.branch
623 _branch = chs.branch
618 n_desc = 'r%s:%s (%s)' % (chs.revision, chs.short_id, _branch)
624 n_desc = 'r%s:%s (%s)' % (chs.revision, chs.short_id, _branch)
619 changesets_group[0].append((chs.raw_id, n_desc,))
625 changesets_group[0].append((chs.raw_id, n_desc,))
620 hist_l.append(changesets_group)
626 hist_l.append(changesets_group)
621
627
622 for name, chs in c.rhodecode_repo.branches.items():
628 for name, chs in c.rhodecode_repo.branches.items():
623 branches_group[0].append((chs, name),)
629 branches_group[0].append((chs, name),)
624 hist_l.append(branches_group)
630 hist_l.append(branches_group)
625
631
626 for name, chs in c.rhodecode_repo.tags.items():
632 for name, chs in c.rhodecode_repo.tags.items():
627 tags_group[0].append((chs, name),)
633 tags_group[0].append((chs, name),)
628 hist_l.append(tags_group)
634 hist_l.append(tags_group)
629
635
630 return hist_l, changesets
636 return hist_l, changesets
631
637
632 @LoginRequired()
638 @LoginRequired()
633 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
639 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
634 'repository.admin')
640 'repository.admin')
635 @jsonify
641 @jsonify
636 def nodelist(self, repo_name, revision, f_path):
642 def nodelist(self, repo_name, revision, f_path):
637 if request.environ.get('HTTP_X_PARTIAL_XHR'):
643 if request.environ.get('HTTP_X_PARTIAL_XHR'):
638 cs = self.__get_cs_or_redirect(revision, repo_name)
644 cs = self.__get_cs_or_redirect(revision, repo_name)
639 _d, _f = ScmModel().get_nodes(repo_name, cs.raw_id, f_path,
645 _d, _f = ScmModel().get_nodes(repo_name, cs.raw_id, f_path,
640 flat=False)
646 flat=False)
641 return {'nodes': _d + _f}
647 return {'nodes': _d + _f}
@@ -1,92 +1,92 b''
1 <%inherit file="/base/base.html"/>
1 <%inherit file="/base/base.html"/>
2
2
3 <%def name="title()">
3 <%def name="title()">
4 ${_('%s Edit file') % c.repo_name} - ${c.rhodecode_name}
4 ${_('%s Edit file') % c.repo_name} - ${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',enctype="multipart/form-data")}
39 ${h.form(h.url.current(),method='post',id='eform',enctype="multipart/form-data")}
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 id="filename_container" class="field file">
43 <div id="filename_container" class="field file">
44 <div class="label">
44 <div class="label">
45 <label for="filename">${_('File Name')}:</label>
45 <label for="filename">${_('File Name')}:</label>
46 </div>
46 </div>
47 <div class="input">
47 <div class="input">
48 <input type="text" value="" size="30" name="filename" id="filename">
48 <input type="text" value="" size="30" name="filename" id="filename">
49 ${_('or')} <span class="ui-btn" id="upload_file_enable">${_('Upload file')}</span>
49 ${_('or')} <span class="ui-btn" id="upload_file_enable">${_('Upload file')}</span>
50 </div>
50 </div>
51 </div>
51 </div>
52 <div id="upload_file_container" class="field" style="display:none">
52 <div id="upload_file_container" class="field" style="display:none">
53 <div class="label">
53 <div class="label">
54 <label for="location">${_('Upload file')}</label>
54 <label for="upload_file_container">${_('Upload file')}</label>
55 </div>
55 </div>
56 <div class="file">
56 <div class="file">
57 <input type="file" size="30" name="upload_file" id="upload_file">
57 <input type="file" size="30" name="upload_file" id="upload_file">
58 ${_('or')} <span class="ui-btn" id="file_enable">${_('Create new file')}</span>
58 ${_('or')} <span class="ui-btn" id="file_enable">${_('Create new file')}</span>
59 </div>
59 </div>
60 </div>
60 </div>
61 <div class="field">
61 <div class="field">
62 <div class="label">
62 <div class="label">
63 <label for="location">${_('Location')}</label>
63 <label for="location">${_('Location')}</label>
64 </div>
64 </div>
65 <div class="input">
65 <div class="input">
66 <input type="text" value="${c.f_path}" size="30" name="location" id="location">
66 <input type="text" value="${c.f_path}" size="30" name="location" id="location">
67 ${_('use / to separate directories')}
67 ${_('use / to separate directories')}
68 </div>
68 </div>
69 </div>
69 </div>
70 </div>
70 </div>
71 </div>
71 </div>
72 <div id="body" class="codeblock">
72 <div id="body" class="codeblock">
73 <div id="editor_container">
73 <div id="editor_container">
74 <pre id="editor_pre"></pre>
74 <pre id="editor_pre"></pre>
75 <textarea id="editor" name="content" style="display:none"></textarea>
75 <textarea id="editor" name="content" style="display:none"></textarea>
76 </div>
76 </div>
77 <div style="padding: 10px;color:#666666">${_('commit message')}</div>
77 <div style="padding: 10px;color:#666666">${_('commit message')}</div>
78 <textarea id="commit" name="message" style="height: 100px;width: 99%;margin-left:4px" placeholder="${c.default_message}"></textarea>
78 <textarea id="commit" name="message" style="height: 100px;width: 99%;margin-left:4px" placeholder="${c.default_message}"></textarea>
79 </div>
79 </div>
80 <div style="text-align: l;padding-top: 5px">
80 <div style="text-align: l;padding-top: 5px">
81 ${h.submit('commit',_('Commit changes'),class_="ui-btn")}
81 ${h.submit('commit',_('Commit changes'),class_="ui-btn")}
82 ${h.reset('reset',_('Reset'),class_="ui-btn")}
82 ${h.reset('reset',_('Reset'),class_="ui-btn")}
83 </div>
83 </div>
84 ${h.end_form()}
84 ${h.end_form()}
85 <script type="text/javascript">
85 <script type="text/javascript">
86 var reset_url = "${h.url('files_home',repo_name=c.repo_name,revision=c.cs.raw_id,f_path=c.f_path)}";
86 var reset_url = "${h.url('files_home',repo_name=c.repo_name,revision=c.cs.raw_id,f_path=c.f_path)}";
87 initCodeMirror('editor',reset_url);
87 initCodeMirror('editor',reset_url);
88 </script>
88 </script>
89 </div>
89 </div>
90 </div>
90 </div>
91 </div>
91 </div>
92 </%def>
92 </%def>
General Comments 0
You need to be logged in to leave comments. Login now