comparison rhodecode/controllers/files.py @ 1305:166317d464f3 beta

Added server side file editing with commit
author Marcin Kuzminski <marcin@python-works.com>
date Tue, 03 May 2011 14:13:37 +0200
parents 64cb9612f9aa
children 6e1d24503383
comparison
equal deleted inserted replaced
1304:5a96551ee9c0 1305:166317d464f3
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 mimetypes 28 import mimetypes
29 import rhodecode.lib.helpers as h 29 import traceback
30 30
31 from pylons import request, response, session, tmpl_context as c, url 31 from pylons import request, response, session, tmpl_context as c, url
32 from pylons.i18n.translation import _ 32 from pylons.i18n.translation import _
33 from pylons.controllers.util import redirect 33 from pylons.controllers.util import redirect
34
35 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
36 from rhodecode.lib.base import BaseRepoController, render
37 from rhodecode.lib.utils import EmptyChangeset
38 from rhodecode.model.repo import RepoModel
39 34
40 from vcs.backends import ARCHIVE_SPECS 35 from vcs.backends import ARCHIVE_SPECS
41 from vcs.exceptions import RepositoryError, ChangesetDoesNotExistError, \ 36 from vcs.exceptions import RepositoryError, ChangesetDoesNotExistError, \
42 EmptyRepositoryError, ImproperArchiveTypeError, VCSError 37 EmptyRepositoryError, ImproperArchiveTypeError, VCSError
43 from vcs.nodes import FileNode, NodeKind 38 from vcs.nodes import FileNode, NodeKind
44 from vcs.utils import diffs as differ 39 from vcs.utils import diffs as differ
45 40
41 from rhodecode.lib import convert_line_endings, detect_mode
42 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
43 from rhodecode.lib.base import BaseRepoController, render
44 from rhodecode.lib.utils import EmptyChangeset
45 import rhodecode.lib.helpers as h
46 from rhodecode.model.repo import RepoModel
47
46 log = logging.getLogger(__name__) 48 log = logging.getLogger(__name__)
47 49
48 50
49 class FilesController(BaseRepoController): 51 class FilesController(BaseRepoController):
50 52
51 @LoginRequired() 53 @LoginRequired()
52 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
53 'repository.admin')
54 def __before__(self): 54 def __before__(self):
55 super(FilesController, self).__before__() 55 super(FilesController, self).__before__()
56 c.cut_off_limit = self.cut_off_limit 56 c.cut_off_limit = self.cut_off_limit
57 57
58 def __get_cs_or_redirect(self, rev, repo_name): 58 def __get_cs_or_redirect(self, rev, repo_name):
93 redirect(h.url('files_home', repo_name=repo_name, 93 redirect(h.url('files_home', repo_name=repo_name,
94 revision=cs.raw_id)) 94 revision=cs.raw_id))
95 95
96 return file_node 96 return file_node
97 97
98 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
99 'repository.admin')
98 def index(self, repo_name, revision, f_path): 100 def index(self, repo_name, revision, f_path):
99 #reditect to given revision from form if given 101 #reditect to given revision from form if given
100 post_revision = request.POST.get('at_rev', None) 102 post_revision = request.POST.get('at_rev', None)
101 if post_revision: 103 if post_revision:
102 cs = self.__get_cs_or_redirect(post_revision, repo_name) 104 cs = self.__get_cs_or_redirect(post_revision, repo_name)
142 redirect(h.url('files_home', repo_name=repo_name, 144 redirect(h.url('files_home', repo_name=repo_name,
143 revision=revision)) 145 revision=revision))
144 146
145 return render('files/files.html') 147 return render('files/files.html')
146 148
149 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
150 'repository.admin')
147 def rawfile(self, repo_name, revision, f_path): 151 def rawfile(self, repo_name, revision, f_path):
148 cs = self.__get_cs_or_redirect(revision, repo_name) 152 cs = self.__get_cs_or_redirect(revision, repo_name)
149 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path) 153 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
150 154
151 response.content_disposition = 'attachment; filename=%s' % \ 155 response.content_disposition = 'attachment; filename=%s' % \
152 f_path.split(os.sep)[-1].encode('utf8', 'replace') 156 f_path.split(os.sep)[-1].encode('utf8', 'replace')
153 157
154 response.content_type = file_node.mimetype 158 response.content_type = file_node.mimetype
155 return file_node.content 159 return file_node.content
156 160
161 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
162 'repository.admin')
157 def raw(self, repo_name, revision, f_path): 163 def raw(self, repo_name, revision, f_path):
158 cs = self.__get_cs_or_redirect(revision, repo_name) 164 cs = self.__get_cs_or_redirect(revision, repo_name)
159 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path) 165 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
160 166
161 raw_mimetype_mapping = { 167 raw_mimetype_mapping = {
196 202
197 response.content_disposition = dispo 203 response.content_disposition = dispo
198 response.content_type = mimetype 204 response.content_type = mimetype
199 return file_node.content 205 return file_node.content
200 206
207 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
208 'repository.admin')
201 def annotate(self, repo_name, revision, f_path): 209 def annotate(self, repo_name, revision, f_path):
202 c.cs = self.__get_cs_or_redirect(revision, repo_name) 210 c.cs = self.__get_cs_or_redirect(revision, repo_name)
203 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path) 211 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
204 212
205 c.file_history = self._get_node_history(c.cs, f_path) 213 c.file_history = self._get_node_history(c.cs, f_path)
206 c.f_path = f_path 214 c.f_path = f_path
207 return render('files/files_annotate.html') 215 return render('files/files_annotate.html')
208 216
217 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
218 def edit(self, repo_name, revision, f_path):
219 r_post = request.POST
220
221 if c.rhodecode_repo.alias == 'hg':
222 from vcs.backends.hg import MercurialInMemoryChangeset as IMC
223 elif c.rhodecode_repo.alias == 'git':
224 from vcs.backends.git import GitInMemoryChangeset as IMC
225
226 c.cs = self.__get_cs_or_redirect(revision, repo_name)
227 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
228
229 c.file_history = self._get_node_history(c.cs, f_path)
230 c.f_path = f_path
231
232 if r_post:
233
234 old_content = c.file.content
235 # modes: 0 - Unix, 1 - Mac, 2 - DOS
236 mode = detect_mode(old_content.splitlines(1)[0], 0)
237 content = convert_line_endings(r_post.get('content'), mode)
238 message = r_post.get('message') or (_('Edited %s via RhodeCode')
239 % (f_path))
240
241 if content == old_content:
242 h.flash(_('No changes'),
243 category='warning')
244 return redirect(url('changeset_home',
245 repo_name=c.repo_name, revision='tip'))
246 try:
247 new_node = FileNode(f_path, content)
248 m = IMC(c.rhodecode_repo)
249 m.change(new_node)
250 m.commit(message=message,
251 author=self.rhodecode_user.full_contact,
252 parents=[c.cs], branch=c.cs.branch)
253 h.flash(_('Successfully committed to %s' % f_path),
254 category='success')
255 except Exception, e:
256 log.error(traceback.format_exc())
257 h.flash(_('Error occurred during commit'), category='error')
258 return redirect(url('changeset_home',
259 repo_name=c.repo_name, revision='tip'))
260
261 return render('files/files_edit.html')
262
263 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
264 'repository.admin')
209 def archivefile(self, repo_name, fname): 265 def archivefile(self, repo_name, fname):
210 266
211 fileformat = None 267 fileformat = None
212 revision = None 268 revision = None
213 ext = None 269 ext = None
237 response.content_disposition = 'attachment; filename=%s-%s%s' \ 293 response.content_disposition = 'attachment; filename=%s-%s%s' \
238 % (repo_name, revision, ext) 294 % (repo_name, revision, ext)
239 295
240 return cs.get_chunked_archive(stream=None, kind=fileformat) 296 return cs.get_chunked_archive(stream=None, kind=fileformat)
241 297
298 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
299 'repository.admin')
242 def diff(self, repo_name, f_path): 300 def diff(self, repo_name, f_path):
243 diff1 = request.GET.get('diff1') 301 diff1 = request.GET.get('diff1')
244 diff2 = request.GET.get('diff2') 302 diff2 = request.GET.get('diff2')
245 c.action = request.GET.get('diff') 303 c.action = request.GET.get('diff')
246 c.no_changes = diff1 == diff2 304 c.no_changes = diff1 == diff2
280 format='gitdiff') 338 format='gitdiff')
281 response.content_type = 'text/plain' 339 response.content_type = 'text/plain'
282 return diff.raw_diff() 340 return diff.raw_diff()
283 341
284 elif c.action == 'diff': 342 elif c.action == 'diff':
285
286 if node1.is_binary or node2.is_binary: 343 if node1.is_binary or node2.is_binary:
287 c.cur_diff = _('Binary file') 344 c.cur_diff = _('Binary file')
288 elif node1.size > self.cut_off_limit or \ 345 elif node1.size > self.cut_off_limit or \
289 node2.size > self.cut_off_limit: 346 node2.size > self.cut_off_limit:
290 c.cur_diff = '' 347 c.cur_diff = ''