##// END OF EJS Templates
code: small cleanups of code
marcink -
r2746:9e435730 default
parent child Browse files
Show More
@@ -1,1292 +1,1289 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2018 RhodeCode GmbH
3 # Copyright (C) 2011-2018 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import itertools
21 import itertools
22 import logging
22 import logging
23 import os
23 import os
24 import shutil
24 import shutil
25 import tempfile
25 import tempfile
26 import collections
26 import collections
27
27
28 from pyramid.httpexceptions import HTTPNotFound, HTTPBadRequest, HTTPFound
28 from pyramid.httpexceptions import HTTPNotFound, HTTPBadRequest, HTTPFound
29 from pyramid.view import view_config
29 from pyramid.view import view_config
30 from pyramid.renderers import render
30 from pyramid.renderers import render
31 from pyramid.response import Response
31 from pyramid.response import Response
32
32
33 from rhodecode.apps._base import RepoAppView
33 from rhodecode.apps._base import RepoAppView
34
34
35 from rhodecode.controllers.utils import parse_path_ref
35 from rhodecode.controllers.utils import parse_path_ref
36 from rhodecode.lib import diffs, helpers as h, caches
36 from rhodecode.lib import diffs, helpers as h, caches
37 from rhodecode.lib import audit_logger
37 from rhodecode.lib import audit_logger
38 from rhodecode.lib.exceptions import NonRelativePathError
38 from rhodecode.lib.exceptions import NonRelativePathError
39 from rhodecode.lib.codeblocks import (
39 from rhodecode.lib.codeblocks import (
40 filenode_as_lines_tokens, filenode_as_annotated_lines_tokens)
40 filenode_as_lines_tokens, filenode_as_annotated_lines_tokens)
41 from rhodecode.lib.utils2 import (
41 from rhodecode.lib.utils2 import (
42 convert_line_endings, detect_mode, safe_str, str2bool)
42 convert_line_endings, detect_mode, safe_str, str2bool)
43 from rhodecode.lib.auth import (
43 from rhodecode.lib.auth import (
44 LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired)
44 LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired)
45 from rhodecode.lib.vcs import path as vcspath
45 from rhodecode.lib.vcs import path as vcspath
46 from rhodecode.lib.vcs.backends.base import EmptyCommit
46 from rhodecode.lib.vcs.backends.base import EmptyCommit
47 from rhodecode.lib.vcs.conf import settings
47 from rhodecode.lib.vcs.conf import settings
48 from rhodecode.lib.vcs.nodes import FileNode
48 from rhodecode.lib.vcs.nodes import FileNode
49 from rhodecode.lib.vcs.exceptions import (
49 from rhodecode.lib.vcs.exceptions import (
50 RepositoryError, CommitDoesNotExistError, EmptyRepositoryError,
50 RepositoryError, CommitDoesNotExistError, EmptyRepositoryError,
51 ImproperArchiveTypeError, VCSError, NodeAlreadyExistsError,
51 ImproperArchiveTypeError, VCSError, NodeAlreadyExistsError,
52 NodeDoesNotExistError, CommitError, NodeError)
52 NodeDoesNotExistError, CommitError, NodeError)
53
53
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 log = logging.getLogger(__name__)
57 log = logging.getLogger(__name__)
58
58
59
59
60 class RepoFilesView(RepoAppView):
60 class RepoFilesView(RepoAppView):
61
61
62 @staticmethod
62 @staticmethod
63 def adjust_file_path_for_svn(f_path, repo):
63 def adjust_file_path_for_svn(f_path, repo):
64 """
64 """
65 Computes the relative path of `f_path`.
65 Computes the relative path of `f_path`.
66
66
67 This is mainly based on prefix matching of the recognized tags and
67 This is mainly based on prefix matching of the recognized tags and
68 branches in the underlying repository.
68 branches in the underlying repository.
69 """
69 """
70 tags_and_branches = itertools.chain(
70 tags_and_branches = itertools.chain(
71 repo.branches.iterkeys(),
71 repo.branches.iterkeys(),
72 repo.tags.iterkeys())
72 repo.tags.iterkeys())
73 tags_and_branches = sorted(tags_and_branches, key=len, reverse=True)
73 tags_and_branches = sorted(tags_and_branches, key=len, reverse=True)
74
74
75 for name in tags_and_branches:
75 for name in tags_and_branches:
76 if f_path.startswith('{}/'.format(name)):
76 if f_path.startswith('{}/'.format(name)):
77 f_path = vcspath.relpath(f_path, name)
77 f_path = vcspath.relpath(f_path, name)
78 break
78 break
79 return f_path
79 return f_path
80
80
81 def load_default_context(self):
81 def load_default_context(self):
82 c = self._get_local_tmpl_context(include_app_defaults=True)
82 c = self._get_local_tmpl_context(include_app_defaults=True)
83
84 c.rhodecode_repo = self.rhodecode_vcs_repo
83 c.rhodecode_repo = self.rhodecode_vcs_repo
85
86
87 return c
84 return c
88
85
89 def _ensure_not_locked(self):
86 def _ensure_not_locked(self):
90 _ = self.request.translate
87 _ = self.request.translate
91
88
92 repo = self.db_repo
89 repo = self.db_repo
93 if repo.enable_locking and repo.locked[0]:
90 if repo.enable_locking and repo.locked[0]:
94 h.flash(_('This repository has been locked by %s on %s')
91 h.flash(_('This repository has been locked by %s on %s')
95 % (h.person_by_id(repo.locked[0]),
92 % (h.person_by_id(repo.locked[0]),
96 h.format_date(h.time_to_datetime(repo.locked[1]))),
93 h.format_date(h.time_to_datetime(repo.locked[1]))),
97 'warning')
94 'warning')
98 files_url = h.route_path(
95 files_url = h.route_path(
99 'repo_files:default_path',
96 'repo_files:default_path',
100 repo_name=self.db_repo_name, commit_id='tip')
97 repo_name=self.db_repo_name, commit_id='tip')
101 raise HTTPFound(files_url)
98 raise HTTPFound(files_url)
102
99
103 def _get_commit_and_path(self):
100 def _get_commit_and_path(self):
104 default_commit_id = self.db_repo.landing_rev[1]
101 default_commit_id = self.db_repo.landing_rev[1]
105 default_f_path = '/'
102 default_f_path = '/'
106
103
107 commit_id = self.request.matchdict.get(
104 commit_id = self.request.matchdict.get(
108 'commit_id', default_commit_id)
105 'commit_id', default_commit_id)
109 f_path = self._get_f_path(self.request.matchdict, default_f_path)
106 f_path = self._get_f_path(self.request.matchdict, default_f_path)
110 return commit_id, f_path
107 return commit_id, f_path
111
108
112 def _get_default_encoding(self, c):
109 def _get_default_encoding(self, c):
113 enc_list = getattr(c, 'default_encodings', [])
110 enc_list = getattr(c, 'default_encodings', [])
114 return enc_list[0] if enc_list else 'UTF-8'
111 return enc_list[0] if enc_list else 'UTF-8'
115
112
116 def _get_commit_or_redirect(self, commit_id, redirect_after=True):
113 def _get_commit_or_redirect(self, commit_id, redirect_after=True):
117 """
114 """
118 This is a safe way to get commit. If an error occurs it redirects to
115 This is a safe way to get commit. If an error occurs it redirects to
119 tip with proper message
116 tip with proper message
120
117
121 :param commit_id: id of commit to fetch
118 :param commit_id: id of commit to fetch
122 :param redirect_after: toggle redirection
119 :param redirect_after: toggle redirection
123 """
120 """
124 _ = self.request.translate
121 _ = self.request.translate
125
122
126 try:
123 try:
127 return self.rhodecode_vcs_repo.get_commit(commit_id)
124 return self.rhodecode_vcs_repo.get_commit(commit_id)
128 except EmptyRepositoryError:
125 except EmptyRepositoryError:
129 if not redirect_after:
126 if not redirect_after:
130 return None
127 return None
131
128
132 _url = h.route_path(
129 _url = h.route_path(
133 'repo_files_add_file',
130 'repo_files_add_file',
134 repo_name=self.db_repo_name, commit_id=0, f_path='',
131 repo_name=self.db_repo_name, commit_id=0, f_path='',
135 _anchor='edit')
132 _anchor='edit')
136
133
137 if h.HasRepoPermissionAny(
134 if h.HasRepoPermissionAny(
138 'repository.write', 'repository.admin')(self.db_repo_name):
135 'repository.write', 'repository.admin')(self.db_repo_name):
139 add_new = h.link_to(
136 add_new = h.link_to(
140 _('Click here to add a new file.'), _url, class_="alert-link")
137 _('Click here to add a new file.'), _url, class_="alert-link")
141 else:
138 else:
142 add_new = ""
139 add_new = ""
143
140
144 h.flash(h.literal(
141 h.flash(h.literal(
145 _('There are no files yet. %s') % add_new), category='warning')
142 _('There are no files yet. %s') % add_new), category='warning')
146 raise HTTPFound(
143 raise HTTPFound(
147 h.route_path('repo_summary', repo_name=self.db_repo_name))
144 h.route_path('repo_summary', repo_name=self.db_repo_name))
148
145
149 except (CommitDoesNotExistError, LookupError):
146 except (CommitDoesNotExistError, LookupError):
150 msg = _('No such commit exists for this repository')
147 msg = _('No such commit exists for this repository')
151 h.flash(msg, category='error')
148 h.flash(msg, category='error')
152 raise HTTPNotFound()
149 raise HTTPNotFound()
153 except RepositoryError as e:
150 except RepositoryError as e:
154 h.flash(safe_str(h.escape(e)), category='error')
151 h.flash(safe_str(h.escape(e)), category='error')
155 raise HTTPNotFound()
152 raise HTTPNotFound()
156
153
157 def _get_filenode_or_redirect(self, commit_obj, path):
154 def _get_filenode_or_redirect(self, commit_obj, path):
158 """
155 """
159 Returns file_node, if error occurs or given path is directory,
156 Returns file_node, if error occurs or given path is directory,
160 it'll redirect to top level path
157 it'll redirect to top level path
161 """
158 """
162 _ = self.request.translate
159 _ = self.request.translate
163
160
164 try:
161 try:
165 file_node = commit_obj.get_node(path)
162 file_node = commit_obj.get_node(path)
166 if file_node.is_dir():
163 if file_node.is_dir():
167 raise RepositoryError('The given path is a directory')
164 raise RepositoryError('The given path is a directory')
168 except CommitDoesNotExistError:
165 except CommitDoesNotExistError:
169 log.exception('No such commit exists for this repository')
166 log.exception('No such commit exists for this repository')
170 h.flash(_('No such commit exists for this repository'), category='error')
167 h.flash(_('No such commit exists for this repository'), category='error')
171 raise HTTPNotFound()
168 raise HTTPNotFound()
172 except RepositoryError as e:
169 except RepositoryError as e:
173 log.warning('Repository error while fetching '
170 log.warning('Repository error while fetching '
174 'filenode `%s`. Err:%s', path, e)
171 'filenode `%s`. Err:%s', path, e)
175 h.flash(safe_str(h.escape(e)), category='error')
172 h.flash(safe_str(h.escape(e)), category='error')
176 raise HTTPNotFound()
173 raise HTTPNotFound()
177
174
178 return file_node
175 return file_node
179
176
180 def _is_valid_head(self, commit_id, repo):
177 def _is_valid_head(self, commit_id, repo):
181 # check if commit is a branch identifier- basically we cannot
178 # check if commit is a branch identifier- basically we cannot
182 # create multiple heads via file editing
179 # create multiple heads via file editing
183 valid_heads = repo.branches.keys() + repo.branches.values()
180 valid_heads = repo.branches.keys() + repo.branches.values()
184
181
185 if h.is_svn(repo) and not repo.is_empty():
182 if h.is_svn(repo) and not repo.is_empty():
186 # Note: Subversion only has one head, we add it here in case there
183 # Note: Subversion only has one head, we add it here in case there
187 # is no branch matched.
184 # is no branch matched.
188 valid_heads.append(repo.get_commit(commit_idx=-1).raw_id)
185 valid_heads.append(repo.get_commit(commit_idx=-1).raw_id)
189
186
190 # check if commit is a branch name or branch hash
187 # check if commit is a branch name or branch hash
191 return commit_id in valid_heads
188 return commit_id in valid_heads
192
189
193 def _get_tree_cache_manager(self, namespace_type):
190 def _get_tree_cache_manager(self, namespace_type):
194 _namespace = caches.get_repo_namespace_key(
191 _namespace = caches.get_repo_namespace_key(
195 namespace_type, self.db_repo_name)
192 namespace_type, self.db_repo_name)
196 return caches.get_cache_manager('repo_cache_long', _namespace)
193 return caches.get_cache_manager('repo_cache_long', _namespace)
197
194
198 def _get_tree_at_commit(
195 def _get_tree_at_commit(
199 self, c, commit_id, f_path, full_load=False, force=False):
196 self, c, commit_id, f_path, full_load=False, force=False):
200 def _cached_tree():
197 def _cached_tree():
201 log.debug('Generating cached file tree for %s, %s, %s',
198 log.debug('Generating cached file tree for %s, %s, %s',
202 self.db_repo_name, commit_id, f_path)
199 self.db_repo_name, commit_id, f_path)
203
200
204 c.full_load = full_load
201 c.full_load = full_load
205 return render(
202 return render(
206 'rhodecode:templates/files/files_browser_tree.mako',
203 'rhodecode:templates/files/files_browser_tree.mako',
207 self._get_template_context(c), self.request)
204 self._get_template_context(c), self.request)
208
205
209 cache_manager = self._get_tree_cache_manager(caches.FILE_TREE)
206 cache_manager = self._get_tree_cache_manager(caches.FILE_TREE)
210
207
211 cache_key = caches.compute_key_from_params(
208 cache_key = caches.compute_key_from_params(
212 self.db_repo_name, commit_id, f_path)
209 self.db_repo_name, commit_id, f_path)
213
210
214 if force:
211 if force:
215 # we want to force recompute of caches
212 # we want to force recompute of caches
216 cache_manager.remove_value(cache_key)
213 cache_manager.remove_value(cache_key)
217
214
218 return cache_manager.get(cache_key, createfunc=_cached_tree)
215 return cache_manager.get(cache_key, createfunc=_cached_tree)
219
216
220 def _get_archive_spec(self, fname):
217 def _get_archive_spec(self, fname):
221 log.debug('Detecting archive spec for: `%s`', fname)
218 log.debug('Detecting archive spec for: `%s`', fname)
222
219
223 fileformat = None
220 fileformat = None
224 ext = None
221 ext = None
225 content_type = None
222 content_type = None
226 for a_type, ext_data in settings.ARCHIVE_SPECS.items():
223 for a_type, ext_data in settings.ARCHIVE_SPECS.items():
227 content_type, extension = ext_data
224 content_type, extension = ext_data
228
225
229 if fname.endswith(extension):
226 if fname.endswith(extension):
230 fileformat = a_type
227 fileformat = a_type
231 log.debug('archive is of type: %s', fileformat)
228 log.debug('archive is of type: %s', fileformat)
232 ext = extension
229 ext = extension
233 break
230 break
234
231
235 if not fileformat:
232 if not fileformat:
236 raise ValueError()
233 raise ValueError()
237
234
238 # left over part of whole fname is the commit
235 # left over part of whole fname is the commit
239 commit_id = fname[:-len(ext)]
236 commit_id = fname[:-len(ext)]
240
237
241 return commit_id, ext, fileformat, content_type
238 return commit_id, ext, fileformat, content_type
242
239
243 @LoginRequired()
240 @LoginRequired()
244 @HasRepoPermissionAnyDecorator(
241 @HasRepoPermissionAnyDecorator(
245 'repository.read', 'repository.write', 'repository.admin')
242 'repository.read', 'repository.write', 'repository.admin')
246 @view_config(
243 @view_config(
247 route_name='repo_archivefile', request_method='GET',
244 route_name='repo_archivefile', request_method='GET',
248 renderer=None)
245 renderer=None)
249 def repo_archivefile(self):
246 def repo_archivefile(self):
250 # archive cache config
247 # archive cache config
251 from rhodecode import CONFIG
248 from rhodecode import CONFIG
252 _ = self.request.translate
249 _ = self.request.translate
253 self.load_default_context()
250 self.load_default_context()
254
251
255 fname = self.request.matchdict['fname']
252 fname = self.request.matchdict['fname']
256 subrepos = self.request.GET.get('subrepos') == 'true'
253 subrepos = self.request.GET.get('subrepos') == 'true'
257
254
258 if not self.db_repo.enable_downloads:
255 if not self.db_repo.enable_downloads:
259 return Response(_('Downloads disabled'))
256 return Response(_('Downloads disabled'))
260
257
261 try:
258 try:
262 commit_id, ext, fileformat, content_type = \
259 commit_id, ext, fileformat, content_type = \
263 self._get_archive_spec(fname)
260 self._get_archive_spec(fname)
264 except ValueError:
261 except ValueError:
265 return Response(_('Unknown archive type for: `{}`').format(
262 return Response(_('Unknown archive type for: `{}`').format(
266 h.escape(fname)))
263 h.escape(fname)))
267
264
268 try:
265 try:
269 commit = self.rhodecode_vcs_repo.get_commit(commit_id)
266 commit = self.rhodecode_vcs_repo.get_commit(commit_id)
270 except CommitDoesNotExistError:
267 except CommitDoesNotExistError:
271 return Response(_('Unknown commit_id {}').format(
268 return Response(_('Unknown commit_id {}').format(
272 h.escape(commit_id)))
269 h.escape(commit_id)))
273 except EmptyRepositoryError:
270 except EmptyRepositoryError:
274 return Response(_('Empty repository'))
271 return Response(_('Empty repository'))
275
272
276 archive_name = '%s-%s%s%s' % (
273 archive_name = '%s-%s%s%s' % (
277 safe_str(self.db_repo_name.replace('/', '_')),
274 safe_str(self.db_repo_name.replace('/', '_')),
278 '-sub' if subrepos else '',
275 '-sub' if subrepos else '',
279 safe_str(commit.short_id), ext)
276 safe_str(commit.short_id), ext)
280
277
281 use_cached_archive = False
278 use_cached_archive = False
282 archive_cache_enabled = CONFIG.get(
279 archive_cache_enabled = CONFIG.get(
283 'archive_cache_dir') and not self.request.GET.get('no_cache')
280 'archive_cache_dir') and not self.request.GET.get('no_cache')
284
281
285 if archive_cache_enabled:
282 if archive_cache_enabled:
286 # check if we it's ok to write
283 # check if we it's ok to write
287 if not os.path.isdir(CONFIG['archive_cache_dir']):
284 if not os.path.isdir(CONFIG['archive_cache_dir']):
288 os.makedirs(CONFIG['archive_cache_dir'])
285 os.makedirs(CONFIG['archive_cache_dir'])
289 cached_archive_path = os.path.join(
286 cached_archive_path = os.path.join(
290 CONFIG['archive_cache_dir'], archive_name)
287 CONFIG['archive_cache_dir'], archive_name)
291 if os.path.isfile(cached_archive_path):
288 if os.path.isfile(cached_archive_path):
292 log.debug('Found cached archive in %s', cached_archive_path)
289 log.debug('Found cached archive in %s', cached_archive_path)
293 fd, archive = None, cached_archive_path
290 fd, archive = None, cached_archive_path
294 use_cached_archive = True
291 use_cached_archive = True
295 else:
292 else:
296 log.debug('Archive %s is not yet cached', archive_name)
293 log.debug('Archive %s is not yet cached', archive_name)
297
294
298 if not use_cached_archive:
295 if not use_cached_archive:
299 # generate new archive
296 # generate new archive
300 fd, archive = tempfile.mkstemp()
297 fd, archive = tempfile.mkstemp()
301 log.debug('Creating new temp archive in %s', archive)
298 log.debug('Creating new temp archive in %s', archive)
302 try:
299 try:
303 commit.archive_repo(archive, kind=fileformat, subrepos=subrepos)
300 commit.archive_repo(archive, kind=fileformat, subrepos=subrepos)
304 except ImproperArchiveTypeError:
301 except ImproperArchiveTypeError:
305 return _('Unknown archive type')
302 return _('Unknown archive type')
306 if archive_cache_enabled:
303 if archive_cache_enabled:
307 # if we generated the archive and we have cache enabled
304 # if we generated the archive and we have cache enabled
308 # let's use this for future
305 # let's use this for future
309 log.debug('Storing new archive in %s', cached_archive_path)
306 log.debug('Storing new archive in %s', cached_archive_path)
310 shutil.move(archive, cached_archive_path)
307 shutil.move(archive, cached_archive_path)
311 archive = cached_archive_path
308 archive = cached_archive_path
312
309
313 # store download action
310 # store download action
314 audit_logger.store_web(
311 audit_logger.store_web(
315 'repo.archive.download', action_data={
312 'repo.archive.download', action_data={
316 'user_agent': self.request.user_agent,
313 'user_agent': self.request.user_agent,
317 'archive_name': archive_name,
314 'archive_name': archive_name,
318 'archive_spec': fname,
315 'archive_spec': fname,
319 'archive_cached': use_cached_archive},
316 'archive_cached': use_cached_archive},
320 user=self._rhodecode_user,
317 user=self._rhodecode_user,
321 repo=self.db_repo,
318 repo=self.db_repo,
322 commit=True
319 commit=True
323 )
320 )
324
321
325 def get_chunked_archive(archive):
322 def get_chunked_archive(archive):
326 with open(archive, 'rb') as stream:
323 with open(archive, 'rb') as stream:
327 while True:
324 while True:
328 data = stream.read(16 * 1024)
325 data = stream.read(16 * 1024)
329 if not data:
326 if not data:
330 if fd: # fd means we used temporary file
327 if fd: # fd means we used temporary file
331 os.close(fd)
328 os.close(fd)
332 if not archive_cache_enabled:
329 if not archive_cache_enabled:
333 log.debug('Destroying temp archive %s', archive)
330 log.debug('Destroying temp archive %s', archive)
334 os.remove(archive)
331 os.remove(archive)
335 break
332 break
336 yield data
333 yield data
337
334
338 response = Response(app_iter=get_chunked_archive(archive))
335 response = Response(app_iter=get_chunked_archive(archive))
339 response.content_disposition = str(
336 response.content_disposition = str(
340 'attachment; filename=%s' % archive_name)
337 'attachment; filename=%s' % archive_name)
341 response.content_type = str(content_type)
338 response.content_type = str(content_type)
342
339
343 return response
340 return response
344
341
345 def _get_file_node(self, commit_id, f_path):
342 def _get_file_node(self, commit_id, f_path):
346 if commit_id not in ['', None, 'None', '0' * 12, '0' * 40]:
343 if commit_id not in ['', None, 'None', '0' * 12, '0' * 40]:
347 commit = self.rhodecode_vcs_repo.get_commit(commit_id=commit_id)
344 commit = self.rhodecode_vcs_repo.get_commit(commit_id=commit_id)
348 try:
345 try:
349 node = commit.get_node(f_path)
346 node = commit.get_node(f_path)
350 if node.is_dir():
347 if node.is_dir():
351 raise NodeError('%s path is a %s not a file'
348 raise NodeError('%s path is a %s not a file'
352 % (node, type(node)))
349 % (node, type(node)))
353 except NodeDoesNotExistError:
350 except NodeDoesNotExistError:
354 commit = EmptyCommit(
351 commit = EmptyCommit(
355 commit_id=commit_id,
352 commit_id=commit_id,
356 idx=commit.idx,
353 idx=commit.idx,
357 repo=commit.repository,
354 repo=commit.repository,
358 alias=commit.repository.alias,
355 alias=commit.repository.alias,
359 message=commit.message,
356 message=commit.message,
360 author=commit.author,
357 author=commit.author,
361 date=commit.date)
358 date=commit.date)
362 node = FileNode(f_path, '', commit=commit)
359 node = FileNode(f_path, '', commit=commit)
363 else:
360 else:
364 commit = EmptyCommit(
361 commit = EmptyCommit(
365 repo=self.rhodecode_vcs_repo,
362 repo=self.rhodecode_vcs_repo,
366 alias=self.rhodecode_vcs_repo.alias)
363 alias=self.rhodecode_vcs_repo.alias)
367 node = FileNode(f_path, '', commit=commit)
364 node = FileNode(f_path, '', commit=commit)
368 return node
365 return node
369
366
370 @LoginRequired()
367 @LoginRequired()
371 @HasRepoPermissionAnyDecorator(
368 @HasRepoPermissionAnyDecorator(
372 'repository.read', 'repository.write', 'repository.admin')
369 'repository.read', 'repository.write', 'repository.admin')
373 @view_config(
370 @view_config(
374 route_name='repo_files_diff', request_method='GET',
371 route_name='repo_files_diff', request_method='GET',
375 renderer=None)
372 renderer=None)
376 def repo_files_diff(self):
373 def repo_files_diff(self):
377 c = self.load_default_context()
374 c = self.load_default_context()
378 f_path = self._get_f_path(self.request.matchdict)
375 f_path = self._get_f_path(self.request.matchdict)
379 diff1 = self.request.GET.get('diff1', '')
376 diff1 = self.request.GET.get('diff1', '')
380 diff2 = self.request.GET.get('diff2', '')
377 diff2 = self.request.GET.get('diff2', '')
381
378
382 path1, diff1 = parse_path_ref(diff1, default_path=f_path)
379 path1, diff1 = parse_path_ref(diff1, default_path=f_path)
383
380
384 ignore_whitespace = str2bool(self.request.GET.get('ignorews'))
381 ignore_whitespace = str2bool(self.request.GET.get('ignorews'))
385 line_context = self.request.GET.get('context', 3)
382 line_context = self.request.GET.get('context', 3)
386
383
387 if not any((diff1, diff2)):
384 if not any((diff1, diff2)):
388 h.flash(
385 h.flash(
389 'Need query parameter "diff1" or "diff2" to generate a diff.',
386 'Need query parameter "diff1" or "diff2" to generate a diff.',
390 category='error')
387 category='error')
391 raise HTTPBadRequest()
388 raise HTTPBadRequest()
392
389
393 c.action = self.request.GET.get('diff')
390 c.action = self.request.GET.get('diff')
394 if c.action not in ['download', 'raw']:
391 if c.action not in ['download', 'raw']:
395 compare_url = h.route_path(
392 compare_url = h.route_path(
396 'repo_compare',
393 'repo_compare',
397 repo_name=self.db_repo_name,
394 repo_name=self.db_repo_name,
398 source_ref_type='rev',
395 source_ref_type='rev',
399 source_ref=diff1,
396 source_ref=diff1,
400 target_repo=self.db_repo_name,
397 target_repo=self.db_repo_name,
401 target_ref_type='rev',
398 target_ref_type='rev',
402 target_ref=diff2,
399 target_ref=diff2,
403 _query=dict(f_path=f_path))
400 _query=dict(f_path=f_path))
404 # redirect to new view if we render diff
401 # redirect to new view if we render diff
405 raise HTTPFound(compare_url)
402 raise HTTPFound(compare_url)
406
403
407 try:
404 try:
408 node1 = self._get_file_node(diff1, path1)
405 node1 = self._get_file_node(diff1, path1)
409 node2 = self._get_file_node(diff2, f_path)
406 node2 = self._get_file_node(diff2, f_path)
410 except (RepositoryError, NodeError):
407 except (RepositoryError, NodeError):
411 log.exception("Exception while trying to get node from repository")
408 log.exception("Exception while trying to get node from repository")
412 raise HTTPFound(
409 raise HTTPFound(
413 h.route_path('repo_files', repo_name=self.db_repo_name,
410 h.route_path('repo_files', repo_name=self.db_repo_name,
414 commit_id='tip', f_path=f_path))
411 commit_id='tip', f_path=f_path))
415
412
416 if all(isinstance(node.commit, EmptyCommit)
413 if all(isinstance(node.commit, EmptyCommit)
417 for node in (node1, node2)):
414 for node in (node1, node2)):
418 raise HTTPNotFound()
415 raise HTTPNotFound()
419
416
420 c.commit_1 = node1.commit
417 c.commit_1 = node1.commit
421 c.commit_2 = node2.commit
418 c.commit_2 = node2.commit
422
419
423 if c.action == 'download':
420 if c.action == 'download':
424 _diff = diffs.get_gitdiff(node1, node2,
421 _diff = diffs.get_gitdiff(node1, node2,
425 ignore_whitespace=ignore_whitespace,
422 ignore_whitespace=ignore_whitespace,
426 context=line_context)
423 context=line_context)
427 diff = diffs.DiffProcessor(_diff, format='gitdiff')
424 diff = diffs.DiffProcessor(_diff, format='gitdiff')
428
425
429 response = Response(self.path_filter.get_raw_patch(diff))
426 response = Response(self.path_filter.get_raw_patch(diff))
430 response.content_type = 'text/plain'
427 response.content_type = 'text/plain'
431 response.content_disposition = (
428 response.content_disposition = (
432 'attachment; filename=%s_%s_vs_%s.diff' % (f_path, diff1, diff2)
429 'attachment; filename=%s_%s_vs_%s.diff' % (f_path, diff1, diff2)
433 )
430 )
434 charset = self._get_default_encoding(c)
431 charset = self._get_default_encoding(c)
435 if charset:
432 if charset:
436 response.charset = charset
433 response.charset = charset
437 return response
434 return response
438
435
439 elif c.action == 'raw':
436 elif c.action == 'raw':
440 _diff = diffs.get_gitdiff(node1, node2,
437 _diff = diffs.get_gitdiff(node1, node2,
441 ignore_whitespace=ignore_whitespace,
438 ignore_whitespace=ignore_whitespace,
442 context=line_context)
439 context=line_context)
443 diff = diffs.DiffProcessor(_diff, format='gitdiff')
440 diff = diffs.DiffProcessor(_diff, format='gitdiff')
444
441
445 response = Response(self.path_filter.get_raw_patch(diff))
442 response = Response(self.path_filter.get_raw_patch(diff))
446 response.content_type = 'text/plain'
443 response.content_type = 'text/plain'
447 charset = self._get_default_encoding(c)
444 charset = self._get_default_encoding(c)
448 if charset:
445 if charset:
449 response.charset = charset
446 response.charset = charset
450 return response
447 return response
451
448
452 # in case we ever end up here
449 # in case we ever end up here
453 raise HTTPNotFound()
450 raise HTTPNotFound()
454
451
455 @LoginRequired()
452 @LoginRequired()
456 @HasRepoPermissionAnyDecorator(
453 @HasRepoPermissionAnyDecorator(
457 'repository.read', 'repository.write', 'repository.admin')
454 'repository.read', 'repository.write', 'repository.admin')
458 @view_config(
455 @view_config(
459 route_name='repo_files_diff_2way_redirect', request_method='GET',
456 route_name='repo_files_diff_2way_redirect', request_method='GET',
460 renderer=None)
457 renderer=None)
461 def repo_files_diff_2way_redirect(self):
458 def repo_files_diff_2way_redirect(self):
462 """
459 """
463 Kept only to make OLD links work
460 Kept only to make OLD links work
464 """
461 """
465 f_path = self._get_f_path_unchecked(self.request.matchdict)
462 f_path = self._get_f_path_unchecked(self.request.matchdict)
466 diff1 = self.request.GET.get('diff1', '')
463 diff1 = self.request.GET.get('diff1', '')
467 diff2 = self.request.GET.get('diff2', '')
464 diff2 = self.request.GET.get('diff2', '')
468
465
469 if not any((diff1, diff2)):
466 if not any((diff1, diff2)):
470 h.flash(
467 h.flash(
471 'Need query parameter "diff1" or "diff2" to generate a diff.',
468 'Need query parameter "diff1" or "diff2" to generate a diff.',
472 category='error')
469 category='error')
473 raise HTTPBadRequest()
470 raise HTTPBadRequest()
474
471
475 compare_url = h.route_path(
472 compare_url = h.route_path(
476 'repo_compare',
473 'repo_compare',
477 repo_name=self.db_repo_name,
474 repo_name=self.db_repo_name,
478 source_ref_type='rev',
475 source_ref_type='rev',
479 source_ref=diff1,
476 source_ref=diff1,
480 target_ref_type='rev',
477 target_ref_type='rev',
481 target_ref=diff2,
478 target_ref=diff2,
482 _query=dict(f_path=f_path, diffmode='sideside',
479 _query=dict(f_path=f_path, diffmode='sideside',
483 target_repo=self.db_repo_name,))
480 target_repo=self.db_repo_name,))
484 raise HTTPFound(compare_url)
481 raise HTTPFound(compare_url)
485
482
486 @LoginRequired()
483 @LoginRequired()
487 @HasRepoPermissionAnyDecorator(
484 @HasRepoPermissionAnyDecorator(
488 'repository.read', 'repository.write', 'repository.admin')
485 'repository.read', 'repository.write', 'repository.admin')
489 @view_config(
486 @view_config(
490 route_name='repo_files', request_method='GET',
487 route_name='repo_files', request_method='GET',
491 renderer=None)
488 renderer=None)
492 @view_config(
489 @view_config(
493 route_name='repo_files:default_path', request_method='GET',
490 route_name='repo_files:default_path', request_method='GET',
494 renderer=None)
491 renderer=None)
495 @view_config(
492 @view_config(
496 route_name='repo_files:default_commit', request_method='GET',
493 route_name='repo_files:default_commit', request_method='GET',
497 renderer=None)
494 renderer=None)
498 @view_config(
495 @view_config(
499 route_name='repo_files:rendered', request_method='GET',
496 route_name='repo_files:rendered', request_method='GET',
500 renderer=None)
497 renderer=None)
501 @view_config(
498 @view_config(
502 route_name='repo_files:annotated', request_method='GET',
499 route_name='repo_files:annotated', request_method='GET',
503 renderer=None)
500 renderer=None)
504 def repo_files(self):
501 def repo_files(self):
505 c = self.load_default_context()
502 c = self.load_default_context()
506
503
507 view_name = getattr(self.request.matched_route, 'name', None)
504 view_name = getattr(self.request.matched_route, 'name', None)
508
505
509 c.annotate = view_name == 'repo_files:annotated'
506 c.annotate = view_name == 'repo_files:annotated'
510 # default is false, but .rst/.md files later are auto rendered, we can
507 # default is false, but .rst/.md files later are auto rendered, we can
511 # overwrite auto rendering by setting this GET flag
508 # overwrite auto rendering by setting this GET flag
512 c.renderer = view_name == 'repo_files:rendered' or \
509 c.renderer = view_name == 'repo_files:rendered' or \
513 not self.request.GET.get('no-render', False)
510 not self.request.GET.get('no-render', False)
514
511
515 # redirect to given commit_id from form if given
512 # redirect to given commit_id from form if given
516 get_commit_id = self.request.GET.get('at_rev', None)
513 get_commit_id = self.request.GET.get('at_rev', None)
517 if get_commit_id:
514 if get_commit_id:
518 self._get_commit_or_redirect(get_commit_id)
515 self._get_commit_or_redirect(get_commit_id)
519
516
520 commit_id, f_path = self._get_commit_and_path()
517 commit_id, f_path = self._get_commit_and_path()
521 c.commit = self._get_commit_or_redirect(commit_id)
518 c.commit = self._get_commit_or_redirect(commit_id)
522 c.branch = self.request.GET.get('branch', None)
519 c.branch = self.request.GET.get('branch', None)
523 c.f_path = f_path
520 c.f_path = f_path
524
521
525 # prev link
522 # prev link
526 try:
523 try:
527 prev_commit = c.commit.prev(c.branch)
524 prev_commit = c.commit.prev(c.branch)
528 c.prev_commit = prev_commit
525 c.prev_commit = prev_commit
529 c.url_prev = h.route_path(
526 c.url_prev = h.route_path(
530 'repo_files', repo_name=self.db_repo_name,
527 'repo_files', repo_name=self.db_repo_name,
531 commit_id=prev_commit.raw_id, f_path=f_path)
528 commit_id=prev_commit.raw_id, f_path=f_path)
532 if c.branch:
529 if c.branch:
533 c.url_prev += '?branch=%s' % c.branch
530 c.url_prev += '?branch=%s' % c.branch
534 except (CommitDoesNotExistError, VCSError):
531 except (CommitDoesNotExistError, VCSError):
535 c.url_prev = '#'
532 c.url_prev = '#'
536 c.prev_commit = EmptyCommit()
533 c.prev_commit = EmptyCommit()
537
534
538 # next link
535 # next link
539 try:
536 try:
540 next_commit = c.commit.next(c.branch)
537 next_commit = c.commit.next(c.branch)
541 c.next_commit = next_commit
538 c.next_commit = next_commit
542 c.url_next = h.route_path(
539 c.url_next = h.route_path(
543 'repo_files', repo_name=self.db_repo_name,
540 'repo_files', repo_name=self.db_repo_name,
544 commit_id=next_commit.raw_id, f_path=f_path)
541 commit_id=next_commit.raw_id, f_path=f_path)
545 if c.branch:
542 if c.branch:
546 c.url_next += '?branch=%s' % c.branch
543 c.url_next += '?branch=%s' % c.branch
547 except (CommitDoesNotExistError, VCSError):
544 except (CommitDoesNotExistError, VCSError):
548 c.url_next = '#'
545 c.url_next = '#'
549 c.next_commit = EmptyCommit()
546 c.next_commit = EmptyCommit()
550
547
551 # files or dirs
548 # files or dirs
552 try:
549 try:
553 c.file = c.commit.get_node(f_path)
550 c.file = c.commit.get_node(f_path)
554 c.file_author = True
551 c.file_author = True
555 c.file_tree = ''
552 c.file_tree = ''
556
553
557 # load file content
554 # load file content
558 if c.file.is_file():
555 if c.file.is_file():
559 c.lf_node = c.file.get_largefile_node()
556 c.lf_node = c.file.get_largefile_node()
560
557
561 c.file_source_page = 'true'
558 c.file_source_page = 'true'
562 c.file_last_commit = c.file.last_commit
559 c.file_last_commit = c.file.last_commit
563 if c.file.size < c.visual.cut_off_limit_diff:
560 if c.file.size < c.visual.cut_off_limit_diff:
564 if c.annotate: # annotation has precedence over renderer
561 if c.annotate: # annotation has precedence over renderer
565 c.annotated_lines = filenode_as_annotated_lines_tokens(
562 c.annotated_lines = filenode_as_annotated_lines_tokens(
566 c.file
563 c.file
567 )
564 )
568 else:
565 else:
569 c.renderer = (
566 c.renderer = (
570 c.renderer and h.renderer_from_filename(c.file.path)
567 c.renderer and h.renderer_from_filename(c.file.path)
571 )
568 )
572 if not c.renderer:
569 if not c.renderer:
573 c.lines = filenode_as_lines_tokens(c.file)
570 c.lines = filenode_as_lines_tokens(c.file)
574
571
575 c.on_branch_head = self._is_valid_head(
572 c.on_branch_head = self._is_valid_head(
576 commit_id, self.rhodecode_vcs_repo)
573 commit_id, self.rhodecode_vcs_repo)
577
574
578 branch = c.commit.branch if (
575 branch = c.commit.branch if (
579 c.commit.branch and '/' not in c.commit.branch) else None
576 c.commit.branch and '/' not in c.commit.branch) else None
580 c.branch_or_raw_id = branch or c.commit.raw_id
577 c.branch_or_raw_id = branch or c.commit.raw_id
581 c.branch_name = c.commit.branch or h.short_id(c.commit.raw_id)
578 c.branch_name = c.commit.branch or h.short_id(c.commit.raw_id)
582
579
583 author = c.file_last_commit.author
580 author = c.file_last_commit.author
584 c.authors = [[
581 c.authors = [[
585 h.email(author),
582 h.email(author),
586 h.person(author, 'username_or_name_or_email'),
583 h.person(author, 'username_or_name_or_email'),
587 1
584 1
588 ]]
585 ]]
589
586
590 else: # load tree content at path
587 else: # load tree content at path
591 c.file_source_page = 'false'
588 c.file_source_page = 'false'
592 c.authors = []
589 c.authors = []
593 # this loads a simple tree without metadata to speed things up
590 # this loads a simple tree without metadata to speed things up
594 # later via ajax we call repo_nodetree_full and fetch whole
591 # later via ajax we call repo_nodetree_full and fetch whole
595 c.file_tree = self._get_tree_at_commit(
592 c.file_tree = self._get_tree_at_commit(
596 c, c.commit.raw_id, f_path)
593 c, c.commit.raw_id, f_path)
597
594
598 except RepositoryError as e:
595 except RepositoryError as e:
599 h.flash(safe_str(h.escape(e)), category='error')
596 h.flash(safe_str(h.escape(e)), category='error')
600 raise HTTPNotFound()
597 raise HTTPNotFound()
601
598
602 if self.request.environ.get('HTTP_X_PJAX'):
599 if self.request.environ.get('HTTP_X_PJAX'):
603 html = render('rhodecode:templates/files/files_pjax.mako',
600 html = render('rhodecode:templates/files/files_pjax.mako',
604 self._get_template_context(c), self.request)
601 self._get_template_context(c), self.request)
605 else:
602 else:
606 html = render('rhodecode:templates/files/files.mako',
603 html = render('rhodecode:templates/files/files.mako',
607 self._get_template_context(c), self.request)
604 self._get_template_context(c), self.request)
608 return Response(html)
605 return Response(html)
609
606
610 @HasRepoPermissionAnyDecorator(
607 @HasRepoPermissionAnyDecorator(
611 'repository.read', 'repository.write', 'repository.admin')
608 'repository.read', 'repository.write', 'repository.admin')
612 @view_config(
609 @view_config(
613 route_name='repo_files:annotated_previous', request_method='GET',
610 route_name='repo_files:annotated_previous', request_method='GET',
614 renderer=None)
611 renderer=None)
615 def repo_files_annotated_previous(self):
612 def repo_files_annotated_previous(self):
616 self.load_default_context()
613 self.load_default_context()
617
614
618 commit_id, f_path = self._get_commit_and_path()
615 commit_id, f_path = self._get_commit_and_path()
619 commit = self._get_commit_or_redirect(commit_id)
616 commit = self._get_commit_or_redirect(commit_id)
620 prev_commit_id = commit.raw_id
617 prev_commit_id = commit.raw_id
621 line_anchor = self.request.GET.get('line_anchor')
618 line_anchor = self.request.GET.get('line_anchor')
622 is_file = False
619 is_file = False
623 try:
620 try:
624 _file = commit.get_node(f_path)
621 _file = commit.get_node(f_path)
625 is_file = _file.is_file()
622 is_file = _file.is_file()
626 except (NodeDoesNotExistError, CommitDoesNotExistError, VCSError):
623 except (NodeDoesNotExistError, CommitDoesNotExistError, VCSError):
627 pass
624 pass
628
625
629 if is_file:
626 if is_file:
630 history = commit.get_file_history(f_path)
627 history = commit.get_file_history(f_path)
631 prev_commit_id = history[1].raw_id \
628 prev_commit_id = history[1].raw_id \
632 if len(history) > 1 else prev_commit_id
629 if len(history) > 1 else prev_commit_id
633 prev_url = h.route_path(
630 prev_url = h.route_path(
634 'repo_files:annotated', repo_name=self.db_repo_name,
631 'repo_files:annotated', repo_name=self.db_repo_name,
635 commit_id=prev_commit_id, f_path=f_path,
632 commit_id=prev_commit_id, f_path=f_path,
636 _anchor='L{}'.format(line_anchor))
633 _anchor='L{}'.format(line_anchor))
637
634
638 raise HTTPFound(prev_url)
635 raise HTTPFound(prev_url)
639
636
640 @LoginRequired()
637 @LoginRequired()
641 @HasRepoPermissionAnyDecorator(
638 @HasRepoPermissionAnyDecorator(
642 'repository.read', 'repository.write', 'repository.admin')
639 'repository.read', 'repository.write', 'repository.admin')
643 @view_config(
640 @view_config(
644 route_name='repo_nodetree_full', request_method='GET',
641 route_name='repo_nodetree_full', request_method='GET',
645 renderer=None, xhr=True)
642 renderer=None, xhr=True)
646 @view_config(
643 @view_config(
647 route_name='repo_nodetree_full:default_path', request_method='GET',
644 route_name='repo_nodetree_full:default_path', request_method='GET',
648 renderer=None, xhr=True)
645 renderer=None, xhr=True)
649 def repo_nodetree_full(self):
646 def repo_nodetree_full(self):
650 """
647 """
651 Returns rendered html of file tree that contains commit date,
648 Returns rendered html of file tree that contains commit date,
652 author, commit_id for the specified combination of
649 author, commit_id for the specified combination of
653 repo, commit_id and file path
650 repo, commit_id and file path
654 """
651 """
655 c = self.load_default_context()
652 c = self.load_default_context()
656
653
657 commit_id, f_path = self._get_commit_and_path()
654 commit_id, f_path = self._get_commit_and_path()
658 commit = self._get_commit_or_redirect(commit_id)
655 commit = self._get_commit_or_redirect(commit_id)
659 try:
656 try:
660 dir_node = commit.get_node(f_path)
657 dir_node = commit.get_node(f_path)
661 except RepositoryError as e:
658 except RepositoryError as e:
662 return Response('error: {}'.format(h.escape(safe_str(e))))
659 return Response('error: {}'.format(h.escape(safe_str(e))))
663
660
664 if dir_node.is_file():
661 if dir_node.is_file():
665 return Response('')
662 return Response('')
666
663
667 c.file = dir_node
664 c.file = dir_node
668 c.commit = commit
665 c.commit = commit
669
666
670 # using force=True here, make a little trick. We flush the cache and
667 # using force=True here, make a little trick. We flush the cache and
671 # compute it using the same key as without previous full_load, so now
668 # compute it using the same key as without previous full_load, so now
672 # the fully loaded tree is now returned instead of partial,
669 # the fully loaded tree is now returned instead of partial,
673 # and we store this in caches
670 # and we store this in caches
674 html = self._get_tree_at_commit(
671 html = self._get_tree_at_commit(
675 c, commit.raw_id, dir_node.path, full_load=True, force=True)
672 c, commit.raw_id, dir_node.path, full_load=True, force=True)
676
673
677 return Response(html)
674 return Response(html)
678
675
679 def _get_attachement_disposition(self, f_path):
676 def _get_attachement_disposition(self, f_path):
680 return 'attachment; filename=%s' % \
677 return 'attachment; filename=%s' % \
681 safe_str(f_path.split(Repository.NAME_SEP)[-1])
678 safe_str(f_path.split(Repository.NAME_SEP)[-1])
682
679
683 @LoginRequired()
680 @LoginRequired()
684 @HasRepoPermissionAnyDecorator(
681 @HasRepoPermissionAnyDecorator(
685 'repository.read', 'repository.write', 'repository.admin')
682 'repository.read', 'repository.write', 'repository.admin')
686 @view_config(
683 @view_config(
687 route_name='repo_file_raw', request_method='GET',
684 route_name='repo_file_raw', request_method='GET',
688 renderer=None)
685 renderer=None)
689 def repo_file_raw(self):
686 def repo_file_raw(self):
690 """
687 """
691 Action for show as raw, some mimetypes are "rendered",
688 Action for show as raw, some mimetypes are "rendered",
692 those include images, icons.
689 those include images, icons.
693 """
690 """
694 c = self.load_default_context()
691 c = self.load_default_context()
695
692
696 commit_id, f_path = self._get_commit_and_path()
693 commit_id, f_path = self._get_commit_and_path()
697 commit = self._get_commit_or_redirect(commit_id)
694 commit = self._get_commit_or_redirect(commit_id)
698 file_node = self._get_filenode_or_redirect(commit, f_path)
695 file_node = self._get_filenode_or_redirect(commit, f_path)
699
696
700 raw_mimetype_mapping = {
697 raw_mimetype_mapping = {
701 # map original mimetype to a mimetype used for "show as raw"
698 # map original mimetype to a mimetype used for "show as raw"
702 # you can also provide a content-disposition to override the
699 # you can also provide a content-disposition to override the
703 # default "attachment" disposition.
700 # default "attachment" disposition.
704 # orig_type: (new_type, new_dispo)
701 # orig_type: (new_type, new_dispo)
705
702
706 # show images inline:
703 # show images inline:
707 # Do not re-add SVG: it is unsafe and permits XSS attacks. One can
704 # Do not re-add SVG: it is unsafe and permits XSS attacks. One can
708 # for example render an SVG with javascript inside or even render
705 # for example render an SVG with javascript inside or even render
709 # HTML.
706 # HTML.
710 'image/x-icon': ('image/x-icon', 'inline'),
707 'image/x-icon': ('image/x-icon', 'inline'),
711 'image/png': ('image/png', 'inline'),
708 'image/png': ('image/png', 'inline'),
712 'image/gif': ('image/gif', 'inline'),
709 'image/gif': ('image/gif', 'inline'),
713 'image/jpeg': ('image/jpeg', 'inline'),
710 'image/jpeg': ('image/jpeg', 'inline'),
714 'application/pdf': ('application/pdf', 'inline'),
711 'application/pdf': ('application/pdf', 'inline'),
715 }
712 }
716
713
717 mimetype = file_node.mimetype
714 mimetype = file_node.mimetype
718 try:
715 try:
719 mimetype, disposition = raw_mimetype_mapping[mimetype]
716 mimetype, disposition = raw_mimetype_mapping[mimetype]
720 except KeyError:
717 except KeyError:
721 # we don't know anything special about this, handle it safely
718 # we don't know anything special about this, handle it safely
722 if file_node.is_binary:
719 if file_node.is_binary:
723 # do same as download raw for binary files
720 # do same as download raw for binary files
724 mimetype, disposition = 'application/octet-stream', 'attachment'
721 mimetype, disposition = 'application/octet-stream', 'attachment'
725 else:
722 else:
726 # do not just use the original mimetype, but force text/plain,
723 # do not just use the original mimetype, but force text/plain,
727 # otherwise it would serve text/html and that might be unsafe.
724 # otherwise it would serve text/html and that might be unsafe.
728 # Note: underlying vcs library fakes text/plain mimetype if the
725 # Note: underlying vcs library fakes text/plain mimetype if the
729 # mimetype can not be determined and it thinks it is not
726 # mimetype can not be determined and it thinks it is not
730 # binary.This might lead to erroneous text display in some
727 # binary.This might lead to erroneous text display in some
731 # cases, but helps in other cases, like with text files
728 # cases, but helps in other cases, like with text files
732 # without extension.
729 # without extension.
733 mimetype, disposition = 'text/plain', 'inline'
730 mimetype, disposition = 'text/plain', 'inline'
734
731
735 if disposition == 'attachment':
732 if disposition == 'attachment':
736 disposition = self._get_attachement_disposition(f_path)
733 disposition = self._get_attachement_disposition(f_path)
737
734
738 def stream_node():
735 def stream_node():
739 yield file_node.raw_bytes
736 yield file_node.raw_bytes
740
737
741 response = Response(app_iter=stream_node())
738 response = Response(app_iter=stream_node())
742 response.content_disposition = disposition
739 response.content_disposition = disposition
743 response.content_type = mimetype
740 response.content_type = mimetype
744
741
745 charset = self._get_default_encoding(c)
742 charset = self._get_default_encoding(c)
746 if charset:
743 if charset:
747 response.charset = charset
744 response.charset = charset
748
745
749 return response
746 return response
750
747
751 @LoginRequired()
748 @LoginRequired()
752 @HasRepoPermissionAnyDecorator(
749 @HasRepoPermissionAnyDecorator(
753 'repository.read', 'repository.write', 'repository.admin')
750 'repository.read', 'repository.write', 'repository.admin')
754 @view_config(
751 @view_config(
755 route_name='repo_file_download', request_method='GET',
752 route_name='repo_file_download', request_method='GET',
756 renderer=None)
753 renderer=None)
757 @view_config(
754 @view_config(
758 route_name='repo_file_download:legacy', request_method='GET',
755 route_name='repo_file_download:legacy', request_method='GET',
759 renderer=None)
756 renderer=None)
760 def repo_file_download(self):
757 def repo_file_download(self):
761 c = self.load_default_context()
758 c = self.load_default_context()
762
759
763 commit_id, f_path = self._get_commit_and_path()
760 commit_id, f_path = self._get_commit_and_path()
764 commit = self._get_commit_or_redirect(commit_id)
761 commit = self._get_commit_or_redirect(commit_id)
765 file_node = self._get_filenode_or_redirect(commit, f_path)
762 file_node = self._get_filenode_or_redirect(commit, f_path)
766
763
767 if self.request.GET.get('lf'):
764 if self.request.GET.get('lf'):
768 # only if lf get flag is passed, we download this file
765 # only if lf get flag is passed, we download this file
769 # as LFS/Largefile
766 # as LFS/Largefile
770 lf_node = file_node.get_largefile_node()
767 lf_node = file_node.get_largefile_node()
771 if lf_node:
768 if lf_node:
772 # overwrite our pointer with the REAL large-file
769 # overwrite our pointer with the REAL large-file
773 file_node = lf_node
770 file_node = lf_node
774
771
775 disposition = self._get_attachement_disposition(f_path)
772 disposition = self._get_attachement_disposition(f_path)
776
773
777 def stream_node():
774 def stream_node():
778 yield file_node.raw_bytes
775 yield file_node.raw_bytes
779
776
780 response = Response(app_iter=stream_node())
777 response = Response(app_iter=stream_node())
781 response.content_disposition = disposition
778 response.content_disposition = disposition
782 response.content_type = file_node.mimetype
779 response.content_type = file_node.mimetype
783
780
784 charset = self._get_default_encoding(c)
781 charset = self._get_default_encoding(c)
785 if charset:
782 if charset:
786 response.charset = charset
783 response.charset = charset
787
784
788 return response
785 return response
789
786
790 def _get_nodelist_at_commit(self, repo_name, commit_id, f_path):
787 def _get_nodelist_at_commit(self, repo_name, commit_id, f_path):
791 def _cached_nodes():
788 def _cached_nodes():
792 log.debug('Generating cached nodelist for %s, %s, %s',
789 log.debug('Generating cached nodelist for %s, %s, %s',
793 repo_name, commit_id, f_path)
790 repo_name, commit_id, f_path)
794 try:
791 try:
795 _d, _f = ScmModel().get_nodes(
792 _d, _f = ScmModel().get_nodes(
796 repo_name, commit_id, f_path, flat=False)
793 repo_name, commit_id, f_path, flat=False)
797 except (RepositoryError, CommitDoesNotExistError, Exception) as e:
794 except (RepositoryError, CommitDoesNotExistError, Exception) as e:
798 log.exception(safe_str(e))
795 log.exception(safe_str(e))
799 h.flash(safe_str(h.escape(e)), category='error')
796 h.flash(safe_str(h.escape(e)), category='error')
800 raise HTTPFound(h.route_path(
797 raise HTTPFound(h.route_path(
801 'repo_files', repo_name=self.db_repo_name,
798 'repo_files', repo_name=self.db_repo_name,
802 commit_id='tip', f_path='/'))
799 commit_id='tip', f_path='/'))
803 return _d + _f
800 return _d + _f
804
801
805 cache_manager = self._get_tree_cache_manager(
802 cache_manager = self._get_tree_cache_manager(
806 caches.FILE_SEARCH_TREE_META)
803 caches.FILE_SEARCH_TREE_META)
807
804
808 cache_key = caches.compute_key_from_params(
805 cache_key = caches.compute_key_from_params(
809 repo_name, commit_id, f_path)
806 repo_name, commit_id, f_path)
810 return cache_manager.get(cache_key, createfunc=_cached_nodes)
807 return cache_manager.get(cache_key, createfunc=_cached_nodes)
811
808
812 @LoginRequired()
809 @LoginRequired()
813 @HasRepoPermissionAnyDecorator(
810 @HasRepoPermissionAnyDecorator(
814 'repository.read', 'repository.write', 'repository.admin')
811 'repository.read', 'repository.write', 'repository.admin')
815 @view_config(
812 @view_config(
816 route_name='repo_files_nodelist', request_method='GET',
813 route_name='repo_files_nodelist', request_method='GET',
817 renderer='json_ext', xhr=True)
814 renderer='json_ext', xhr=True)
818 def repo_nodelist(self):
815 def repo_nodelist(self):
819 self.load_default_context()
816 self.load_default_context()
820
817
821 commit_id, f_path = self._get_commit_and_path()
818 commit_id, f_path = self._get_commit_and_path()
822 commit = self._get_commit_or_redirect(commit_id)
819 commit = self._get_commit_or_redirect(commit_id)
823
820
824 metadata = self._get_nodelist_at_commit(
821 metadata = self._get_nodelist_at_commit(
825 self.db_repo_name, commit.raw_id, f_path)
822 self.db_repo_name, commit.raw_id, f_path)
826 return {'nodes': metadata}
823 return {'nodes': metadata}
827
824
828 def _create_references(
825 def _create_references(
829 self, branches_or_tags, symbolic_reference, f_path):
826 self, branches_or_tags, symbolic_reference, f_path):
830 items = []
827 items = []
831 for name, commit_id in branches_or_tags.items():
828 for name, commit_id in branches_or_tags.items():
832 sym_ref = symbolic_reference(commit_id, name, f_path)
829 sym_ref = symbolic_reference(commit_id, name, f_path)
833 items.append((sym_ref, name))
830 items.append((sym_ref, name))
834 return items
831 return items
835
832
836 def _symbolic_reference(self, commit_id, name, f_path):
833 def _symbolic_reference(self, commit_id, name, f_path):
837 return commit_id
834 return commit_id
838
835
839 def _symbolic_reference_svn(self, commit_id, name, f_path):
836 def _symbolic_reference_svn(self, commit_id, name, f_path):
840 new_f_path = vcspath.join(name, f_path)
837 new_f_path = vcspath.join(name, f_path)
841 return u'%s@%s' % (new_f_path, commit_id)
838 return u'%s@%s' % (new_f_path, commit_id)
842
839
843 def _get_node_history(self, commit_obj, f_path, commits=None):
840 def _get_node_history(self, commit_obj, f_path, commits=None):
844 """
841 """
845 get commit history for given node
842 get commit history for given node
846
843
847 :param commit_obj: commit to calculate history
844 :param commit_obj: commit to calculate history
848 :param f_path: path for node to calculate history for
845 :param f_path: path for node to calculate history for
849 :param commits: if passed don't calculate history and take
846 :param commits: if passed don't calculate history and take
850 commits defined in this list
847 commits defined in this list
851 """
848 """
852 _ = self.request.translate
849 _ = self.request.translate
853
850
854 # calculate history based on tip
851 # calculate history based on tip
855 tip = self.rhodecode_vcs_repo.get_commit()
852 tip = self.rhodecode_vcs_repo.get_commit()
856 if commits is None:
853 if commits is None:
857 pre_load = ["author", "branch"]
854 pre_load = ["author", "branch"]
858 try:
855 try:
859 commits = tip.get_file_history(f_path, pre_load=pre_load)
856 commits = tip.get_file_history(f_path, pre_load=pre_load)
860 except (NodeDoesNotExistError, CommitError):
857 except (NodeDoesNotExistError, CommitError):
861 # this node is not present at tip!
858 # this node is not present at tip!
862 commits = commit_obj.get_file_history(f_path, pre_load=pre_load)
859 commits = commit_obj.get_file_history(f_path, pre_load=pre_load)
863
860
864 history = []
861 history = []
865 commits_group = ([], _("Changesets"))
862 commits_group = ([], _("Changesets"))
866 for commit in commits:
863 for commit in commits:
867 branch = ' (%s)' % commit.branch if commit.branch else ''
864 branch = ' (%s)' % commit.branch if commit.branch else ''
868 n_desc = 'r%s:%s%s' % (commit.idx, commit.short_id, branch)
865 n_desc = 'r%s:%s%s' % (commit.idx, commit.short_id, branch)
869 commits_group[0].append((commit.raw_id, n_desc,))
866 commits_group[0].append((commit.raw_id, n_desc,))
870 history.append(commits_group)
867 history.append(commits_group)
871
868
872 symbolic_reference = self._symbolic_reference
869 symbolic_reference = self._symbolic_reference
873
870
874 if self.rhodecode_vcs_repo.alias == 'svn':
871 if self.rhodecode_vcs_repo.alias == 'svn':
875 adjusted_f_path = RepoFilesView.adjust_file_path_for_svn(
872 adjusted_f_path = RepoFilesView.adjust_file_path_for_svn(
876 f_path, self.rhodecode_vcs_repo)
873 f_path, self.rhodecode_vcs_repo)
877 if adjusted_f_path != f_path:
874 if adjusted_f_path != f_path:
878 log.debug(
875 log.debug(
879 'Recognized svn tag or branch in file "%s", using svn '
876 'Recognized svn tag or branch in file "%s", using svn '
880 'specific symbolic references', f_path)
877 'specific symbolic references', f_path)
881 f_path = adjusted_f_path
878 f_path = adjusted_f_path
882 symbolic_reference = self._symbolic_reference_svn
879 symbolic_reference = self._symbolic_reference_svn
883
880
884 branches = self._create_references(
881 branches = self._create_references(
885 self.rhodecode_vcs_repo.branches, symbolic_reference, f_path)
882 self.rhodecode_vcs_repo.branches, symbolic_reference, f_path)
886 branches_group = (branches, _("Branches"))
883 branches_group = (branches, _("Branches"))
887
884
888 tags = self._create_references(
885 tags = self._create_references(
889 self.rhodecode_vcs_repo.tags, symbolic_reference, f_path)
886 self.rhodecode_vcs_repo.tags, symbolic_reference, f_path)
890 tags_group = (tags, _("Tags"))
887 tags_group = (tags, _("Tags"))
891
888
892 history.append(branches_group)
889 history.append(branches_group)
893 history.append(tags_group)
890 history.append(tags_group)
894
891
895 return history, commits
892 return history, commits
896
893
897 @LoginRequired()
894 @LoginRequired()
898 @HasRepoPermissionAnyDecorator(
895 @HasRepoPermissionAnyDecorator(
899 'repository.read', 'repository.write', 'repository.admin')
896 'repository.read', 'repository.write', 'repository.admin')
900 @view_config(
897 @view_config(
901 route_name='repo_file_history', request_method='GET',
898 route_name='repo_file_history', request_method='GET',
902 renderer='json_ext')
899 renderer='json_ext')
903 def repo_file_history(self):
900 def repo_file_history(self):
904 self.load_default_context()
901 self.load_default_context()
905
902
906 commit_id, f_path = self._get_commit_and_path()
903 commit_id, f_path = self._get_commit_and_path()
907 commit = self._get_commit_or_redirect(commit_id)
904 commit = self._get_commit_or_redirect(commit_id)
908 file_node = self._get_filenode_or_redirect(commit, f_path)
905 file_node = self._get_filenode_or_redirect(commit, f_path)
909
906
910 if file_node.is_file():
907 if file_node.is_file():
911 file_history, _hist = self._get_node_history(commit, f_path)
908 file_history, _hist = self._get_node_history(commit, f_path)
912
909
913 res = []
910 res = []
914 for obj in file_history:
911 for obj in file_history:
915 res.append({
912 res.append({
916 'text': obj[1],
913 'text': obj[1],
917 'children': [{'id': o[0], 'text': o[1]} for o in obj[0]]
914 'children': [{'id': o[0], 'text': o[1]} for o in obj[0]]
918 })
915 })
919
916
920 data = {
917 data = {
921 'more': False,
918 'more': False,
922 'results': res
919 'results': res
923 }
920 }
924 return data
921 return data
925
922
926 log.warning('Cannot fetch history for directory')
923 log.warning('Cannot fetch history for directory')
927 raise HTTPBadRequest()
924 raise HTTPBadRequest()
928
925
929 @LoginRequired()
926 @LoginRequired()
930 @HasRepoPermissionAnyDecorator(
927 @HasRepoPermissionAnyDecorator(
931 'repository.read', 'repository.write', 'repository.admin')
928 'repository.read', 'repository.write', 'repository.admin')
932 @view_config(
929 @view_config(
933 route_name='repo_file_authors', request_method='GET',
930 route_name='repo_file_authors', request_method='GET',
934 renderer='rhodecode:templates/files/file_authors_box.mako')
931 renderer='rhodecode:templates/files/file_authors_box.mako')
935 def repo_file_authors(self):
932 def repo_file_authors(self):
936 c = self.load_default_context()
933 c = self.load_default_context()
937
934
938 commit_id, f_path = self._get_commit_and_path()
935 commit_id, f_path = self._get_commit_and_path()
939 commit = self._get_commit_or_redirect(commit_id)
936 commit = self._get_commit_or_redirect(commit_id)
940 file_node = self._get_filenode_or_redirect(commit, f_path)
937 file_node = self._get_filenode_or_redirect(commit, f_path)
941
938
942 if not file_node.is_file():
939 if not file_node.is_file():
943 raise HTTPBadRequest()
940 raise HTTPBadRequest()
944
941
945 c.file_last_commit = file_node.last_commit
942 c.file_last_commit = file_node.last_commit
946 if self.request.GET.get('annotate') == '1':
943 if self.request.GET.get('annotate') == '1':
947 # use _hist from annotation if annotation mode is on
944 # use _hist from annotation if annotation mode is on
948 commit_ids = set(x[1] for x in file_node.annotate)
945 commit_ids = set(x[1] for x in file_node.annotate)
949 _hist = (
946 _hist = (
950 self.rhodecode_vcs_repo.get_commit(commit_id)
947 self.rhodecode_vcs_repo.get_commit(commit_id)
951 for commit_id in commit_ids)
948 for commit_id in commit_ids)
952 else:
949 else:
953 _f_history, _hist = self._get_node_history(commit, f_path)
950 _f_history, _hist = self._get_node_history(commit, f_path)
954 c.file_author = False
951 c.file_author = False
955
952
956 unique = collections.OrderedDict()
953 unique = collections.OrderedDict()
957 for commit in _hist:
954 for commit in _hist:
958 author = commit.author
955 author = commit.author
959 if author not in unique:
956 if author not in unique:
960 unique[commit.author] = [
957 unique[commit.author] = [
961 h.email(author),
958 h.email(author),
962 h.person(author, 'username_or_name_or_email'),
959 h.person(author, 'username_or_name_or_email'),
963 1 # counter
960 1 # counter
964 ]
961 ]
965
962
966 else:
963 else:
967 # increase counter
964 # increase counter
968 unique[commit.author][2] += 1
965 unique[commit.author][2] += 1
969
966
970 c.authors = [val for val in unique.values()]
967 c.authors = [val for val in unique.values()]
971
968
972 return self._get_template_context(c)
969 return self._get_template_context(c)
973
970
974 @LoginRequired()
971 @LoginRequired()
975 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
972 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
976 @view_config(
973 @view_config(
977 route_name='repo_files_remove_file', request_method='GET',
974 route_name='repo_files_remove_file', request_method='GET',
978 renderer='rhodecode:templates/files/files_delete.mako')
975 renderer='rhodecode:templates/files/files_delete.mako')
979 def repo_files_remove_file(self):
976 def repo_files_remove_file(self):
980 _ = self.request.translate
977 _ = self.request.translate
981 c = self.load_default_context()
978 c = self.load_default_context()
982 commit_id, f_path = self._get_commit_and_path()
979 commit_id, f_path = self._get_commit_and_path()
983
980
984 self._ensure_not_locked()
981 self._ensure_not_locked()
985
982
986 if not self._is_valid_head(commit_id, self.rhodecode_vcs_repo):
983 if not self._is_valid_head(commit_id, self.rhodecode_vcs_repo):
987 h.flash(_('You can only delete files with commit '
984 h.flash(_('You can only delete files with commit '
988 'being a valid branch '), category='warning')
985 'being a valid branch '), category='warning')
989 raise HTTPFound(
986 raise HTTPFound(
990 h.route_path('repo_files',
987 h.route_path('repo_files',
991 repo_name=self.db_repo_name, commit_id='tip',
988 repo_name=self.db_repo_name, commit_id='tip',
992 f_path=f_path))
989 f_path=f_path))
993
990
994 c.commit = self._get_commit_or_redirect(commit_id)
991 c.commit = self._get_commit_or_redirect(commit_id)
995 c.file = self._get_filenode_or_redirect(c.commit, f_path)
992 c.file = self._get_filenode_or_redirect(c.commit, f_path)
996
993
997 c.default_message = _(
994 c.default_message = _(
998 'Deleted file {} via RhodeCode Enterprise').format(f_path)
995 'Deleted file {} via RhodeCode Enterprise').format(f_path)
999 c.f_path = f_path
996 c.f_path = f_path
1000
997
1001 return self._get_template_context(c)
998 return self._get_template_context(c)
1002
999
1003 @LoginRequired()
1000 @LoginRequired()
1004 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1001 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1005 @CSRFRequired()
1002 @CSRFRequired()
1006 @view_config(
1003 @view_config(
1007 route_name='repo_files_delete_file', request_method='POST',
1004 route_name='repo_files_delete_file', request_method='POST',
1008 renderer=None)
1005 renderer=None)
1009 def repo_files_delete_file(self):
1006 def repo_files_delete_file(self):
1010 _ = self.request.translate
1007 _ = self.request.translate
1011
1008
1012 c = self.load_default_context()
1009 c = self.load_default_context()
1013 commit_id, f_path = self._get_commit_and_path()
1010 commit_id, f_path = self._get_commit_and_path()
1014
1011
1015 self._ensure_not_locked()
1012 self._ensure_not_locked()
1016
1013
1017 if not self._is_valid_head(commit_id, self.rhodecode_vcs_repo):
1014 if not self._is_valid_head(commit_id, self.rhodecode_vcs_repo):
1018 h.flash(_('You can only delete files with commit '
1015 h.flash(_('You can only delete files with commit '
1019 'being a valid branch '), category='warning')
1016 'being a valid branch '), category='warning')
1020 raise HTTPFound(
1017 raise HTTPFound(
1021 h.route_path('repo_files',
1018 h.route_path('repo_files',
1022 repo_name=self.db_repo_name, commit_id='tip',
1019 repo_name=self.db_repo_name, commit_id='tip',
1023 f_path=f_path))
1020 f_path=f_path))
1024
1021
1025 c.commit = self._get_commit_or_redirect(commit_id)
1022 c.commit = self._get_commit_or_redirect(commit_id)
1026 c.file = self._get_filenode_or_redirect(c.commit, f_path)
1023 c.file = self._get_filenode_or_redirect(c.commit, f_path)
1027
1024
1028 c.default_message = _(
1025 c.default_message = _(
1029 'Deleted file {} via RhodeCode Enterprise').format(f_path)
1026 'Deleted file {} via RhodeCode Enterprise').format(f_path)
1030 c.f_path = f_path
1027 c.f_path = f_path
1031 node_path = f_path
1028 node_path = f_path
1032 author = self._rhodecode_db_user.full_contact
1029 author = self._rhodecode_db_user.full_contact
1033 message = self.request.POST.get('message') or c.default_message
1030 message = self.request.POST.get('message') or c.default_message
1034 try:
1031 try:
1035 nodes = {
1032 nodes = {
1036 node_path: {
1033 node_path: {
1037 'content': ''
1034 'content': ''
1038 }
1035 }
1039 }
1036 }
1040 ScmModel().delete_nodes(
1037 ScmModel().delete_nodes(
1041 user=self._rhodecode_db_user.user_id, repo=self.db_repo,
1038 user=self._rhodecode_db_user.user_id, repo=self.db_repo,
1042 message=message,
1039 message=message,
1043 nodes=nodes,
1040 nodes=nodes,
1044 parent_commit=c.commit,
1041 parent_commit=c.commit,
1045 author=author,
1042 author=author,
1046 )
1043 )
1047
1044
1048 h.flash(
1045 h.flash(
1049 _('Successfully deleted file `{}`').format(
1046 _('Successfully deleted file `{}`').format(
1050 h.escape(f_path)), category='success')
1047 h.escape(f_path)), category='success')
1051 except Exception:
1048 except Exception:
1052 log.exception('Error during commit operation')
1049 log.exception('Error during commit operation')
1053 h.flash(_('Error occurred during commit'), category='error')
1050 h.flash(_('Error occurred during commit'), category='error')
1054 raise HTTPFound(
1051 raise HTTPFound(
1055 h.route_path('repo_commit', repo_name=self.db_repo_name,
1052 h.route_path('repo_commit', repo_name=self.db_repo_name,
1056 commit_id='tip'))
1053 commit_id='tip'))
1057
1054
1058 @LoginRequired()
1055 @LoginRequired()
1059 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1056 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1060 @view_config(
1057 @view_config(
1061 route_name='repo_files_edit_file', request_method='GET',
1058 route_name='repo_files_edit_file', request_method='GET',
1062 renderer='rhodecode:templates/files/files_edit.mako')
1059 renderer='rhodecode:templates/files/files_edit.mako')
1063 def repo_files_edit_file(self):
1060 def repo_files_edit_file(self):
1064 _ = self.request.translate
1061 _ = self.request.translate
1065 c = self.load_default_context()
1062 c = self.load_default_context()
1066 commit_id, f_path = self._get_commit_and_path()
1063 commit_id, f_path = self._get_commit_and_path()
1067
1064
1068 self._ensure_not_locked()
1065 self._ensure_not_locked()
1069
1066
1070 if not self._is_valid_head(commit_id, self.rhodecode_vcs_repo):
1067 if not self._is_valid_head(commit_id, self.rhodecode_vcs_repo):
1071 h.flash(_('You can only edit files with commit '
1068 h.flash(_('You can only edit files with commit '
1072 'being a valid branch '), category='warning')
1069 'being a valid branch '), category='warning')
1073 raise HTTPFound(
1070 raise HTTPFound(
1074 h.route_path('repo_files',
1071 h.route_path('repo_files',
1075 repo_name=self.db_repo_name, commit_id='tip',
1072 repo_name=self.db_repo_name, commit_id='tip',
1076 f_path=f_path))
1073 f_path=f_path))
1077
1074
1078 c.commit = self._get_commit_or_redirect(commit_id)
1075 c.commit = self._get_commit_or_redirect(commit_id)
1079 c.file = self._get_filenode_or_redirect(c.commit, f_path)
1076 c.file = self._get_filenode_or_redirect(c.commit, f_path)
1080
1077
1081 if c.file.is_binary:
1078 if c.file.is_binary:
1082 files_url = h.route_path(
1079 files_url = h.route_path(
1083 'repo_files',
1080 'repo_files',
1084 repo_name=self.db_repo_name,
1081 repo_name=self.db_repo_name,
1085 commit_id=c.commit.raw_id, f_path=f_path)
1082 commit_id=c.commit.raw_id, f_path=f_path)
1086 raise HTTPFound(files_url)
1083 raise HTTPFound(files_url)
1087
1084
1088 c.default_message = _(
1085 c.default_message = _(
1089 'Edited file {} via RhodeCode Enterprise').format(f_path)
1086 'Edited file {} via RhodeCode Enterprise').format(f_path)
1090 c.f_path = f_path
1087 c.f_path = f_path
1091
1088
1092 return self._get_template_context(c)
1089 return self._get_template_context(c)
1093
1090
1094 @LoginRequired()
1091 @LoginRequired()
1095 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1092 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1096 @CSRFRequired()
1093 @CSRFRequired()
1097 @view_config(
1094 @view_config(
1098 route_name='repo_files_update_file', request_method='POST',
1095 route_name='repo_files_update_file', request_method='POST',
1099 renderer=None)
1096 renderer=None)
1100 def repo_files_update_file(self):
1097 def repo_files_update_file(self):
1101 _ = self.request.translate
1098 _ = self.request.translate
1102 c = self.load_default_context()
1099 c = self.load_default_context()
1103 commit_id, f_path = self._get_commit_and_path()
1100 commit_id, f_path = self._get_commit_and_path()
1104
1101
1105 self._ensure_not_locked()
1102 self._ensure_not_locked()
1106
1103
1107 if not self._is_valid_head(commit_id, self.rhodecode_vcs_repo):
1104 if not self._is_valid_head(commit_id, self.rhodecode_vcs_repo):
1108 h.flash(_('You can only edit files with commit '
1105 h.flash(_('You can only edit files with commit '
1109 'being a valid branch '), category='warning')
1106 'being a valid branch '), category='warning')
1110 raise HTTPFound(
1107 raise HTTPFound(
1111 h.route_path('repo_files',
1108 h.route_path('repo_files',
1112 repo_name=self.db_repo_name, commit_id='tip',
1109 repo_name=self.db_repo_name, commit_id='tip',
1113 f_path=f_path))
1110 f_path=f_path))
1114
1111
1115 c.commit = self._get_commit_or_redirect(commit_id)
1112 c.commit = self._get_commit_or_redirect(commit_id)
1116 c.file = self._get_filenode_or_redirect(c.commit, f_path)
1113 c.file = self._get_filenode_or_redirect(c.commit, f_path)
1117
1114
1118 if c.file.is_binary:
1115 if c.file.is_binary:
1119 raise HTTPFound(
1116 raise HTTPFound(
1120 h.route_path('repo_files',
1117 h.route_path('repo_files',
1121 repo_name=self.db_repo_name,
1118 repo_name=self.db_repo_name,
1122 commit_id=c.commit.raw_id,
1119 commit_id=c.commit.raw_id,
1123 f_path=f_path))
1120 f_path=f_path))
1124
1121
1125 c.default_message = _(
1122 c.default_message = _(
1126 'Edited file {} via RhodeCode Enterprise').format(f_path)
1123 'Edited file {} via RhodeCode Enterprise').format(f_path)
1127 c.f_path = f_path
1124 c.f_path = f_path
1128 old_content = c.file.content
1125 old_content = c.file.content
1129 sl = old_content.splitlines(1)
1126 sl = old_content.splitlines(1)
1130 first_line = sl[0] if sl else ''
1127 first_line = sl[0] if sl else ''
1131
1128
1132 r_post = self.request.POST
1129 r_post = self.request.POST
1133 # modes: 0 - Unix, 1 - Mac, 2 - DOS
1130 # modes: 0 - Unix, 1 - Mac, 2 - DOS
1134 mode = detect_mode(first_line, 0)
1131 mode = detect_mode(first_line, 0)
1135 content = convert_line_endings(r_post.get('content', ''), mode)
1132 content = convert_line_endings(r_post.get('content', ''), mode)
1136
1133
1137 message = r_post.get('message') or c.default_message
1134 message = r_post.get('message') or c.default_message
1138 org_f_path = c.file.unicode_path
1135 org_f_path = c.file.unicode_path
1139 filename = r_post['filename']
1136 filename = r_post['filename']
1140 org_filename = c.file.name
1137 org_filename = c.file.name
1141
1138
1142 if content == old_content and filename == org_filename:
1139 if content == old_content and filename == org_filename:
1143 h.flash(_('No changes'), category='warning')
1140 h.flash(_('No changes'), category='warning')
1144 raise HTTPFound(
1141 raise HTTPFound(
1145 h.route_path('repo_commit', repo_name=self.db_repo_name,
1142 h.route_path('repo_commit', repo_name=self.db_repo_name,
1146 commit_id='tip'))
1143 commit_id='tip'))
1147 try:
1144 try:
1148 mapping = {
1145 mapping = {
1149 org_f_path: {
1146 org_f_path: {
1150 'org_filename': org_f_path,
1147 'org_filename': org_f_path,
1151 'filename': os.path.join(c.file.dir_path, filename),
1148 'filename': os.path.join(c.file.dir_path, filename),
1152 'content': content,
1149 'content': content,
1153 'lexer': '',
1150 'lexer': '',
1154 'op': 'mod',
1151 'op': 'mod',
1155 }
1152 }
1156 }
1153 }
1157
1154
1158 ScmModel().update_nodes(
1155 ScmModel().update_nodes(
1159 user=self._rhodecode_db_user.user_id,
1156 user=self._rhodecode_db_user.user_id,
1160 repo=self.db_repo,
1157 repo=self.db_repo,
1161 message=message,
1158 message=message,
1162 nodes=mapping,
1159 nodes=mapping,
1163 parent_commit=c.commit,
1160 parent_commit=c.commit,
1164 )
1161 )
1165
1162
1166 h.flash(
1163 h.flash(
1167 _('Successfully committed changes to file `{}`').format(
1164 _('Successfully committed changes to file `{}`').format(
1168 h.escape(f_path)), category='success')
1165 h.escape(f_path)), category='success')
1169 except Exception:
1166 except Exception:
1170 log.exception('Error occurred during commit')
1167 log.exception('Error occurred during commit')
1171 h.flash(_('Error occurred during commit'), category='error')
1168 h.flash(_('Error occurred during commit'), category='error')
1172 raise HTTPFound(
1169 raise HTTPFound(
1173 h.route_path('repo_commit', repo_name=self.db_repo_name,
1170 h.route_path('repo_commit', repo_name=self.db_repo_name,
1174 commit_id='tip'))
1171 commit_id='tip'))
1175
1172
1176 @LoginRequired()
1173 @LoginRequired()
1177 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1174 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1178 @view_config(
1175 @view_config(
1179 route_name='repo_files_add_file', request_method='GET',
1176 route_name='repo_files_add_file', request_method='GET',
1180 renderer='rhodecode:templates/files/files_add.mako')
1177 renderer='rhodecode:templates/files/files_add.mako')
1181 def repo_files_add_file(self):
1178 def repo_files_add_file(self):
1182 _ = self.request.translate
1179 _ = self.request.translate
1183 c = self.load_default_context()
1180 c = self.load_default_context()
1184 commit_id, f_path = self._get_commit_and_path()
1181 commit_id, f_path = self._get_commit_and_path()
1185
1182
1186 self._ensure_not_locked()
1183 self._ensure_not_locked()
1187
1184
1188 c.commit = self._get_commit_or_redirect(commit_id, redirect_after=False)
1185 c.commit = self._get_commit_or_redirect(commit_id, redirect_after=False)
1189 if c.commit is None:
1186 if c.commit is None:
1190 c.commit = EmptyCommit(alias=self.rhodecode_vcs_repo.alias)
1187 c.commit = EmptyCommit(alias=self.rhodecode_vcs_repo.alias)
1191 c.default_message = (_('Added file via RhodeCode Enterprise'))
1188 c.default_message = (_('Added file via RhodeCode Enterprise'))
1192 c.f_path = f_path.lstrip('/') # ensure not relative path
1189 c.f_path = f_path.lstrip('/') # ensure not relative path
1193
1190
1194 return self._get_template_context(c)
1191 return self._get_template_context(c)
1195
1192
1196 @LoginRequired()
1193 @LoginRequired()
1197 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1194 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1198 @CSRFRequired()
1195 @CSRFRequired()
1199 @view_config(
1196 @view_config(
1200 route_name='repo_files_create_file', request_method='POST',
1197 route_name='repo_files_create_file', request_method='POST',
1201 renderer=None)
1198 renderer=None)
1202 def repo_files_create_file(self):
1199 def repo_files_create_file(self):
1203 _ = self.request.translate
1200 _ = self.request.translate
1204 c = self.load_default_context()
1201 c = self.load_default_context()
1205 commit_id, f_path = self._get_commit_and_path()
1202 commit_id, f_path = self._get_commit_and_path()
1206
1203
1207 self._ensure_not_locked()
1204 self._ensure_not_locked()
1208
1205
1209 r_post = self.request.POST
1206 r_post = self.request.POST
1210
1207
1211 c.commit = self._get_commit_or_redirect(
1208 c.commit = self._get_commit_or_redirect(
1212 commit_id, redirect_after=False)
1209 commit_id, redirect_after=False)
1213 if c.commit is None:
1210 if c.commit is None:
1214 c.commit = EmptyCommit(alias=self.rhodecode_vcs_repo.alias)
1211 c.commit = EmptyCommit(alias=self.rhodecode_vcs_repo.alias)
1215 c.default_message = (_('Added file via RhodeCode Enterprise'))
1212 c.default_message = (_('Added file via RhodeCode Enterprise'))
1216 c.f_path = f_path
1213 c.f_path = f_path
1217 unix_mode = 0
1214 unix_mode = 0
1218 content = convert_line_endings(r_post.get('content', ''), unix_mode)
1215 content = convert_line_endings(r_post.get('content', ''), unix_mode)
1219
1216
1220 message = r_post.get('message') or c.default_message
1217 message = r_post.get('message') or c.default_message
1221 filename = r_post.get('filename')
1218 filename = r_post.get('filename')
1222 location = r_post.get('location', '') # dir location
1219 location = r_post.get('location', '') # dir location
1223 file_obj = r_post.get('upload_file', None)
1220 file_obj = r_post.get('upload_file', None)
1224
1221
1225 if file_obj is not None and hasattr(file_obj, 'filename'):
1222 if file_obj is not None and hasattr(file_obj, 'filename'):
1226 filename = r_post.get('filename_upload')
1223 filename = r_post.get('filename_upload')
1227 content = file_obj.file
1224 content = file_obj.file
1228
1225
1229 if hasattr(content, 'file'):
1226 if hasattr(content, 'file'):
1230 # non posix systems store real file under file attr
1227 # non posix systems store real file under file attr
1231 content = content.file
1228 content = content.file
1232
1229
1233 if self.rhodecode_vcs_repo.is_empty:
1230 if self.rhodecode_vcs_repo.is_empty:
1234 default_redirect_url = h.route_path(
1231 default_redirect_url = h.route_path(
1235 'repo_summary', repo_name=self.db_repo_name)
1232 'repo_summary', repo_name=self.db_repo_name)
1236 else:
1233 else:
1237 default_redirect_url = h.route_path(
1234 default_redirect_url = h.route_path(
1238 'repo_commit', repo_name=self.db_repo_name, commit_id='tip')
1235 'repo_commit', repo_name=self.db_repo_name, commit_id='tip')
1239
1236
1240 # If there's no commit, redirect to repo summary
1237 # If there's no commit, redirect to repo summary
1241 if type(c.commit) is EmptyCommit:
1238 if type(c.commit) is EmptyCommit:
1242 redirect_url = h.route_path(
1239 redirect_url = h.route_path(
1243 'repo_summary', repo_name=self.db_repo_name)
1240 'repo_summary', repo_name=self.db_repo_name)
1244 else:
1241 else:
1245 redirect_url = default_redirect_url
1242 redirect_url = default_redirect_url
1246
1243
1247 if not filename:
1244 if not filename:
1248 h.flash(_('No filename'), category='warning')
1245 h.flash(_('No filename'), category='warning')
1249 raise HTTPFound(redirect_url)
1246 raise HTTPFound(redirect_url)
1250
1247
1251 # extract the location from filename,
1248 # extract the location from filename,
1252 # allows using foo/bar.txt syntax to create subdirectories
1249 # allows using foo/bar.txt syntax to create subdirectories
1253 subdir_loc = filename.rsplit('/', 1)
1250 subdir_loc = filename.rsplit('/', 1)
1254 if len(subdir_loc) == 2:
1251 if len(subdir_loc) == 2:
1255 location = os.path.join(location, subdir_loc[0])
1252 location = os.path.join(location, subdir_loc[0])
1256
1253
1257 # strip all crap out of file, just leave the basename
1254 # strip all crap out of file, just leave the basename
1258 filename = os.path.basename(filename)
1255 filename = os.path.basename(filename)
1259 node_path = os.path.join(location, filename)
1256 node_path = os.path.join(location, filename)
1260 author = self._rhodecode_db_user.full_contact
1257 author = self._rhodecode_db_user.full_contact
1261
1258
1262 try:
1259 try:
1263 nodes = {
1260 nodes = {
1264 node_path: {
1261 node_path: {
1265 'content': content
1262 'content': content
1266 }
1263 }
1267 }
1264 }
1268 ScmModel().create_nodes(
1265 ScmModel().create_nodes(
1269 user=self._rhodecode_db_user.user_id,
1266 user=self._rhodecode_db_user.user_id,
1270 repo=self.db_repo,
1267 repo=self.db_repo,
1271 message=message,
1268 message=message,
1272 nodes=nodes,
1269 nodes=nodes,
1273 parent_commit=c.commit,
1270 parent_commit=c.commit,
1274 author=author,
1271 author=author,
1275 )
1272 )
1276
1273
1277 h.flash(
1274 h.flash(
1278 _('Successfully committed new file `{}`').format(
1275 _('Successfully committed new file `{}`').format(
1279 h.escape(node_path)), category='success')
1276 h.escape(node_path)), category='success')
1280 except NonRelativePathError:
1277 except NonRelativePathError:
1281 log.exception('Non Relative path found')
1278 log.exception('Non Relative path found')
1282 h.flash(_(
1279 h.flash(_(
1283 'The location specified must be a relative path and must not '
1280 'The location specified must be a relative path and must not '
1284 'contain .. in the path'), category='warning')
1281 'contain .. in the path'), category='warning')
1285 raise HTTPFound(default_redirect_url)
1282 raise HTTPFound(default_redirect_url)
1286 except (NodeError, NodeAlreadyExistsError) as e:
1283 except (NodeError, NodeAlreadyExistsError) as e:
1287 h.flash(_(h.escape(e)), category='error')
1284 h.flash(_(h.escape(e)), category='error')
1288 except Exception:
1285 except Exception:
1289 log.exception('Error occurred during commit')
1286 log.exception('Error occurred during commit')
1290 h.flash(_('Error occurred during commit'), category='error')
1287 h.flash(_('Error occurred during commit'), category='error')
1291
1288
1292 raise HTTPFound(default_redirect_url)
1289 raise HTTPFound(default_redirect_url)
@@ -1,247 +1,248 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2018 RhodeCode GmbH
3 # Copyright (C) 2011-2018 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22
22
23 from pyramid.view import view_config
23 from pyramid.view import view_config
24 from pyramid.httpexceptions import HTTPFound
24 from pyramid.httpexceptions import HTTPFound
25
25
26 from rhodecode.apps._base import RepoAppView
26 from rhodecode.apps._base import RepoAppView
27 from rhodecode.lib import helpers as h
27 from rhodecode.lib import helpers as h
28 from rhodecode.lib import audit_logger
28 from rhodecode.lib import audit_logger
29 from rhodecode.lib.auth import (
29 from rhodecode.lib.auth import (
30 LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired,
30 LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired,
31 HasRepoPermissionAny)
31 HasRepoPermissionAny)
32 from rhodecode.lib.exceptions import AttachedForksError
32 from rhodecode.lib.exceptions import AttachedForksError
33 from rhodecode.lib.utils2 import safe_int
33 from rhodecode.lib.utils2 import safe_int
34 from rhodecode.lib.vcs import RepositoryError
34 from rhodecode.lib.vcs import RepositoryError
35 from rhodecode.model.db import Session, UserFollowing, User, Repository
35 from rhodecode.model.db import Session, UserFollowing, User, Repository
36 from rhodecode.model.repo import RepoModel
36 from rhodecode.model.repo import RepoModel
37 from rhodecode.model.scm import ScmModel
37 from rhodecode.model.scm import ScmModel
38
38
39 log = logging.getLogger(__name__)
39 log = logging.getLogger(__name__)
40
40
41
41
42 class RepoSettingsView(RepoAppView):
42 class RepoSettingsView(RepoAppView):
43
43
44 def load_default_context(self):
44 def load_default_context(self):
45 c = self._get_local_tmpl_context()
45 c = self._get_local_tmpl_context()
46 return c
46 return c
47
47
48 @LoginRequired()
48 @LoginRequired()
49 @HasRepoPermissionAnyDecorator('repository.admin')
49 @HasRepoPermissionAnyDecorator('repository.admin')
50 @view_config(
50 @view_config(
51 route_name='edit_repo_advanced', request_method='GET',
51 route_name='edit_repo_advanced', request_method='GET',
52 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
52 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
53 def edit_advanced(self):
53 def edit_advanced(self):
54 c = self.load_default_context()
54 c = self.load_default_context()
55 c.active = 'advanced'
55 c.active = 'advanced'
56
56
57 c.default_user_id = User.get_default_user().user_id
57 c.default_user_id = User.get_default_user().user_id
58 c.in_public_journal = UserFollowing.query() \
58 c.in_public_journal = UserFollowing.query() \
59 .filter(UserFollowing.user_id == c.default_user_id) \
59 .filter(UserFollowing.user_id == c.default_user_id) \
60 .filter(UserFollowing.follows_repository == self.db_repo).scalar()
60 .filter(UserFollowing.follows_repository == self.db_repo).scalar()
61
61
62 c.has_origin_repo_read_perm = False
62 c.has_origin_repo_read_perm = False
63 if self.db_repo.fork:
63 if self.db_repo.fork:
64 c.has_origin_repo_read_perm = h.HasRepoPermissionAny(
64 c.has_origin_repo_read_perm = h.HasRepoPermissionAny(
65 'repository.write', 'repository.read', 'repository.admin')(
65 'repository.write', 'repository.read', 'repository.admin')(
66 self.db_repo.fork.repo_name, 'repo set as fork page')
66 self.db_repo.fork.repo_name, 'repo set as fork page')
67
67
68 return self._get_template_context(c)
68 return self._get_template_context(c)
69
69
70 @LoginRequired()
70 @LoginRequired()
71 @HasRepoPermissionAnyDecorator('repository.admin')
71 @HasRepoPermissionAnyDecorator('repository.admin')
72 @CSRFRequired()
72 @CSRFRequired()
73 @view_config(
73 @view_config(
74 route_name='edit_repo_advanced_delete', request_method='POST',
74 route_name='edit_repo_advanced_delete', request_method='POST',
75 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
75 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
76 def edit_advanced_delete(self):
76 def edit_advanced_delete(self):
77 """
77 """
78 Deletes the repository, or shows warnings if deletion is not possible
78 Deletes the repository, or shows warnings if deletion is not possible
79 because of attached forks or other errors.
79 because of attached forks or other errors.
80 """
80 """
81 _ = self.request.translate
81 _ = self.request.translate
82 handle_forks = self.request.POST.get('forks', None)
82 handle_forks = self.request.POST.get('forks', None)
83
83
84 try:
84 try:
85 _forks = self.db_repo.forks.count()
85 _forks = self.db_repo.forks.count()
86 if _forks and handle_forks:
86 if _forks and handle_forks:
87 if handle_forks == 'detach_forks':
87 if handle_forks == 'detach_forks':
88 handle_forks = 'detach'
88 handle_forks = 'detach'
89 h.flash(_('Detached %s forks') % _forks, category='success')
89 h.flash(_('Detached %s forks') % _forks, category='success')
90 elif handle_forks == 'delete_forks':
90 elif handle_forks == 'delete_forks':
91 handle_forks = 'delete'
91 handle_forks = 'delete'
92 h.flash(_('Deleted %s forks') % _forks, category='success')
92 h.flash(_('Deleted %s forks') % _forks, category='success')
93
93
94 old_data = self.db_repo.get_api_data()
94 old_data = self.db_repo.get_api_data()
95 RepoModel().delete(self.db_repo, forks=handle_forks)
95 RepoModel().delete(self.db_repo, forks=handle_forks)
96
96
97 repo = audit_logger.RepoWrap(repo_id=None,
97 repo = audit_logger.RepoWrap(repo_id=None,
98 repo_name=self.db_repo.repo_name)
98 repo_name=self.db_repo.repo_name)
99 audit_logger.store_web(
99 audit_logger.store_web(
100 'repo.delete', action_data={'old_data': old_data},
100 'repo.delete', action_data={'old_data': old_data},
101 user=self._rhodecode_user, repo=repo)
101 user=self._rhodecode_user, repo=repo)
102
102
103 ScmModel().mark_for_invalidation(self.db_repo_name, delete=True)
103 ScmModel().mark_for_invalidation(self.db_repo_name, delete=True)
104 h.flash(
104 h.flash(
105 _('Deleted repository `%s`') % self.db_repo_name,
105 _('Deleted repository `%s`') % self.db_repo_name,
106 category='success')
106 category='success')
107 Session().commit()
107 Session().commit()
108 except AttachedForksError:
108 except AttachedForksError:
109 repo_advanced_url = h.route_path(
109 repo_advanced_url = h.route_path(
110 'edit_repo_advanced', repo_name=self.db_repo_name,
110 'edit_repo_advanced', repo_name=self.db_repo_name,
111 _anchor='advanced-delete')
111 _anchor='advanced-delete')
112 delete_anchor = h.link_to(_('detach or delete'), repo_advanced_url)
112 delete_anchor = h.link_to(_('detach or delete'), repo_advanced_url)
113 h.flash(_('Cannot delete `{repo}` it still contains attached forks. '
113 h.flash(_('Cannot delete `{repo}` it still contains attached forks. '
114 'Try using {delete_or_detach} option.')
114 'Try using {delete_or_detach} option.')
115 .format(repo=self.db_repo_name, delete_or_detach=delete_anchor),
115 .format(repo=self.db_repo_name, delete_or_detach=delete_anchor),
116 category='warning')
116 category='warning')
117
117
118 # redirect to advanced for forks handle action ?
118 # redirect to advanced for forks handle action ?
119 raise HTTPFound(repo_advanced_url)
119 raise HTTPFound(repo_advanced_url)
120
120
121 except Exception:
121 except Exception:
122 log.exception("Exception during deletion of repository")
122 log.exception("Exception during deletion of repository")
123 h.flash(_('An error occurred during deletion of `%s`')
123 h.flash(_('An error occurred during deletion of `%s`')
124 % self.db_repo_name, category='error')
124 % self.db_repo_name, category='error')
125 # redirect to advanced for more deletion options
125 # redirect to advanced for more deletion options
126 raise HTTPFound(
126 raise HTTPFound(
127 h.route_path('edit_repo_advanced', repo_name=self.db_repo_name,
127 h.route_path('edit_repo_advanced', repo_name=self.db_repo_name,
128 _anchor='advanced-delete'))
128 _anchor='advanced-delete'))
129
129
130 raise HTTPFound(h.route_path('home'))
130 raise HTTPFound(h.route_path('home'))
131
131
132 @LoginRequired()
132 @LoginRequired()
133 @HasRepoPermissionAnyDecorator('repository.admin')
133 @HasRepoPermissionAnyDecorator('repository.admin')
134 @CSRFRequired()
134 @CSRFRequired()
135 @view_config(
135 @view_config(
136 route_name='edit_repo_advanced_journal', request_method='POST',
136 route_name='edit_repo_advanced_journal', request_method='POST',
137 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
137 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
138 def edit_advanced_journal(self):
138 def edit_advanced_journal(self):
139 """
139 """
140 Set's this repository to be visible in public journal,
140 Set's this repository to be visible in public journal,
141 in other words making default user to follow this repo
141 in other words making default user to follow this repo
142 """
142 """
143 _ = self.request.translate
143 _ = self.request.translate
144
144
145 try:
145 try:
146 user_id = User.get_default_user().user_id
146 user_id = User.get_default_user().user_id
147 ScmModel().toggle_following_repo(self.db_repo.repo_id, user_id)
147 ScmModel().toggle_following_repo(self.db_repo.repo_id, user_id)
148 h.flash(_('Updated repository visibility in public journal'),
148 h.flash(_('Updated repository visibility in public journal'),
149 category='success')
149 category='success')
150 Session().commit()
150 Session().commit()
151 except Exception:
151 except Exception:
152 h.flash(_('An error occurred during setting this '
152 h.flash(_('An error occurred during setting this '
153 'repository in public journal'),
153 'repository in public journal'),
154 category='error')
154 category='error')
155
155
156 raise HTTPFound(
156 raise HTTPFound(
157 h.route_path('edit_repo_advanced', repo_name=self.db_repo_name))
157 h.route_path('edit_repo_advanced', repo_name=self.db_repo_name))
158
158
159 @LoginRequired()
159 @LoginRequired()
160 @HasRepoPermissionAnyDecorator('repository.admin')
160 @HasRepoPermissionAnyDecorator('repository.admin')
161 @CSRFRequired()
161 @CSRFRequired()
162 @view_config(
162 @view_config(
163 route_name='edit_repo_advanced_fork', request_method='POST',
163 route_name='edit_repo_advanced_fork', request_method='POST',
164 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
164 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
165 def edit_advanced_fork(self):
165 def edit_advanced_fork(self):
166 """
166 """
167 Mark given repository as a fork of another
167 Mark given repository as a fork of another
168 """
168 """
169 _ = self.request.translate
169 _ = self.request.translate
170
170
171 new_fork_id = safe_int(self.request.POST.get('id_fork_of'))
171 new_fork_id = safe_int(self.request.POST.get('id_fork_of'))
172
172
173 # valid repo, re-check permissions
173 # valid repo, re-check permissions
174 if new_fork_id:
174 if new_fork_id:
175 repo = Repository.get(new_fork_id)
175 repo = Repository.get(new_fork_id)
176 # ensure we have at least read access to the repo we mark
176 # ensure we have at least read access to the repo we mark
177 perm_check = HasRepoPermissionAny(
177 perm_check = HasRepoPermissionAny(
178 'repository.read', 'repository.write', 'repository.admin')
178 'repository.read', 'repository.write', 'repository.admin')
179
179
180 if repo and perm_check(repo_name=repo.repo_name):
180 if repo and perm_check(repo_name=repo.repo_name):
181 new_fork_id = repo.repo_id
181 new_fork_id = repo.repo_id
182 else:
182 else:
183 new_fork_id = None
183 new_fork_id = None
184
184
185 try:
185 try:
186 repo = ScmModel().mark_as_fork(
186 repo = ScmModel().mark_as_fork(
187 self.db_repo_name, new_fork_id, self._rhodecode_user.user_id)
187 self.db_repo_name, new_fork_id, self._rhodecode_user.user_id)
188 fork = repo.fork.repo_name if repo.fork else _('Nothing')
188 fork = repo.fork.repo_name if repo.fork else _('Nothing')
189 Session().commit()
189 Session().commit()
190 h.flash(
190 h.flash(
191 _('Marked repo %s as fork of %s') % (self.db_repo_name, fork),
191 _('Marked repo %s as fork of %s') % (self.db_repo_name, fork),
192 category='success')
192 category='success')
193 except RepositoryError as e:
193 except RepositoryError as e:
194 log.exception("Repository Error occurred")
194 log.exception("Repository Error occurred")
195 h.flash(str(e), category='error')
195 h.flash(str(e), category='error')
196 except Exception:
196 except Exception:
197 log.exception("Exception while editing fork")
197 log.exception("Exception while editing fork")
198 h.flash(_('An error occurred during this operation'),
198 h.flash(_('An error occurred during this operation'),
199 category='error')
199 category='error')
200
200
201 raise HTTPFound(
201 raise HTTPFound(
202 h.route_path('edit_repo_advanced', repo_name=self.db_repo_name))
202 h.route_path('edit_repo_advanced', repo_name=self.db_repo_name))
203
203
204 @LoginRequired()
204 @LoginRequired()
205 @HasRepoPermissionAnyDecorator('repository.admin')
205 @HasRepoPermissionAnyDecorator('repository.admin')
206 @CSRFRequired()
206 @CSRFRequired()
207 @view_config(
207 @view_config(
208 route_name='edit_repo_advanced_locking', request_method='POST',
208 route_name='edit_repo_advanced_locking', request_method='POST',
209 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
209 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
210 def edit_advanced_locking(self):
210 def edit_advanced_locking(self):
211 """
211 """
212 Toggle locking of repository
212 Toggle locking of repository
213 """
213 """
214 _ = self.request.translate
214 _ = self.request.translate
215 set_lock = self.request.POST.get('set_lock')
215 set_lock = self.request.POST.get('set_lock')
216 set_unlock = self.request.POST.get('set_unlock')
216 set_unlock = self.request.POST.get('set_unlock')
217
217
218 try:
218 try:
219 if set_lock:
219 if set_lock:
220 Repository.lock(self.db_repo, self._rhodecode_user.user_id,
220 Repository.lock(self.db_repo, self._rhodecode_user.user_id,
221 lock_reason=Repository.LOCK_WEB)
221 lock_reason=Repository.LOCK_WEB)
222 h.flash(_('Locked repository'), category='success')
222 h.flash(_('Locked repository'), category='success')
223 elif set_unlock:
223 elif set_unlock:
224 Repository.unlock(self.db_repo)
224 Repository.unlock(self.db_repo)
225 h.flash(_('Unlocked repository'), category='success')
225 h.flash(_('Unlocked repository'), category='success')
226 except Exception as e:
226 except Exception as e:
227 log.exception("Exception during unlocking")
227 log.exception("Exception during unlocking")
228 h.flash(_('An error occurred during unlocking'), category='error')
228 h.flash(_('An error occurred during unlocking'), category='error')
229
229
230 raise HTTPFound(
230 raise HTTPFound(
231 h.route_path('edit_repo_advanced', repo_name=self.db_repo_name))
231 h.route_path('edit_repo_advanced', repo_name=self.db_repo_name))
232
232
233 @LoginRequired()
233 @LoginRequired()
234 @HasRepoPermissionAnyDecorator('repository.admin')
234 @HasRepoPermissionAnyDecorator('repository.admin')
235 @view_config(
235 @view_config(
236 route_name='edit_repo_advanced_hooks', request_method='GET',
236 route_name='edit_repo_advanced_hooks', request_method='GET',
237 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
237 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
238 def edit_advanced_install_hooks(self):
238 def edit_advanced_install_hooks(self):
239 """
239 """
240 Install Hooks for repository
240 Install Hooks for repository
241 """
241 """
242 _ = self.request.translate
242 _ = self.request.translate
243 self.load_default_context()
243 self.load_default_context()
244 self.rhodecode_vcs_repo.install_hooks(force=True)
244 self.rhodecode_vcs_repo.install_hooks(force=True)
245 h.flash(_('installed hooks repository'), category='success')
245 h.flash(_('installed updated hooks into this repository'),
246 category='success')
246 raise HTTPFound(
247 raise HTTPFound(
247 h.route_path('edit_repo_advanced', repo_name=self.db_repo_name))
248 h.route_path('edit_repo_advanced', repo_name=self.db_repo_name))
General Comments 0
You need to be logged in to leave comments. Login now