##// END OF EJS Templates
file: new file editors...
dan -
r3754:5cee44bd new-ui
parent child Browse files
Show More
@@ -68,6 +68,10 b' def includeme(config):'
68 pattern='/_markup_preview')
68 pattern='/_markup_preview')
69
69
70 config.add_route(
70 config.add_route(
71 name='file_preview',
72 pattern='/_file_preview')
73
74 config.add_route(
71 name='store_user_session_value',
75 name='store_user_session_value',
72 pattern='/_store_session_attr')
76 pattern='/_store_session_attr')
73
77
@@ -27,11 +27,12 b' from pyramid.view import view_config'
27 from rhodecode.apps._base import BaseAppView
27 from rhodecode.apps._base import BaseAppView
28 from rhodecode.lib import helpers as h
28 from rhodecode.lib import helpers as h
29 from rhodecode.lib.auth import (
29 from rhodecode.lib.auth import (
30 LoginRequired, NotAnonymous, HasRepoGroupPermissionAnyDecorator,
30 LoginRequired, NotAnonymous, HasRepoGroupPermissionAnyDecorator, CSRFRequired)
31 CSRFRequired)
31 from rhodecode.lib.codeblocks import filenode_as_lines_tokens
32 from rhodecode.lib.index import searcher_from_config
32 from rhodecode.lib.index import searcher_from_config
33 from rhodecode.lib.utils2 import safe_unicode, str2bool, safe_int
33 from rhodecode.lib.utils2 import safe_unicode, str2bool, safe_int
34 from rhodecode.lib.ext_json import json
34 from rhodecode.lib.ext_json import json
35 from rhodecode.lib.vcs.nodes import FileNode
35 from rhodecode.model.db import (
36 from rhodecode.model.db import (
36 func, true, or_, case, in_filter_generator, Repository, RepoGroup, User, UserGroup)
37 func, true, or_, case, in_filter_generator, Repository, RepoGroup, User, UserGroup)
37 from rhodecode.model.repo import RepoModel
38 from rhodecode.model.repo import RepoModel
@@ -743,6 +744,34 b' class HomeView(BaseAppView):'
743 @LoginRequired()
744 @LoginRequired()
744 @CSRFRequired()
745 @CSRFRequired()
745 @view_config(
746 @view_config(
747 route_name='file_preview', request_method='POST',
748 renderer='string', xhr=True)
749 def file_preview(self):
750 # Technically a CSRF token is not needed as no state changes with this
751 # call. However, as this is a POST is better to have it, so automated
752 # tools don't flag it as potential CSRF.
753 # Post is required because the payload could be bigger than the maximum
754 # allowed by GET.
755
756 text = self.request.POST.get('text')
757 file_path = self.request.POST.get('file_path')
758
759 renderer = h.renderer_from_filename(file_path)
760
761 if renderer:
762 return h.render(text, renderer=renderer, mentions=True)
763 else:
764 self.load_default_context()
765 _render = self.request.get_partial_renderer(
766 'rhodecode:templates/files/file_content.mako')
767
768 lines = filenode_as_lines_tokens(FileNode(file_path, text))
769
770 return _render('render_lines', lines)
771
772 @LoginRequired()
773 @CSRFRequired()
774 @view_config(
746 route_name='store_user_session_value', request_method='POST',
775 route_name='store_user_session_value', request_method='POST',
747 renderer='string', xhr=True)
776 renderer='string', xhr=True)
748 def store_user_session_attr(self):
777 def store_user_session_attr(self):
@@ -25,6 +25,7 b' import shutil'
25 import tempfile
25 import tempfile
26 import collections
26 import collections
27 import urllib
27 import urllib
28 import pathlib2
28
29
29 from pyramid.httpexceptions import HTTPNotFound, HTTPBadRequest, HTTPFound
30 from pyramid.httpexceptions import HTTPNotFound, HTTPBadRequest, HTTPFound
30 from pyramid.view import view_config
31 from pyramid.view import view_config
@@ -42,7 +43,7 b' from rhodecode.lib.exceptions import Non'
42 from rhodecode.lib.codeblocks import (
43 from rhodecode.lib.codeblocks import (
43 filenode_as_lines_tokens, filenode_as_annotated_lines_tokens)
44 filenode_as_lines_tokens, filenode_as_annotated_lines_tokens)
44 from rhodecode.lib.utils2 import (
45 from rhodecode.lib.utils2 import (
45 convert_line_endings, detect_mode, safe_str, str2bool, safe_int, sha1)
46 convert_line_endings, detect_mode, safe_str, str2bool, safe_int, sha1, safe_unicode)
46 from rhodecode.lib.auth import (
47 from rhodecode.lib.auth import (
47 LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired)
48 LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired)
48 from rhodecode.lib.vcs import path as vcspath
49 from rhodecode.lib.vcs import path as vcspath
@@ -87,7 +88,7 b' class RepoFilesView(RepoAppView):'
87 c.enable_downloads = self.db_repo.enable_downloads
88 c.enable_downloads = self.db_repo.enable_downloads
88 return c
89 return c
89
90
90 def _ensure_not_locked(self):
91 def _ensure_not_locked(self, commit_id='tip'):
91 _ = self.request.translate
92 _ = self.request.translate
92
93
93 repo = self.db_repo
94 repo = self.db_repo
@@ -98,21 +99,40 b' class RepoFilesView(RepoAppView):'
98 'warning')
99 'warning')
99 files_url = h.route_path(
100 files_url = h.route_path(
100 'repo_files:default_path',
101 'repo_files:default_path',
101 repo_name=self.db_repo_name, commit_id='tip')
102 repo_name=self.db_repo_name, commit_id=commit_id)
102 raise HTTPFound(files_url)
103 raise HTTPFound(files_url)
103
104
104 def check_branch_permission(self, branch_name):
105 def forbid_non_head(self, is_head, f_path, commit_id='tip', json_mode=False):
106 _ = self.request.translate
107
108 if not is_head:
109 message = _('You can only modify files with commit being a valid branch head.')
110 h.flash(message, category='warning')
111
112 if json_mode:
113 return message
114
115 files_url = h.route_path(
116 'repo_files', repo_name=self.db_repo_name, commit_id=commit_id,
117 f_path=f_path)
118 raise HTTPFound(files_url)
119
120 def check_branch_permission(self, branch_name, commit_id='tip', json_mode=False):
105 _ = self.request.translate
121 _ = self.request.translate
106
122
107 rule, branch_perm = self._rhodecode_user.get_rule_and_branch_permission(
123 rule, branch_perm = self._rhodecode_user.get_rule_and_branch_permission(
108 self.db_repo_name, branch_name)
124 self.db_repo_name, branch_name)
109 if branch_perm and branch_perm not in ['branch.push', 'branch.push_force']:
125 if branch_perm and branch_perm not in ['branch.push', 'branch.push_force']:
110 h.flash(
126 message = _('Branch `{}` changes forbidden by rule {}.').format(
111 _('Branch `{}` changes forbidden by rule {}.').format(branch_name, rule),
127 branch_name, rule)
112 'warning')
128 h.flash(message, 'warning')
129
130 if json_mode:
131 return message
132
113 files_url = h.route_path(
133 files_url = h.route_path(
114 'repo_files:default_path',
134 'repo_files:default_path', repo_name=self.db_repo_name, commit_id=commit_id)
115 repo_name=self.db_repo_name, commit_id='tip')
135
116 raise HTTPFound(files_url)
136 raise HTTPFound(files_url)
117
137
118 def _get_commit_and_path(self):
138 def _get_commit_and_path(self):
@@ -146,8 +166,7 b' class RepoFilesView(RepoAppView):'
146
166
147 _url = h.route_path(
167 _url = h.route_path(
148 'repo_files_add_file',
168 'repo_files_add_file',
149 repo_name=self.db_repo_name, commit_id=0, f_path='',
169 repo_name=self.db_repo_name, commit_id=0, f_path='')
150 _anchor='edit')
151
170
152 if h.HasRepoPermissionAny(
171 if h.HasRepoPermissionAny(
153 'repository.write', 'repository.admin')(self.db_repo_name):
172 'repository.write', 'repository.admin')(self.db_repo_name):
@@ -217,7 +236,7 b' class RepoFilesView(RepoAppView):'
217 break
236 break
218
237
219 # checked branches, means we only need to try to get the branch/commit_sha
238 # checked branches, means we only need to try to get the branch/commit_sha
220 if not repo.is_empty:
239 if not repo.is_empty():
221 commit = repo.get_commit(commit_id=commit_id)
240 commit = repo.get_commit(commit_id=commit_id)
222 if commit:
241 if commit:
223 branch_name = commit.branch
242 branch_name = commit.branch
@@ -276,6 +295,15 b' class RepoFilesView(RepoAppView):'
276
295
277 return commit_id, ext, fileformat, content_type
296 return commit_id, ext, fileformat, content_type
278
297
298 def create_pure_path(self, *parts):
299 # Split paths and sanitize them, removing any ../ etc
300 sanitized_path = [
301 x for x in pathlib2.PurePath(*parts).parts
302 if x not in ['.', '..']]
303
304 pure_path = pathlib2.PurePath(*sanitized_path)
305 return pure_path
306
279 @LoginRequired()
307 @LoginRequired()
280 @HasRepoPermissionAnyDecorator(
308 @HasRepoPermissionAnyDecorator(
281 'repository.read', 'repository.write', 'repository.admin')
309 'repository.read', 'repository.write', 'repository.admin')
@@ -1054,15 +1082,9 b' class RepoFilesView(RepoAppView):'
1054 _branch_name, _sha_commit_id, is_head = \
1082 _branch_name, _sha_commit_id, is_head = \
1055 self._is_valid_head(commit_id, self.rhodecode_vcs_repo)
1083 self._is_valid_head(commit_id, self.rhodecode_vcs_repo)
1056
1084
1057 if not is_head:
1085 self.forbid_non_head(is_head, f_path)
1058 h.flash(_('You can only delete files with commit '
1086 self.check_branch_permission(_branch_name)
1059 'being a valid branch head.'), category='warning')
1060 raise HTTPFound(
1061 h.route_path('repo_files',
1062 repo_name=self.db_repo_name, commit_id='tip',
1063 f_path=f_path))
1064
1087
1065 self.check_branch_permission(_branch_name)
1066 c.commit = self._get_commit_or_redirect(commit_id)
1088 c.commit = self._get_commit_or_redirect(commit_id)
1067 c.file = self._get_filenode_or_redirect(c.commit, f_path)
1089 c.file = self._get_filenode_or_redirect(c.commit, f_path)
1068
1090
@@ -1088,13 +1110,7 b' class RepoFilesView(RepoAppView):'
1088 _branch_name, _sha_commit_id, is_head = \
1110 _branch_name, _sha_commit_id, is_head = \
1089 self._is_valid_head(commit_id, self.rhodecode_vcs_repo)
1111 self._is_valid_head(commit_id, self.rhodecode_vcs_repo)
1090
1112
1091 if not is_head:
1113 self.forbid_non_head(is_head, f_path)
1092 h.flash(_('You can only delete files with commit '
1093 'being a valid branch head.'), category='warning')
1094 raise HTTPFound(
1095 h.route_path('repo_files',
1096 repo_name=self.db_repo_name, commit_id='tip',
1097 f_path=f_path))
1098 self.check_branch_permission(_branch_name)
1114 self.check_branch_permission(_branch_name)
1099
1115
1100 c.commit = self._get_commit_or_redirect(commit_id)
1116 c.commit = self._get_commit_or_redirect(commit_id)
@@ -1144,14 +1160,8 b' class RepoFilesView(RepoAppView):'
1144 _branch_name, _sha_commit_id, is_head = \
1160 _branch_name, _sha_commit_id, is_head = \
1145 self._is_valid_head(commit_id, self.rhodecode_vcs_repo)
1161 self._is_valid_head(commit_id, self.rhodecode_vcs_repo)
1146
1162
1147 if not is_head:
1163 self.forbid_non_head(is_head, f_path, commit_id=commit_id)
1148 h.flash(_('You can only edit files with commit '
1164 self.check_branch_permission(_branch_name, commit_id=commit_id)
1149 'being a valid branch head.'), category='warning')
1150 raise HTTPFound(
1151 h.route_path('repo_files',
1152 repo_name=self.db_repo_name, commit_id='tip',
1153 f_path=f_path))
1154 self.check_branch_permission(_branch_name)
1155
1165
1156 c.commit = self._get_commit_or_redirect(commit_id)
1166 c.commit = self._get_commit_or_redirect(commit_id)
1157 c.file = self._get_filenode_or_redirect(c.commit, f_path)
1167 c.file = self._get_filenode_or_redirect(c.commit, f_path)
@@ -1163,8 +1173,7 b' class RepoFilesView(RepoAppView):'
1163 commit_id=c.commit.raw_id, f_path=f_path)
1173 commit_id=c.commit.raw_id, f_path=f_path)
1164 raise HTTPFound(files_url)
1174 raise HTTPFound(files_url)
1165
1175
1166 c.default_message = _(
1176 c.default_message = _('Edited file {} via RhodeCode Enterprise').format(f_path)
1167 'Edited file {} via RhodeCode Enterprise').format(f_path)
1168 c.f_path = f_path
1177 c.f_path = f_path
1169
1178
1170 return self._get_template_context(c)
1179 return self._get_template_context(c)
@@ -1181,32 +1190,23 b' class RepoFilesView(RepoAppView):'
1181 commit_id, f_path = self._get_commit_and_path()
1190 commit_id, f_path = self._get_commit_and_path()
1182
1191
1183 self._ensure_not_locked()
1192 self._ensure_not_locked()
1184 _branch_name, _sha_commit_id, is_head = \
1185 self._is_valid_head(commit_id, self.rhodecode_vcs_repo)
1186
1187 if not is_head:
1188 h.flash(_('You can only edit files with commit '
1189 'being a valid branch head.'), category='warning')
1190 raise HTTPFound(
1191 h.route_path('repo_files',
1192 repo_name=self.db_repo_name, commit_id='tip',
1193 f_path=f_path))
1194
1195 self.check_branch_permission(_branch_name)
1196
1193
1197 c.commit = self._get_commit_or_redirect(commit_id)
1194 c.commit = self._get_commit_or_redirect(commit_id)
1198 c.file = self._get_filenode_or_redirect(c.commit, f_path)
1195 c.file = self._get_filenode_or_redirect(c.commit, f_path)
1199
1196
1200 if c.file.is_binary:
1197 if c.file.is_binary:
1201 raise HTTPFound(
1198 raise HTTPFound(h.route_path('repo_files', repo_name=self.db_repo_name,
1202 h.route_path('repo_files',
1199 commit_id=c.commit.raw_id, f_path=f_path))
1203 repo_name=self.db_repo_name,
1200
1204 commit_id=c.commit.raw_id,
1201 _branch_name, _sha_commit_id, is_head = \
1205 f_path=f_path))
1202 self._is_valid_head(commit_id, self.rhodecode_vcs_repo)
1206
1203
1207 c.default_message = _(
1204 self.forbid_non_head(is_head, f_path, commit_id=commit_id)
1208 'Edited file {} via RhodeCode Enterprise').format(f_path)
1205 self.check_branch_permission(_branch_name, commit_id=commit_id)
1206
1207 c.default_message = _('Edited file {} via RhodeCode Enterprise').format(f_path)
1209 c.f_path = f_path
1208 c.f_path = f_path
1209
1210 old_content = c.file.content
1210 old_content = c.file.content
1211 sl = old_content.splitlines(1)
1211 sl = old_content.splitlines(1)
1212 first_line = sl[0] if sl else ''
1212 first_line = sl[0] if sl else ''
@@ -1217,20 +1217,25 b' class RepoFilesView(RepoAppView):'
1217 content = convert_line_endings(r_post.get('content', ''), line_ending_mode)
1217 content = convert_line_endings(r_post.get('content', ''), line_ending_mode)
1218
1218
1219 message = r_post.get('message') or c.default_message
1219 message = r_post.get('message') or c.default_message
1220 org_f_path = c.file.unicode_path
1220 org_node_path = c.file.unicode_path
1221 filename = r_post['filename']
1221 filename = r_post['filename']
1222 org_filename = c.file.name
1222
1223 root_path = c.file.dir_path
1224 pure_path = self.create_pure_path(root_path, filename)
1225 node_path = safe_unicode(bytes(pure_path))
1223
1226
1224 if content == old_content and filename == org_filename:
1227 default_redirect_url = h.route_path('repo_commit', repo_name=self.db_repo_name,
1225 h.flash(_('No changes'), category='warning')
1228 commit_id=commit_id)
1226 raise HTTPFound(
1229 if content == old_content and node_path == org_node_path:
1227 h.route_path('repo_commit', repo_name=self.db_repo_name,
1230 h.flash(_('No changes detected on {}').format(org_node_path),
1228 commit_id='tip'))
1231 category='warning')
1232 raise HTTPFound(default_redirect_url)
1233
1229 try:
1234 try:
1230 mapping = {
1235 mapping = {
1231 org_f_path: {
1236 org_node_path: {
1232 'org_filename': org_f_path,
1237 'org_filename': org_node_path,
1233 'filename': os.path.join(c.file.dir_path, filename),
1238 'filename': node_path,
1234 'content': content,
1239 'content': content,
1235 'lexer': '',
1240 'lexer': '',
1236 'op': 'mod',
1241 'op': 'mod',
@@ -1238,7 +1243,7 b' class RepoFilesView(RepoAppView):'
1238 }
1243 }
1239 }
1244 }
1240
1245
1241 ScmModel().update_nodes(
1246 commit = ScmModel().update_nodes(
1242 user=self._rhodecode_db_user.user_id,
1247 user=self._rhodecode_db_user.user_id,
1243 repo=self.db_repo,
1248 repo=self.db_repo,
1244 message=message,
1249 message=message,
@@ -1246,15 +1251,16 b' class RepoFilesView(RepoAppView):'
1246 parent_commit=c.commit,
1251 parent_commit=c.commit,
1247 )
1252 )
1248
1253
1249 h.flash(
1254 h.flash(_('Successfully committed changes to file `{}`').format(
1250 _('Successfully committed changes to file `{}`').format(
1251 h.escape(f_path)), category='success')
1255 h.escape(f_path)), category='success')
1256 default_redirect_url = h.route_path(
1257 'repo_commit', repo_name=self.db_repo_name, commit_id=commit.raw_id)
1258
1252 except Exception:
1259 except Exception:
1253 log.exception('Error occurred during commit')
1260 log.exception('Error occurred during commit')
1254 h.flash(_('Error occurred during commit'), category='error')
1261 h.flash(_('Error occurred during commit'), category='error')
1255 raise HTTPFound(
1262
1256 h.route_path('repo_commit', repo_name=self.db_repo_name,
1263 raise HTTPFound(default_redirect_url)
1257 commit_id='tip'))
1258
1264
1259 @LoginRequired()
1265 @LoginRequired()
1260 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1266 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
@@ -1274,10 +1280,8 b' class RepoFilesView(RepoAppView):'
1274 c.commit = self._get_commit_or_redirect(commit_id, redirect_after=False)
1280 c.commit = self._get_commit_or_redirect(commit_id, redirect_after=False)
1275 if c.commit is None:
1281 if c.commit is None:
1276 c.commit = EmptyCommit(alias=self.rhodecode_vcs_repo.alias)
1282 c.commit = EmptyCommit(alias=self.rhodecode_vcs_repo.alias)
1277 c.default_message = (_('Added file via RhodeCode Enterprise'))
1278 c.f_path = f_path.lstrip('/') # ensure not relative path
1279
1283
1280 if self.rhodecode_vcs_repo.is_empty:
1284 if self.rhodecode_vcs_repo.is_empty():
1281 # for empty repository we cannot check for current branch, we rely on
1285 # for empty repository we cannot check for current branch, we rely on
1282 # c.commit.branch instead
1286 # c.commit.branch instead
1283 _branch_name = c.commit.branch
1287 _branch_name = c.commit.branch
@@ -1286,15 +1290,11 b' class RepoFilesView(RepoAppView):'
1286 _branch_name, _sha_commit_id, is_head = \
1290 _branch_name, _sha_commit_id, is_head = \
1287 self._is_valid_head(commit_id, self.rhodecode_vcs_repo)
1291 self._is_valid_head(commit_id, self.rhodecode_vcs_repo)
1288
1292
1289 if not is_head:
1293 self.forbid_non_head(is_head, f_path, commit_id=commit_id)
1290 h.flash(_('You can only add files with commit '
1294 self.check_branch_permission(_branch_name, commit_id=commit_id)
1291 'being a valid branch head.'), category='warning')
1292 raise HTTPFound(
1293 h.route_path('repo_files',
1294 repo_name=self.db_repo_name, commit_id='tip',
1295 f_path=f_path))
1296
1295
1297 self.check_branch_permission(_branch_name)
1296 c.default_message = (_('Added file via RhodeCode Enterprise'))
1297 c.f_path = f_path.lstrip('/') # ensure not relative path
1298
1298
1299 return self._get_template_context(c)
1299 return self._get_template_context(c)
1300
1300
@@ -1311,14 +1311,19 b' class RepoFilesView(RepoAppView):'
1311
1311
1312 self._ensure_not_locked()
1312 self._ensure_not_locked()
1313
1313
1314 r_post = self.request.POST
1314 c.commit = self._get_commit_or_redirect(commit_id, redirect_after=False)
1315
1316 c.commit = self._get_commit_or_redirect(
1317 commit_id, redirect_after=False)
1318 if c.commit is None:
1315 if c.commit is None:
1319 c.commit = EmptyCommit(alias=self.rhodecode_vcs_repo.alias)
1316 c.commit = EmptyCommit(alias=self.rhodecode_vcs_repo.alias)
1320
1317
1321 if self.rhodecode_vcs_repo.is_empty:
1318 # calculate redirect URL
1319 if self.rhodecode_vcs_repo.is_empty():
1320 default_redirect_url = h.route_path(
1321 'repo_summary', repo_name=self.db_repo_name)
1322 else:
1323 default_redirect_url = h.route_path(
1324 'repo_commit', repo_name=self.db_repo_name, commit_id='tip')
1325
1326 if self.rhodecode_vcs_repo.is_empty():
1322 # for empty repository we cannot check for current branch, we rely on
1327 # for empty repository we cannot check for current branch, we rely on
1323 # c.commit.branch instead
1328 # c.commit.branch instead
1324 _branch_name = c.commit.branch
1329 _branch_name = c.commit.branch
@@ -1327,70 +1332,42 b' class RepoFilesView(RepoAppView):'
1327 _branch_name, _sha_commit_id, is_head = \
1332 _branch_name, _sha_commit_id, is_head = \
1328 self._is_valid_head(commit_id, self.rhodecode_vcs_repo)
1333 self._is_valid_head(commit_id, self.rhodecode_vcs_repo)
1329
1334
1330 if not is_head:
1335 self.forbid_non_head(is_head, f_path, commit_id=commit_id)
1331 h.flash(_('You can only add files with commit '
1336 self.check_branch_permission(_branch_name, commit_id=commit_id)
1332 'being a valid branch head.'), category='warning')
1333 raise HTTPFound(
1334 h.route_path('repo_files',
1335 repo_name=self.db_repo_name, commit_id='tip',
1336 f_path=f_path))
1337
1338 self.check_branch_permission(_branch_name)
1339
1337
1340 c.default_message = (_('Added file via RhodeCode Enterprise'))
1338 c.default_message = (_('Added file via RhodeCode Enterprise'))
1341 c.f_path = f_path
1339 c.f_path = f_path
1340
1341 r_post = self.request.POST
1342 message = r_post.get('message') or c.default_message
1343 filename = r_post.get('filename')
1342 unix_mode = 0
1344 unix_mode = 0
1343 content = convert_line_endings(r_post.get('content', ''), unix_mode)
1345 content = convert_line_endings(r_post.get('content', ''), unix_mode)
1344
1346
1345 message = r_post.get('message') or c.default_message
1347 if not filename:
1346 filename = r_post.get('filename')
1347 location = r_post.get('location', '') # dir location
1348 file_obj = r_post.get('upload_file', None)
1349
1350 if file_obj is not None and hasattr(file_obj, 'filename'):
1351 filename = r_post.get('filename_upload')
1352 content = file_obj.file
1353
1354 if hasattr(content, 'file'):
1355 # non posix systems store real file under file attr
1356 content = content.file
1357
1358 if self.rhodecode_vcs_repo.is_empty:
1359 default_redirect_url = h.route_path(
1360 'repo_summary', repo_name=self.db_repo_name)
1361 else:
1362 default_redirect_url = h.route_path(
1363 'repo_commit', repo_name=self.db_repo_name, commit_id='tip')
1364
1365 # If there's no commit, redirect to repo summary
1348 # If there's no commit, redirect to repo summary
1366 if type(c.commit) is EmptyCommit:
1349 if type(c.commit) is EmptyCommit:
1367 redirect_url = h.route_path(
1350 redirect_url = h.route_path(
1368 'repo_summary', repo_name=self.db_repo_name)
1351 'repo_summary', repo_name=self.db_repo_name)
1369 else:
1352 else:
1370 redirect_url = default_redirect_url
1353 redirect_url = default_redirect_url
1371
1354 h.flash(_('No filename specified'), category='warning')
1372 if not filename:
1373 h.flash(_('No filename'), category='warning')
1374 raise HTTPFound(redirect_url)
1355 raise HTTPFound(redirect_url)
1375
1356
1376 # extract the location from filename,
1357 root_path = f_path
1377 # allows using foo/bar.txt syntax to create subdirectories
1358 pure_path = self.create_pure_path(root_path, filename)
1378 subdir_loc = filename.rsplit('/', 1)
1359 node_path = safe_unicode(bytes(pure_path).lstrip('/'))
1379 if len(subdir_loc) == 2:
1380 location = os.path.join(location, subdir_loc[0])
1381
1360
1382 # strip all crap out of file, just leave the basename
1383 filename = os.path.basename(filename)
1384 node_path = os.path.join(location, filename)
1385 author = self._rhodecode_db_user.full_contact
1361 author = self._rhodecode_db_user.full_contact
1386
1387 try:
1388 nodes = {
1362 nodes = {
1389 node_path: {
1363 node_path: {
1390 'content': content
1364 'content': content
1391 }
1365 }
1392 }
1366 }
1393 ScmModel().create_nodes(
1367
1368 try:
1369
1370 commit = ScmModel().create_nodes(
1394 user=self._rhodecode_db_user.user_id,
1371 user=self._rhodecode_db_user.user_id,
1395 repo=self.db_repo,
1372 repo=self.db_repo,
1396 message=message,
1373 message=message,
@@ -1399,13 +1376,15 b' class RepoFilesView(RepoAppView):'
1399 author=author,
1376 author=author,
1400 )
1377 )
1401
1378
1402 h.flash(
1379 h.flash(_('Successfully committed new file `{}`').format(
1403 _('Successfully committed new file `{}`').format(
1404 h.escape(node_path)), category='success')
1380 h.escape(node_path)), category='success')
1381
1382 default_redirect_url = h.route_path(
1383 'repo_commit', repo_name=self.db_repo_name, commit_id=commit.raw_id)
1384
1405 except NonRelativePathError:
1385 except NonRelativePathError:
1406 log.exception('Non Relative path found')
1386 log.exception('Non Relative path found')
1407 h.flash(_(
1387 h.flash(_('The location specified must be a relative path and must not '
1408 'The location specified must be a relative path and must not '
1409 'contain .. in the path'), category='warning')
1388 'contain .. in the path'), category='warning')
1410 raise HTTPFound(default_redirect_url)
1389 raise HTTPFound(default_redirect_url)
1411 except (NodeError, NodeAlreadyExistsError) as e:
1390 except (NodeError, NodeAlreadyExistsError) as e:
@@ -1415,3 +1394,135 b' class RepoFilesView(RepoAppView):'
1415 h.flash(_('Error occurred during commit'), category='error')
1394 h.flash(_('Error occurred during commit'), category='error')
1416
1395
1417 raise HTTPFound(default_redirect_url)
1396 raise HTTPFound(default_redirect_url)
1397
1398 @LoginRequired()
1399 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1400 @CSRFRequired()
1401 @view_config(
1402 route_name='repo_files_upload_file', request_method='POST',
1403 renderer='json_ext')
1404 def repo_files_upload_file(self):
1405 _ = self.request.translate
1406 c = self.load_default_context()
1407 commit_id, f_path = self._get_commit_and_path()
1408
1409 self._ensure_not_locked()
1410
1411 c.commit = self._get_commit_or_redirect(commit_id, redirect_after=False)
1412 if c.commit is None:
1413 c.commit = EmptyCommit(alias=self.rhodecode_vcs_repo.alias)
1414
1415 # calculate redirect URL
1416 if self.rhodecode_vcs_repo.is_empty():
1417 default_redirect_url = h.route_path(
1418 'repo_summary', repo_name=self.db_repo_name)
1419 else:
1420 default_redirect_url = h.route_path(
1421 'repo_commit', repo_name=self.db_repo_name, commit_id='tip')
1422
1423 if self.rhodecode_vcs_repo.is_empty():
1424 # for empty repository we cannot check for current branch, we rely on
1425 # c.commit.branch instead
1426 _branch_name = c.commit.branch
1427 is_head = True
1428 else:
1429 _branch_name, _sha_commit_id, is_head = \
1430 self._is_valid_head(commit_id, self.rhodecode_vcs_repo)
1431
1432 error = self.forbid_non_head(is_head, f_path, json_mode=True)
1433 if error:
1434 return {
1435 'error': error,
1436 'redirect_url': default_redirect_url
1437 }
1438 error = self.check_branch_permission(_branch_name, json_mode=True)
1439 if error:
1440 return {
1441 'error': error,
1442 'redirect_url': default_redirect_url
1443 }
1444
1445 c.default_message = (_('Uploaded file via RhodeCode Enterprise'))
1446 c.f_path = f_path
1447
1448 r_post = self.request.POST
1449
1450 message = c.default_message
1451 user_message = r_post.getall('message')
1452 if isinstance(user_message, list) and user_message:
1453 # we take the first from duplicated results if it's not empty
1454 message = user_message[0] if user_message[0] else message
1455
1456 nodes = {}
1457
1458 for file_obj in r_post.getall('files_upload') or []:
1459 content = file_obj.file
1460 filename = file_obj.filename
1461
1462 root_path = f_path
1463 pure_path = self.create_pure_path(root_path, filename)
1464 node_path = safe_unicode(bytes(pure_path).lstrip('/'))
1465
1466 nodes[node_path] = {
1467 'content': content
1468 }
1469
1470 if not nodes:
1471 error = 'missing files'
1472 return {
1473 'error': error,
1474 'redirect_url': default_redirect_url
1475 }
1476
1477 author = self._rhodecode_db_user.full_contact
1478
1479 try:
1480 commit = ScmModel().create_nodes(
1481 user=self._rhodecode_db_user.user_id,
1482 repo=self.db_repo,
1483 message=message,
1484 nodes=nodes,
1485 parent_commit=c.commit,
1486 author=author,
1487 )
1488 if len(nodes) == 1:
1489 flash_message = _('Successfully committed {} new files').format(len(nodes))
1490 else:
1491 flash_message = _('Successfully committed 1 new file')
1492
1493 h.flash(flash_message, category='success')
1494
1495 default_redirect_url = h.route_path(
1496 'repo_commit', repo_name=self.db_repo_name, commit_id=commit.raw_id)
1497
1498 except NonRelativePathError:
1499 log.exception('Non Relative path found')
1500 error = _('The location specified must be a relative path and must not '
1501 'contain .. in the path')
1502 h.flash(error, category='warning')
1503
1504 return {
1505 'error': error,
1506 'redirect_url': default_redirect_url
1507 }
1508 except (NodeError, NodeAlreadyExistsError) as e:
1509 error = h.escape(e)
1510 h.flash(error, category='error')
1511
1512 return {
1513 'error': error,
1514 'redirect_url': default_redirect_url
1515 }
1516 except Exception:
1517 log.exception('Error occurred during commit')
1518 error = _('Error occurred during commit')
1519 h.flash(error, category='error')
1520 return {
1521 'error': error,
1522 'redirect_url': default_redirect_url
1523 }
1524
1525 return {
1526 'error': None,
1527 'redirect_url': default_redirect_url
1528 }
@@ -1939,23 +1939,26 b' def secure_form(form_url, method="POST",'
1939
1939
1940 def dropdownmenu(name, selected, options, enable_filter=False, **attrs):
1940 def dropdownmenu(name, selected, options, enable_filter=False, **attrs):
1941 select_html = select(name, selected, options, **attrs)
1941 select_html = select(name, selected, options, **attrs)
1942
1942 select2 = """
1943 select2 = """
1943 <script>
1944 <script>
1944 $(document).ready(function() {
1945 $(document).ready(function() {
1945 $('#%s').select2({
1946 $('#%s').select2({
1946 containerCssClass: 'drop-menu',
1947 containerCssClass: 'drop-menu %s',
1947 dropdownCssClass: 'drop-menu-dropdown',
1948 dropdownCssClass: 'drop-menu-dropdown',
1948 dropdownAutoWidth: true%s
1949 dropdownAutoWidth: true%s
1949 });
1950 });
1950 });
1951 });
1951 </script>
1952 </script>
1952 """
1953 """
1954
1953 filter_option = """,
1955 filter_option = """,
1954 minimumResultsForSearch: -1
1956 minimumResultsForSearch: -1
1955 """
1957 """
1956 input_id = attrs.get('id') or name
1958 input_id = attrs.get('id') or name
1959 extra_classes = ' '.join(attrs.pop('extra_classes', []))
1957 filter_enabled = "" if enable_filter else filter_option
1960 filter_enabled = "" if enable_filter else filter_option
1958 select_script = literal(select2 % (input_id, filter_enabled))
1961 select_script = literal(select2 % (input_id, extra_classes, filter_enabled))
1959
1962
1960 return literal(select_html+select_script)
1963 return literal(select_html+select_script)
1961
1964
@@ -818,6 +818,8 b' class ScmModel(BaseModel):'
818 repo_name=repo.repo_name, repo_alias=scm_instance.alias,
818 repo_name=repo.repo_name, repo_alias=scm_instance.alias,
819 commit_ids=[tip.raw_id])
819 commit_ids=[tip.raw_id])
820
820
821 return tip
822
821 def delete_nodes(self, user, repo, message, nodes, parent_commit=None,
823 def delete_nodes(self, user, repo, message, nodes, parent_commit=None,
822 author=None, trigger_push_hook=True):
824 author=None, trigger_push_hook=True):
823 """
825 """
@@ -27,7 +27,7 b''
27
27
28 .CodeMirror-gutters {
28 .CodeMirror-gutters {
29 border-right: 1px solid #ddd;
29 border-right: 1px solid #ddd;
30 background-color: @grey6;
30 background-color: white;
31 white-space: nowrap;
31 white-space: nowrap;
32 }
32 }
33 .CodeMirror-linenumbers {}
33 .CodeMirror-linenumbers {}
@@ -231,6 +231,11 b' form.rcform {'
231
231
232 .drop-menu {
232 .drop-menu {
233 float: left;
233 float: left;
234
235 & + .last-item {
236 margin: 0;
237 }
238
234 margin: 0 @input-padding 0 0;
239 margin: 0 @input-padding 0 0;
235 }
240 }
236
241
@@ -277,7 +277,9 b' input.inline[type="file"] {'
277 // Gists
277 // Gists
278 #files_data {
278 #files_data {
279 clear: both; //for firefox
279 clear: both; //for firefox
280 }
280 padding-top: 10px;
281 }
282
281 #gistid {
283 #gistid {
282 margin-right: @padding;
284 margin-right: @padding;
283 }
285 }
@@ -1329,7 +1331,7 b' table.integrations {'
1329
1331
1330 #editor_container{
1332 #editor_container {
1331 position: relative;
1333 position: relative;
1332 margin: @padding;
1334 margin: @padding 10px;
1333 }
1335 }
1334 }
1336 }
1335
1337
@@ -2063,15 +2065,15 b' BIN_FILENODE = 7'
2063
2065
2064 // Files
2066 // Files
2065 .edit-file-title {
2067 .edit-file-title {
2066 border-bottom: @border-thickness solid @border-default-color;
2068 font-size: 16px;
2067
2069
2068 .breadcrumbs {
2070 .title-heading {
2069 margin-bottom: 0;
2071 padding: 2px;
2070 }
2072 }
2071 }
2073 }
2072
2074
2073 .edit-file-fieldset {
2075 .edit-file-fieldset {
2074 margin-top: @sidebarpadding;
2076 margin: @sidebarpadding 0;
2075
2077
2076 .fieldset {
2078 .fieldset {
2077 .left-label {
2079 .left-label {
@@ -2120,6 +2122,27 b' BIN_FILENODE = 7'
2120 margin: 0 0 0 10px;
2122 margin: 0 0 0 10px;
2121 }
2123 }
2122
2124
2125 .file-upload-transaction-wrapper {
2126 margin-top: 57px;
2127 clear: both;
2128 }
2129
2130 .file-upload-transaction-wrapper .error {
2131 color: @color5;
2132 }
2133
2134 .file-upload-transaction {
2135 min-height: 200px;
2136 padding: 54px;
2137 border: 1px solid @grey5;
2138 text-align: center;
2139 clear: both;
2140 }
2141
2142 .file-upload-transaction i {
2143 font-size: 48px
2144 }
2145
2123 h3.files_location{
2146 h3.files_location{
2124 line-height: 2.4em;
2147 line-height: 2.4em;
2125 }
2148 }
@@ -2308,6 +2331,101 b' h3.files_location{'
2308 }
2331 }
2309
2332
2310
2333
2334 .edit-file-fieldset #location,
2335 .edit-file-fieldset #filename {
2336 display: flex;
2337 width: -moz-available; /* WebKit-based browsers will ignore this. */
2338 width: -webkit-fill-available; /* Mozilla-based browsers will ignore this. */
2339 width: fill-available;
2340 border: 0;
2341 }
2342
2343 .path-items {
2344 display: flex;
2345 padding: 0;
2346 border: 1px solid #eeeeee;
2347 width: 100%;
2348 float: left;
2349
2350 .breadcrumb-path {
2351 line-height: 30px;
2352 padding: 0 4px;
2353 white-space: nowrap;
2354 }
2355
2356 .location-path {
2357 width: -moz-available; /* WebKit-based browsers will ignore this. */
2358 width: -webkit-fill-available; /* Mozilla-based browsers will ignore this. */
2359 width: fill-available;
2360
2361 .file-name-input {
2362 padding: 0.5em 0;
2363 }
2364
2365 }
2366
2367 ul {
2368 display: flex;
2369 margin: 0;
2370 padding: 0;
2371 }
2372 li {
2373 list-style-type: none;
2374 }
2375 }
2376
2377 .editor-items {
2378 height: 40px;
2379 margin: 10px 0 -17px 10px;
2380
2381 .editor-action {
2382 cursor: pointer;
2383 }
2384
2385 .editor-action.active {
2386 border-bottom: 2px solid #5C5C5C;
2387 }
2388
2389 li {
2390 list-style-type: none;
2391 }
2392 }
2393
2394 .edit-file-fieldset .message textarea {
2395 border: 1px solid #eeeeee;
2396 }
2397
2398 #files_data .codeblock {
2399 background-color: #F5F5F5;
2400 }
2401
2402 #editor_preview {
2403 background: white;
2404 }
2405
2406 .show-editor {
2407 padding: 10px;
2408 background-color: white;
2409
2410 }
2411
2412 .show-preview {
2413 padding: 10px;
2414 background-color: white;
2415 border-left: 1px solid #eeeeee;
2416 }
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2311 // Search
2429 // Search
2312
2430
2313 .search-form{
2431 .search-form{
@@ -2577,27 +2695,28 b' form.markup-form {'
2577 padding: 20px;
2695 padding: 20px;
2578 }
2696 }
2579
2697
2580 .dropzone {
2698 .dropzone,
2699 .dropzone-pure {
2581 border: 2px dashed @grey5;
2700 border: 2px dashed @grey5;
2582 border-radius: 5px;
2701 border-radius: 5px;
2583 background: white;
2702 background: white;
2584 min-height: 200px;
2703 min-height: 200px;
2585 padding: 54px;
2704 padding: 54px;
2586 }
2705
2587 .dropzone .dz-message {
2706 .dz-message {
2588 font-weight: 700;
2707 font-weight: 700;
2589 }
2590
2591 .dropzone .dz-message {
2592 text-align: center;
2708 text-align: center;
2593 margin: 2em 0;
2709 margin: 2em 0;
2594 }
2710 }
2595
2711
2712 }
2713
2596 .dz-preview {
2714 .dz-preview {
2597 margin: 10px 0px !important;
2715 margin: 10px 0 !important;
2598 position: relative;
2716 position: relative;
2599 vertical-align: top;
2717 vertical-align: top;
2600 padding: 10px;
2718 padding: 10px;
2719 border-bottom: 1px solid @grey5;
2601 }
2720 }
2602
2721
2603 .dz-filename {
2722 .dz-filename {
@@ -2605,6 +2724,10 b' form.markup-form {'
2605 float:left;
2724 float:left;
2606 }
2725 }
2607
2726
2727 .dz-sending {
2728 float: right;
2729 }
2730
2608 .dz-response {
2731 .dz-response {
2609 clear:both
2732 clear:both
2610 }
2733 }
@@ -2615,4 +2738,6 b' form.markup-form {'
2615
2738
2616 .dz-error-message {
2739 .dz-error-message {
2617 color: @alert2;
2740 color: @alert2;
2618 } No newline at end of file
2741 padding-top: 10px;
2742 clear: both;
2743 }
@@ -149,6 +149,7 b' function registerRCRoutes() {'
149 pyroutes.register('repo_group_list_data', '/_repo_groups', []);
149 pyroutes.register('repo_group_list_data', '/_repo_groups', []);
150 pyroutes.register('goto_switcher_data', '/_goto_data', []);
150 pyroutes.register('goto_switcher_data', '/_goto_data', []);
151 pyroutes.register('markup_preview', '/_markup_preview', []);
151 pyroutes.register('markup_preview', '/_markup_preview', []);
152 pyroutes.register('file_preview', '/_file_preview', []);
152 pyroutes.register('store_user_session_value', '/_store_session_attr', []);
153 pyroutes.register('store_user_session_value', '/_store_session_attr', []);
153 pyroutes.register('journal', '/_admin/journal', []);
154 pyroutes.register('journal', '/_admin/journal', []);
154 pyroutes.register('journal_rss', '/_admin/journal/rss', []);
155 pyroutes.register('journal_rss', '/_admin/journal/rss', []);
@@ -204,7 +204,12 b' var CodeMirrorCompleteAfter = function(c'
204 };
204 };
205
205
206 var initCodeMirror = function(textAreadId, resetUrl, focus, options) {
206 var initCodeMirror = function(textAreadId, resetUrl, focus, options) {
207 if (textAreadId.substr(0,1) === "#"){
208 var ta = $(textAreadId).get(0);
209 }else {
207 var ta = $('#' + textAreadId).get(0);
210 var ta = $('#' + textAreadId).get(0);
211 }
212
208 if (focus === undefined) {
213 if (focus === undefined) {
209 focus = true;
214 focus = true;
210 }
215 }
@@ -644,18 +649,6 b' var fillCodeMirrorOptions = function(tar'
644 }
649 }
645 };
650 };
646
651
647 var CodeMirrorPreviewEnable = function(edit_mode) {
648 // in case it a preview enabled mode enable the button
649 if (['markdown', 'rst', 'gfm'].indexOf(edit_mode) !== -1) {
650 $('#render_preview').removeClass('hidden');
651 }
652 else {
653 if (!$('#render_preview').hasClass('hidden')) {
654 $('#render_preview').addClass('hidden');
655 }
656 }
657 };
658
659
652
660 /* markup form */
653 /* markup form */
661 (function(mod) {
654 (function(mod) {
@@ -409,3 +409,109 b' var showAuthors = function(elem, annotat'
409 $('#file_authors_title').html(_gettext('All Authors'))
409 $('#file_authors_title').html(_gettext('All Authors'))
410 })
410 })
411 };
411 };
412
413
414 (function (mod) {
415
416 if (typeof exports == "object" && typeof module == "object") {
417 // CommonJS
418 module.exports = mod();
419 } else {
420 // Plain browser env
421 (this || window).FileEditor = mod();
422 }
423
424 })(function () {
425 "use strict";
426
427 function FileEditor(textAreaElement, options) {
428 if (!(this instanceof FileEditor)) {
429 return new FileEditor(textAreaElement, options);
430 }
431 // bind the element instance to our Form
432 var te = $(textAreaElement).get(0);
433 if (te !== undefined) {
434 te.FileEditor = this;
435 }
436
437 this.modes_select = '#set_mode';
438 this.filename_selector = '#filename';
439 this.commit_btn_selector = '#commit_btn';
440 this.line_wrap_selector = '#line_wrap';
441 this.editor_preview_selector = '#editor_preview';
442
443 if (te !== undefined) {
444 this.cm = initCodeMirror(textAreaElement, null, false);
445 }
446
447 // FUNCTIONS and helpers
448 var self = this;
449
450 this.submitHandler = function() {
451 $(self.commit_btn_selector).on('click', function(e) {
452
453 var filename = $(self.filename_selector).val();
454 if (filename === "") {
455 alert("Missing filename");
456 e.preventDefault();
457 }
458
459 var button = $(this);
460 if (button.hasClass('clicked')) {
461 button.attr('disabled', true);
462 } else {
463 button.addClass('clicked');
464 }
465 });
466 };
467 this.submitHandler();
468
469 // on select line wraps change the editor
470 this.lineWrapHandler = function () {
471 $(self.line_wrap_selector).on('change', function (e) {
472 var selected = e.currentTarget;
473 var line_wraps = {'on': true, 'off': false}[selected.value];
474 setCodeMirrorLineWrap(self.cm, line_wraps)
475 });
476 };
477 this.lineWrapHandler();
478
479
480 this.showPreview = function () {
481
482 var _text = self.cm.getValue();
483 var _file_path = $(self.filename_selector).val();
484 if (_text && _file_path) {
485 $('.show-preview').addClass('active');
486 $('.show-editor').removeClass('active');
487
488 $(self.editor_preview_selector).show();
489 $(self.cm.getWrapperElement()).hide();
490
491
492 var post_data = {'text': _text, 'file_path': _file_path, 'csrf_token': CSRF_TOKEN};
493 $(self.editor_preview_selector).html(_gettext('Loading ...'));
494
495 var url = pyroutes.url('file_preview');
496
497 ajaxPOST(url, post_data, function (o) {
498 $(self.editor_preview_selector).html(o);
499 })
500 }
501
502 };
503
504 this.showEditor = function () {
505 $(self.editor_preview_selector).hide();
506 $('.show-editor').addClass('active');
507 $('.show-preview').removeClass('active');
508
509 $(self.cm.getWrapperElement()).show();
510 };
511
512
513 }
514
515 return FileEditor;
516 });
517
@@ -1,7 +1,7 b''
1 <%inherit file="/base/base.mako"/>
1 <%inherit file="/base/base.mako"/>
2
2
3 <%def name="title()">
3 <%def name="title()">
4 ${_('%s Files Add') % c.repo_name}
4 ${_('{} Files Add').format(c.repo_name)}
5 %if c.rhodecode_name:
5 %if c.rhodecode_name:
6 &middot; ${h.branding(c.rhodecode_name)}
6 &middot; ${h.branding(c.rhodecode_name)}
7 %endif
7 %endif
@@ -11,58 +11,60 b''
11 ${self.menu_items(active='repositories')}
11 ${self.menu_items(active='repositories')}
12 </%def>
12 </%def>
13
13
14 <%def name="breadcrumbs_links()">
14 <%def name="breadcrumbs_links()"></%def>
15 ${_('Add new file')} @ ${h.show_id(c.commit)} ${_('Branch')}: ${c.commit.branch}
16 </%def>
17
15
18 <%def name="menu_bar_subnav()">
16 <%def name="menu_bar_subnav()">
19 ${self.repo_menu(active='files')}
17 ${self.repo_menu(active='files')}
20 </%def>
18 </%def>
21
19
22 <%def name="main()">
20 <%def name="main()">
21
23 <div class="box">
22 <div class="box">
24
23
25 <div class="edit-file-title">
24 <div class="edit-file-title">
26 ${self.breadcrumbs()}
25 <span class="title-heading">${_('Add new file')} @ <code>${h.show_id(c.commit)}</code></span>
26 <span class="tag branchtag"><i class="icon-branch"></i> ${c.commit.branch}</span>
27 </div>
27 </div>
28
28
29 ${h.secure_form(h.route_path('repo_files_create_file', repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path=c.f_path), id='eform', enctype="multipart/form-data", class_="form-horizontal", request=request)}
29 ${h.secure_form(h.route_path('repo_files_create_file', repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path=c.f_path), id='eform', request=request)}
30 <div class="edit-file-fieldset">
30 <div class="edit-file-fieldset">
31 <div class="fieldset">
31 <div class="path-items">
32 <div id="destination-label" class="left-label">
32 <ul>
33 ${_('Path')}:
33 <li class="breadcrumb-path">
34 </div>
35 <div class="right-content">
36 <div>
34 <div>
37 ${h.files_breadcrumbs(c.repo_name,c.commit.raw_id,c.f_path, request.GET.get('at'))} /
35 <a href="${h.route_path('repo_files', repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path='')}"><i class="icon-home"></i></a> /
38 <input type="input-small" value="${c.f_path}" size="46" name="location" id="location">
36 <a href="${h.route_path('repo_files', repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path=c.f_path)}">${c.f_path}</a> ${('/' if c.f_path else '')}
39 </div>
37 </div>
40 </div>
38 </li>
41 </div>
39 <li class="location-path">
42 <div id="filename_container" class="fieldset">
40 <input class="file-name-input input-small" type="text" value="" name="filename" id="filename" placeholder="${_('Filename e.g example.py, or docs/readme.md')}">
43 <div class="filename-label left-label">
41 </li>
44 ${_('Filename')}:
42 </ul>
45 </div>
46 <div class="right-content">
47 <input class="input-small" type="text" value="" size="46" name="filename" id="filename">
48
49 </div>
50 </div>
43 </div>
51
44
52 </div>
45 </div>
53
46
54 <div class="table">
47 <div class="table">
55 <div id="files_data">
48 <div id="files_data">
49
56 <div id="codeblock" class="codeblock">
50 <div id="codeblock" class="codeblock">
57 <div class="code-header form" id="set_mode_header">
51 <div class="editor-items">
58 <div class="fields">
52 <div class="editor-action active show-editor pull-left" onclick="fileEditor.showEditor(); return false">
53 ${_('Edit')}
54 </div>
55
56 <div class="editor-action show-preview pull-left" onclick="fileEditor.showPreview(); return false">
57 ${_('Preview')}
58 </div>
59
60 <div class="pull-right">
61 ${h.dropdownmenu('line_wrap', 'off', [('on', _('Line wraps on')), ('off', _('line wraps off'))], extra_classes=['last-item'])}
62 </div>
63 <div class="pull-right">
59 ${h.dropdownmenu('set_mode','plain',[('plain',_('plain'))],enable_filter=True)}
64 ${h.dropdownmenu('set_mode','plain',[('plain', _('plain'))], enable_filter=True)}
60 <label for="line_wrap">${_('line wraps')}</label>
61 ${h.dropdownmenu('line_wrap', 'off', [('on', _('on')), ('off', _('off')),])}
62
63 <div id="render_preview" class="btn btn-small preview hidden" >${_('Preview')}</div>
64 </div>
65 </div>
65 </div>
66 </div>
67
66 <div id="editor_container">
68 <div id="editor_container">
67 <pre id="editor_pre"></pre>
69 <pre id="editor_pre"></pre>
68 <textarea id="editor" name="content" ></textarea>
70 <textarea id="editor" name="content" ></textarea>
@@ -74,109 +76,35 b''
74
76
75 <div class="edit-file-fieldset">
77 <div class="edit-file-fieldset">
76 <div class="fieldset">
78 <div class="fieldset">
77 <div id="commit-message-label" class="commit-message-label left-label">
78 ${_('Commit Message')}:
79 </div>
80 <div class="right-content">
81 <div class="message">
79 <div class="message">
82 <textarea id="commit" name="message" placeholder="${c.default_message}"></textarea>
80 <textarea id="commit" name="message" placeholder="${c.default_message}"></textarea>
83 </div>
81 </div>
84 </div>
82 </div>
85 </div>
83 <div class="pull-left">
86 <div class="pull-right">
87 ${h.reset('reset',_('Cancel'),class_="btn btn-small")}
88 ${h.submit('commit_btn',_('Commit changes'),class_="btn btn-small btn-success")}
84 ${h.submit('commit_btn',_('Commit changes'), class_="btn btn-small btn-success")}
89 </div>
85 </div>
90 </div>
86 </div>
91 ${h.end_form()}
87 ${h.end_form()}
92 </div>
88 </div>
89
93 <script type="text/javascript">
90 <script type="text/javascript">
94
91
95 $('#commit_btn').on('click', function() {
92 $(document).ready(function () {
96 var button = $(this);
97 if (button.hasClass('clicked')) {
98 button.attr('disabled', true);
99 } else {
100 button.addClass('clicked');
101 }
102 });
103
104 var hide_upload = function(){
105 $('#files_data').show();
106 $('#upload_file_container').hide();
107 $('#filename_container').show();
108 };
109
110 $('#file_enable').on('click', function(e){
111 e.preventDefault();
112 hide_upload();
113 });
114
115 var renderer = "";
116 var reset_url = "${h.route_path('repo_files',repo_name=c.repo_name,commit_id=c.commit.raw_id,f_path=c.f_path)}";
117 var myCodeMirror = initCodeMirror('editor', reset_url, false);
118
119 var modes_select = $('#set_mode');
93 var modes_select = $('#set_mode');
94 var filename_selector = '#filename';
120 fillCodeMirrorOptions(modes_select);
95 fillCodeMirrorOptions(modes_select);
121
96
122 var filename_selector = '#filename';
97 fileEditor = new FileEditor('#editor');
123 var callback = function(filename, mimetype, mode){
98
124 CodeMirrorPreviewEnable(mode);
125 };
126 // on change of select field set mode
99 // on change of select field set mode
127 setCodeMirrorModeFromSelect(
100 setCodeMirrorModeFromSelect(modes_select, filename_selector, fileEditor.cm, null);
128 modes_select, filename_selector, myCodeMirror, callback);
129
101
130 // on entering the new filename set mode, from given extension
102 // on entering the new filename set mode, from given extension
131 setCodeMirrorModeFromInput(
103 setCodeMirrorModeFromInput(modes_select, filename_selector, fileEditor.cm, null);
132 modes_select, filename_selector, myCodeMirror, callback);
133
134 // if the file is renderable set line wraps automatically
135 if (renderer !== ""){
136 var line_wrap = 'on';
137 $($('#line_wrap option[value="'+line_wrap+'"]')[0]).attr("selected", "selected");
138 setCodeMirrorLineWrap(myCodeMirror, true);
139 }
140
141 // on select line wraps change the editor
142 $('#line_wrap').on('change', function(e){
143 var selected = e.currentTarget;
144 var line_wraps = {'on': true, 'off': false}[selected.value];
145 setCodeMirrorLineWrap(myCodeMirror, line_wraps)
146 });
147
148 // render preview/edit button
149 $('#render_preview').on('click', function(e){
150 if($(this).hasClass('preview')){
151 $(this).removeClass('preview');
152 $(this).html("${_('Edit')}");
153 $('#editor_preview').show();
154 $(myCodeMirror.getWrapperElement()).hide();
155
104
156 var possible_renderer = {
105 $('#filename').focus();
157 'rst':'rst',
158 'markdown':'markdown',
159 'gfm': 'markdown'}[myCodeMirror.getMode().name];
160 var _text = myCodeMirror.getValue();
161 var _renderer = possible_renderer || DEFAULT_RENDERER;
162 var post_data = {'text': _text, 'renderer': _renderer, 'csrf_token': CSRF_TOKEN};
163 $('#editor_preview').html(_gettext('Loading ...'));
164 var url = pyroutes.url('repo_commit_comment_preview',
165 {'repo_name': '${c.repo_name}',
166 'commit_id': '${c.commit.raw_id}'});
167
106
168 ajaxPOST(url, post_data, function(o){
169 $('#editor_preview').html(o);
170 })
171 }
172 else{
173 $(this).addClass('preview');
174 $(this).html("${_('Preview')}");
175 $('#editor_preview').hide();
176 $(myCodeMirror.getWrapperElement()).show();
177 }
178 });
107 });
179 $('#filename').focus();
180
108
181 </script>
109 </script>
182 </%def>
110 </%def>
@@ -1,7 +1,7 b''
1 <%inherit file="/base/base.mako"/>
1 <%inherit file="/base/base.mako"/>
2
2
3 <%def name="title()">
3 <%def name="title()">
4 ${_('%s Files Delete') % c.repo_name}
4 ${_('{} Files Delete').format(c.repo_name)}
5 %if c.rhodecode_name:
5 %if c.rhodecode_name:
6 &middot; ${h.branding(c.rhodecode_name)}
6 &middot; ${h.branding(c.rhodecode_name)}
7 %endif
7 %endif
@@ -11,29 +11,36 b''
11 ${self.menu_items(active='repositories')}
11 ${self.menu_items(active='repositories')}
12 </%def>
12 </%def>
13
13
14 <%def name="breadcrumbs_links()">
14 <%def name="breadcrumbs_links()"></%def>
15 ${_('Delete file')} @ ${h.show_id(c.commit)}
16 </%def>
17
15
18 <%def name="menu_bar_subnav()">
16 <%def name="menu_bar_subnav()">
19 ${self.repo_menu(active='files')}
17 ${self.repo_menu(active='files')}
20 </%def>
18 </%def>
21
19
22 <%def name="main()">
20 <%def name="main()">
21
23 <div class="box">
22 <div class="box">
23
24 <div class="edit-file-title">
24 <div class="edit-file-title">
25 ${self.breadcrumbs()}
25 <span class="title-heading">${_('Delete file')} @ <code>${h.show_id(c.commit)}</code></span>
26 <span class="tag branchtag"><i class="icon-branch"></i> ${c.commit.branch}</span>
26 </div>
27 </div>
27 ${h.secure_form(h.route_path('repo_files_delete_file', repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path=c.f_path), id='eform', class_="form-horizontal", request=request)}
28
29 ${h.secure_form(h.route_path('repo_files_delete_file', repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path=c.f_path), id='eform', request=request)}
28 <div class="edit-file-fieldset">
30 <div class="edit-file-fieldset">
29 <div class="fieldset">
31 <div class="path-items">
30 <div id="destination-label" class="left-label">
32 <li class="breadcrumb-path">
31 ${_('Path')}:
33 <div>
34 <a href="${h.route_path('repo_files', repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path='')}"><i class="icon-home"></i></a> /
35 <a href="${h.route_path('repo_files', repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path=c.file.dir_path)}">${c.file.dir_path}</a> ${('/' if c.file.dir_path else '')}
32 </div>
36 </div>
33 <div class="right-content">
37 </li>
34 <span id="path-breadcrumbs">${h.files_breadcrumbs(c.repo_name,c.commit.raw_id,c.f_path, request.GET.get('at'))}</span>
38 <li class="location-path">
39 <input type="hidden" value="${c.f_path}" name="root_path">
40 <input class="file-name-input input-small" type="text" value="${c.file.name}" name="filename" id="filename" disabled="disabled">
41 </li>
35 </div>
42 </div>
36 </div>
43
37 </div>
44 </div>
38
45
39 <div id="codeblock" class="codeblock delete-file-preview">
46 <div id="codeblock" class="codeblock delete-file-preview">
@@ -53,20 +60,26 b''
53
60
54 <div class="edit-file-fieldset">
61 <div class="edit-file-fieldset">
55 <div class="fieldset">
62 <div class="fieldset">
56 <div id="commit-message-label" class="commit-message-label left-label">
57 ${_('Commit Message')}:
58 </div>
59 <div class="right-content">
60 <div class="message">
63 <div class="message">
61 <textarea id="commit" name="message" placeholder="${c.default_message}"></textarea>
64 <textarea id="commit" name="message" placeholder="${c.default_message}"></textarea>
62 </div>
65 </div>
63 </div>
66 </div>
64 </div>
67 <div class="pull-left">
65 <div class="pull-right">
68 ${h.submit('commit',_('Commit changes'),class_="btn btn-small btn-danger-action")}
66 ${h.reset('reset',_('Cancel'),class_="btn btn-small btn-danger")}
67 ${h.submit('commit',_('Delete File'),class_="btn btn-small btn-danger-action")}
68 </div>
69 </div>
69 </div>
70 </div>
70 ${h.end_form()}
71 ${h.end_form()}
71 </div>
72 </div>
73
74
75 <script type="text/javascript">
76
77 $(document).ready(function () {
78
79 fileEditor = new FileEditor('#editor');
80
81 });
82
83 </script>
84
72 </%def>
85 </%def>
@@ -1,7 +1,7 b''
1 <%inherit file="/base/base.mako"/>
1 <%inherit file="/base/base.mako"/>
2
2
3 <%def name="title()">
3 <%def name="title()">
4 ${_('%s File Edit') % c.repo_name}
4 ${_('{} Files Edit').format(c.repo_name)}
5 %if c.rhodecode_name:
5 %if c.rhodecode_name:
6 &middot; ${h.branding(c.rhodecode_name)}
6 &middot; ${h.branding(c.rhodecode_name)}
7 %endif
7 %endif
@@ -11,77 +11,61 b''
11 ${self.menu_items(active='repositories')}
11 ${self.menu_items(active='repositories')}
12 </%def>
12 </%def>
13
13
14 <%def name="breadcrumbs_links()">
14 <%def name="breadcrumbs_links()"></%def>
15 ${_('Edit file')} @ ${h.show_id(c.commit)}
16 </%def>
17
15
18 <%def name="menu_bar_subnav()">
16 <%def name="menu_bar_subnav()">
19 ${self.repo_menu(active='files')}
17 ${self.repo_menu(active='files')}
20 </%def>
18 </%def>
21
19
22 <%def name="main()">
20 <%def name="main()">
23 <% renderer = h.renderer_from_filename(c.f_path)%>
21
24 <div class="box">
22 <div class="box">
23
25 <div class="edit-file-title">
24 <div class="edit-file-title">
26 ${self.breadcrumbs()}
25 <span class="title-heading">${_('Edit file')} @ <code>${h.show_id(c.commit)}</code></span>
26 <span class="tag branchtag"><i class="icon-branch"></i> ${c.commit.branch}</span>
27 </div>
27 </div>
28
29 ${h.secure_form(h.route_path('repo_files_update_file', repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path=c.f_path), id='eform', request=request)}
28 <div class="edit-file-fieldset">
30 <div class="edit-file-fieldset">
29 <div class="fieldset">
31 <div class="path-items">
30 <div id="destination-label" class="left-label">
32 <ul>
31 ${_('Path')}:
33 <li class="breadcrumb-path">
34 <div>
35 <a href="${h.route_path('repo_files', repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path='')}"><i class="icon-home"></i></a> /
36 <a href="${h.route_path('repo_files', repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path=c.file.dir_path)}">${c.file.dir_path}</a> ${('/' if c.file.dir_path else '')}
32 </div>
37 </div>
33 <div class="right-content">
38 </li>
34 <div id="specify-custom-path-container">
39 <li class="location-path">
35 <span id="path-breadcrumbs">${h.files_breadcrumbs(c.repo_name,c.commit.raw_id,c.f_path, request.GET.get('at'))}</span>
40 <input type="hidden" value="${c.f_path}" name="root_path">
41 <input class="file-name-input input-small" type="text" value="${c.file.name}" name="filename" id="filename" placeholder="${_('Filename e.g example.py, or docs/readme.md')}">
42 </li>
43 </ul>
36 </div>
44 </div>
37 </div>
45
38 </div>
39 </div>
46 </div>
40
47
41 <div class="table">
48 <div class="table">
42 ${h.secure_form(h.route_path('repo_files_update_file', repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path=c.f_path), id='eform', request=request)}
49 <div id="files_data">
50
43 <div id="codeblock" class="codeblock" >
51 <div id="codeblock" class="codeblock">
44 <div class="code-header">
52 <div class="editor-items">
45 <div class="stats">
53 <div class="editor-action active show-editor pull-left" onclick="fileEditor.showEditor(); return false">
46 <i class="icon-file"></i>
54 ${_('Edit')}
47 <span class="item">${h.link_to("r%s:%s" % (c.file.commit.idx,h.short_id(c.file.commit.raw_id)),h.route_path('repo_commit',repo_name=c.repo_name,commit_id=c.file.commit.raw_id))}</span>
55 </div>
48 <span class="item">${h.format_byte_size_binary(c.file.size)}</span>
49 <span class="item last">${c.file.mimetype}</span>
50 <div class="buttons">
51 <a class="btn btn-mini" href="${h.route_path('repo_commits_file',repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path=c.f_path)}">
52 <i class="icon-time"></i> ${_('history')}
53 </a>
54
56
55 % if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
57 <div class="editor-action show-preview pull-left" onclick="fileEditor.showPreview(); return false">
56 % if not c.file.is_binary:
58 ${_('Preview')}
57 %if True:
59 </div>
58 ${h.link_to(_('source'), h.route_path('repo_files', repo_name=c.repo_name,commit_id=c.commit.raw_id,f_path=c.f_path),class_="btn btn-mini")}
59 %else:
60 ${h.link_to(_('annotation'),h.route_path('repo_files:annotated',repo_name=c.repo_name,commit_id=c.commit.raw_id,f_path=c.f_path),class_="btn btn-mini")}
61 %endif
62
60
63 <a class="btn btn-mini" href="${h.route_path('repo_file_raw',repo_name=c.repo_name,commit_id=c.commit.raw_id,f_path=c.f_path)}">
61 <div class="pull-right">
64 ${_('raw')}
62 ${h.dropdownmenu('line_wrap', 'off', [('on', _('Line wraps on')), ('off', _('line wraps off')),])}
65 </a>
63 </div>
66 <a class="btn btn-mini" href="${h.route_path('repo_file_download',repo_name=c.repo_name,commit_id=c.commit.raw_id,f_path=c.f_path)}">
64 <div class="pull-right">
67 <i class="icon-archive"></i> ${_('download')}
65 ${h.dropdownmenu('set_mode','plain',[('plain', _('plain'))],enable_filter=True)}
68 </a>
69 % endif
70 % endif
71 </div>
66 </div>
72 </div>
67 </div>
73 <div class="form">
74 <label for="set_mode">${_('Editing file')}:</label>
75 ${'%s /' % c.file.dir_path if c.file.dir_path else c.file.dir_path}
76 <input id="filename" type="text" name="filename" value="${c.file.name}">
77
68
78 ${h.dropdownmenu('set_mode','plain',[('plain',_('plain'))],enable_filter=True)}
79 <label for="line_wrap">${_('line wraps')}</label>
80 ${h.dropdownmenu('line_wrap', 'off', [('on', _('on')), ('off', _('off')),])}
81
82 <div id="render_preview" class="btn btn-small preview hidden">${_('Preview')}</div>
83 </div>
84 </div>
85 <div id="editor_container">
69 <div id="editor_container">
86 <pre id="editor_pre"></pre>
70 <pre id="editor_pre"></pre>
87 <textarea id="editor" name="content" >${h.escape(c.file.content)|n}</textarea>
71 <textarea id="editor" name="content" >${h.escape(c.file.content)|n}</textarea>
@@ -89,106 +73,48 b''
89 </div>
73 </div>
90 </div>
74 </div>
91 </div>
75 </div>
76 </div>
92
77
93 <div class="edit-file-fieldset">
78 <div class="edit-file-fieldset">
94 <div class="fieldset">
79 <div class="fieldset">
95 <div id="commit-message-label" class="commit-message-label left-label">
96 ${_('Commit Message')}:
97 </div>
98 <div class="right-content">
99 <div class="message">
80 <div class="message">
100 <textarea id="commit" name="message" placeholder="${c.default_message}"></textarea>
81 <textarea id="commit" name="message" placeholder="${c.default_message}"></textarea>
101 </div>
82 </div>
102 </div>
83 </div>
103 </div>
84 <div class="pull-left">
104 <div class="pull-right">
85 ${h.submit('commit_btn',_('Commit changes'), class_="btn btn-small btn-success")}
105 ${h.reset('reset',_('Cancel'),class_="btn btn-small")}
106 ${h.submit('commit',_('Commit changes'),class_="btn btn-small btn-success")}
107 </div>
86 </div>
108 </div>
87 </div>
109 ${h.end_form()}
88 ${h.end_form()}
110 </div>
89 </div>
111
90
112 <script type="text/javascript">
91 <script type="text/javascript">
92
113 $(document).ready(function(){
93 $(document).ready(function() {
114 var renderer = "${renderer}";
115 var reset_url = "${h.route_path('repo_files',repo_name=c.repo_name,commit_id=c.commit.raw_id,f_path=c.file.path)}";
116 var myCodeMirror = initCodeMirror('editor', reset_url);
117
118 var modes_select = $('#set_mode');
94 var modes_select = $('#set_mode');
95 var filename_selector = '#filename';
119 fillCodeMirrorOptions(modes_select);
96 fillCodeMirrorOptions(modes_select);
120
97
98 fileEditor = new FileEditor('#editor');
99
121 // try to detect the mode based on the file we edit
100 // try to detect the mode based on the file we edit
122 var mimetype = "${c.file.mimetype}";
101 var detected_mode = detectCodeMirrorMode("${c.file.name}", "${c.file.mimetype}");
123 var detected_mode = detectCodeMirrorMode(
124 "${c.file.name}", mimetype);
125
102
126 if(detected_mode){
103 if (detected_mode) {
127 setCodeMirrorMode(myCodeMirror, detected_mode);
104 setCodeMirrorMode(fileEditor.cm, detected_mode);
128 $(modes_select).select2("val", mimetype);
105
129 $(modes_select).change();
106 var mimetype = $(modes_select).find("option[mode={0}]".format(detected_mode)).val();
130 setCodeMirrorMode(myCodeMirror, detected_mode);
107 $(modes_select).select2("val", mimetype).trigger('change');
131 }
108 }
132
109
133 var filename_selector = '#filename';
134 var callback = function(filename, mimetype, mode){
135 CodeMirrorPreviewEnable(mode);
136 };
137 // on change of select field set mode
110 // on change of select field set mode
138 setCodeMirrorModeFromSelect(
111 setCodeMirrorModeFromSelect(modes_select, filename_selector, fileEditor.cm, null);
139 modes_select, filename_selector, myCodeMirror, callback);
140
112
141 // on entering the new filename set mode, from given extension
113 // on entering the new filename set mode, from given extension
142 setCodeMirrorModeFromInput(
114 setCodeMirrorModeFromInput(modes_select, filename_selector, fileEditor.cm, null);
143 modes_select, filename_selector, myCodeMirror, callback);
144
115
145 // if the file is renderable set line wraps automatically
146 if (renderer !== ""){
147 var line_wrap = 'on';
148 $($('#line_wrap option[value="'+line_wrap+'"]')[0]).attr("selected", "selected");
149 setCodeMirrorLineWrap(myCodeMirror, true);
150 }
151 // on select line wraps change the editor
152 $('#line_wrap').on('change', function(e){
153 var selected = e.currentTarget;
154 var line_wraps = {'on': true, 'off': false}[selected.value];
155 setCodeMirrorLineWrap(myCodeMirror, line_wraps)
156 });
116 });
157
117
158 // render preview/edit button
159 if (mimetype === 'text/x-rst' || mimetype === 'text/plain') {
160 $('#render_preview').removeClass('hidden');
161 }
162 $('#render_preview').on('click', function(e){
163 if($(this).hasClass('preview')){
164 $(this).removeClass('preview');
165 $(this).html("${_('Edit')}");
166 $('#editor_preview').show();
167 $(myCodeMirror.getWrapperElement()).hide();
168
118
169 var possible_renderer = {
170 'rst':'rst',
171 'markdown':'markdown',
172 'gfm': 'markdown'}[myCodeMirror.getMode().name];
173 var _text = myCodeMirror.getValue();
174 var _renderer = possible_renderer || DEFAULT_RENDERER;
175 var post_data = {'text': _text, 'renderer': _renderer, 'csrf_token': CSRF_TOKEN};
176 $('#editor_preview').html(_gettext('Loading ...'));
177 var url = pyroutes.url('repo_commit_comment_preview',
178 {'repo_name': '${c.repo_name}',
179 'commit_id': '${c.commit.raw_id}'});
180 ajaxPOST(url, post_data, function(o){
181 $('#editor_preview').html(o);
182 })
183 }
184 else{
185 $(this).addClass('preview');
186 $(this).html("${_('Preview')}");
187 $('#editor_preview').hide();
188 $(myCodeMirror.getWrapperElement()).show();
189 }
190 });
191
192 })
193 </script>
119 </script>
194 </%def>
120 </%def>
@@ -23,17 +23,17 b''
23
23
24 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
24 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
25 ## on branch head, can edit files
25 ## on branch head, can edit files
26 %if c.on_branch_head and c.branch_or_raw_id and not c.file.is_binary:
26 %if c.on_branch_head and c.branch_or_raw_id:
27 ## binary files are delete only
27 ## binary files are delete only
28 % if c.file.is_binary:
28 % if c.file.is_binary:
29 ${h.link_to(_('Edit'), '#Edit', class_="btn btn-default disabled tooltip", title=_('Editing binary files not allowed'))}
29 ${h.link_to(_('Edit'), '#Edit', class_="btn btn-default disabled tooltip", title=_('Editing binary files not allowed'))}
30 ${h.link_to(_('Delete'), h.route_path('repo_files_remove_file',repo_name=c.repo_name,commit_id=c.branch_or_raw_id,f_path=c.f_path, _anchor='edit'),class_="btn btn-danger")}
30 ${h.link_to(_('Delete'), h.route_path('repo_files_remove_file',repo_name=c.repo_name,commit_id=c.branch_or_raw_id,f_path=c.f_path),class_="btn btn-danger")}
31 % else:
31 % else:
32 <a class="btn btn-default" href="${h.route_path('repo_files_edit_file',repo_name=c.repo_name,commit_id=c.branch_or_raw_id,f_path=c.f_path, _anchor='edit')}">
32 <a class="btn btn-default" href="${h.route_path('repo_files_edit_file',repo_name=c.repo_name,commit_id=c.branch_or_raw_id,f_path=c.f_path)}">
33 ${_('Edit on branch: ')}<code>${c.branch_name}</code>
33 ${_('Edit on branch: ')}<code>${c.branch_name}</code>
34 </a>
34 </a>
35
35
36 <a class="btn btn-danger" href="${h.route_path('repo_files_remove_file',repo_name=c.repo_name,commit_id=c.branch_or_raw_id,f_path=c.f_path, _anchor='edit')}">
36 <a class="btn btn-danger" href="${h.route_path('repo_files_remove_file',repo_name=c.repo_name,commit_id=c.branch_or_raw_id,f_path=c.f_path)}">
37 ${_('Delete')}
37 ${_('Delete')}
38 </a>
38 </a>
39 % endif
39 % endif
@@ -1,7 +1,7 b''
1 <%inherit file="/base/base.mako"/>
1 <%inherit file="/base/base.mako"/>
2
2
3 <%def name="title()">
3 <%def name="title()">
4 ${_('%s Files Add') % c.repo_name}
4 ${_('{} Files Upload').format(c.repo_name)}
5 %if c.rhodecode_name:
5 %if c.rhodecode_name:
6 &middot; ${h.branding(c.rhodecode_name)}
6 &middot; ${h.branding(c.rhodecode_name)}
7 %endif
7 %endif
@@ -11,180 +11,197 b''
11 ${self.menu_items(active='repositories')}
11 ${self.menu_items(active='repositories')}
12 </%def>
12 </%def>
13
13
14 <%def name="breadcrumbs_links()">
14 <%def name="breadcrumbs_links()"></%def>
15 ${_('Add new file')} @ ${h.show_id(c.commit)} ${_('Branch')}: ${c.commit.branch}
16 </%def>
17
15
18 <%def name="menu_bar_subnav()">
16 <%def name="menu_bar_subnav()">
19 ${self.repo_menu(active='files')}
17 ${self.repo_menu(active='files')}
20 </%def>
18 </%def>
21
19
22 <%def name="main()">
20 <%def name="main()">
21
23 <div class="box">
22 <div class="box">
23 ## Template for uploads
24 <div style="display: none" id="tpl-dropzone">
25 <div class="dz-preview dz-file-preview">
26 <div class="dz-details">
24
27
25 <div class="edit-file-title">
28 <div class="dz-filename">
26 ${self.breadcrumbs()}
29 <span data-dz-name></span>
30 </div>
31 <div class="dz-filename-size">
32 <span class="dz-size" data-dz-size></span>
33
27 </div>
34 </div>
28
35
29 ${h.secure_form(h.route_path('repo_files_create_file', repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path=c.f_path), id='eform', enctype="multipart/form-data", class_="form-horizontal", request=request)}
36 <div class="dz-sending" style="display: none">${_('Uploading...')}</div>
30 <div class="edit-file-fieldset">
37 <div class="dz-response" style="display: none">
31 <div class="fieldset">
38 ${_('Uploaded')} 100%
32 <div id="destination-label" class="left-label">
39 </div>
33 ${_('Path')}:
40
34 </div>
41 </div>
35 <div class="right-content">
42 <div class="dz-progress">
36 <div>
43 <span class="dz-upload" data-dz-uploadprogress></span>
37 ${h.files_breadcrumbs(c.repo_name,c.commit.raw_id,c.f_path, request.GET.get('at'))} /
44 </div>
38 <input type="input-small" value="${c.f_path}" size="46" name="location" id="location">
45
46 <div class="dz-error-message">
39 </div>
47 </div>
40 </div>
48 </div>
41 </div>
49 </div>
42
50
43 <div id="upload_file_container" class="fieldset">
51 <div class="edit-file-title">
44 <div class="filename-label left-label">
52 <span class="title-heading">${_('Upload new file')} @ <code>${h.show_id(c.commit)}</code></span>
45 ${_('Filename')}:
53 <span class="tag branchtag"><i class="icon-branch"></i> ${c.commit.branch}</span>
46 </div>
54 </div>
47 <div class="right-content">
55
48 <input class="input-small" type="text" value="" size="46" name="filename_upload" id="filename_upload" placeholder="${_('No file selected')}">
56 <% form_url = h.route_path('repo_files_upload_file', repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path=c.f_path) %>
57 ##${h.secure_form(form_url, id='eform', enctype="multipart/form-data", request=request)}
58 <div class="edit-file-fieldset">
59 <div class="path-items">
60 <ul>
61 <li class="breadcrumb-path">
62 <div>
63 <a href="${h.route_path('repo_files', repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path='')}"><i class="icon-home"></i></a> /
64 <a href="${h.route_path('repo_files', repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path=c.f_path)}">${c.f_path}</a> ${('/' if c.f_path else '')}
49 </div>
65 </div>
50 <div class="filename-label left-label file-upload-label">
66 </li>
51 ${_('Upload file')}:
67 <li class="location-path">
68
69 </li>
70 </ul>
71 </div>
72
52 </div>
73 </div>
53 <div class="right-content file-upload-input">
74
54 <label for="upload_file" class="btn btn-default">Browse</label>
75 <div class="upload-form table">
76 <div id="files_data">
55
77
56 <input type="file" name="upload_file" id="upload_file">
78 <div class="dropzone-wrapper" id="file-uploader">
79 <div class="dropzone-pure">
80 <div class="dz-message">
81 <i class="icon-upload" style="font-size:36px"></i></br>
82 ${_("Drag'n Drop files here or")} <span class="link">${_('Choose your files')}</span>.<br>
83 </div>
84 </div>
85
57 </div>
86 </div>
58 </div>
87 </div>
59
88
60 </div>
89 </div>
61
90
62 <div class="table">
91 <div class="upload-form edit-file-fieldset">
63 <div id="files_data">
64 <div id="codeblock" class="codeblock">
65 <div class="code-header form" id="set_mode_header">
66 <div class="fields">
67 ${h.dropdownmenu('set_mode','plain',[('plain',_('plain'))],enable_filter=True)}
68 <label for="line_wrap">${_('line wraps')}</label>
69 ${h.dropdownmenu('line_wrap', 'off', [('on', _('on')), ('off', _('off')),])}
70
71 <div id="render_preview" class="btn btn-small preview hidden" >${_('Preview')}</div>
72 </div>
73 </div>
74 <div id="editor_container">
75 <pre id="editor_pre"></pre>
76 <textarea id="editor" name="content" ></textarea>
77 <div id="editor_preview"></div>
78 </div>
79 </div>
80 </div>
81 </div>
82
83 <div class="edit-file-fieldset">
84 <div class="fieldset">
92 <div class="fieldset">
85 <div id="commit-message-label" class="commit-message-label left-label">
86 ${_('Commit Message')}:
87 </div>
88 <div class="right-content">
89 <div class="message">
93 <div class="message">
90 <textarea id="commit" name="message" placeholder="${c.default_message}"></textarea>
94 <textarea id="commit" name="message" placeholder="${c.default_message}"></textarea>
91 </div>
95 </div>
92 </div>
96 </div>
93 </div>
97 <div class="pull-left">
94 <div class="pull-right">
95 ${h.reset('reset',_('Cancel'),class_="btn btn-small")}
96 ${h.submit('commit_btn',_('Commit changes'),class_="btn btn-small btn-success")}
98 ${h.submit('commit_btn',_('Commit changes'), class_="btn btn-small btn-success")}
97 </div>
99 </div>
98 </div>
100 </div>
99 ${h.end_form()}
101 ##${h.end_form()}
102
103 <div class="file-upload-transaction-wrapper" style="display: none">
104 <div class="file-upload-transaction">
105 <h3>${_('Commiting...')}</h3>
106 <p>${_('Please wait while the files are being uploaded')}</p>
107 <p class="error" style="display: none">
108
109 </p>
110 <i class="icon-spin animate-spin"></i>
111 <p></p>
100 </div>
112 </div>
113 </div>
114
115 </div>
116
101 <script type="text/javascript">
117 <script type="text/javascript">
102
118
103 $('#commit_btn').on('click', function() {
119 $(document).ready(function () {
120
121 //see: https://www.dropzonejs.com/#configuration
122 myDropzone = new Dropzone("div#file-uploader", {
123 url: "${form_url}",
124 headers: {"X-CSRF-Token": CSRF_TOKEN},
125 paramName: function () {
126 return "files_upload"
127 }, // The name that will be used to transfer the file
128 parallelUploads: 20,
129 maxFiles: 20,
130 uploadMultiple: true,
131 //chunking: true, // use chunking transfer, not supported at the moment
132 //maxFilesize: 2, // in MBs
133 autoProcessQueue: false, // if false queue will not be processed automatically.
134 createImageThumbnails: false,
135 previewTemplate: document.querySelector('#tpl-dropzone').innerHTML,
136 accept: function (file, done) {
137 done();
138 },
139 init: function () {
140 this.on("addedfile", function (file) {
141
142 });
143
144 this.on("sending", function (file, xhr, formData) {
145 formData.append("message", $('#commit').val());
146 $(file.previewElement).find('.dz-sending').show();
147 });
148
149 this.on("success", function (file, response) {
150 $(file.previewElement).find('.dz-sending').hide();
151 $(file.previewElement).find('.dz-response').show();
152
153 if (response.error !== null) {
154 $('.file-upload-transaction-wrapper .error').html('ERROR: {0}'.format(response.error));
155 $('.file-upload-transaction-wrapper .error').show();
156 $('.file-upload-transaction-wrapper i').hide()
157 }
158
159 var redirect_url = response.redirect_url || '/';
160 window.location = redirect_url
161
162 });
163
164 this.on("error", function (file, errorMessage, xhr) {
165 var error = null;
166
167 if (xhr !== undefined){
168 var httpStatus = xhr.status + " " + xhr.statusText;
169 if (xhr.status >= 500) {
170 error = httpStatus;
171 }
172 }
173
174 if (error === null) {
175 error = errorMessage.error || errorMessage || httpStatus;
176 }
177
178 $(file.previewElement).find('.dz-error-message').html('ERROR: {0}'.format(error));
179 });
180 }
181 });
182
183 $('#commit_btn').on('click', function(e) {
184 e.preventDefault();
104 var button = $(this);
185 var button = $(this);
105 if (button.hasClass('clicked')) {
186 if (button.hasClass('clicked')) {
106 button.attr('disabled', true);
187 button.attr('disabled', true);
107 } else {
188 } else {
108 button.addClass('clicked');
189 button.addClass('clicked');
109 }
190 }
110 });
111
191
112 var hide_upload = function(){
192 var files = myDropzone.getQueuedFiles();
113 $('#files_data').show();
193 if (files.length === 0) {
114 $('#upload_file_container').hide();
194 alert("Missing files");
115 $('#filename_container').show();
116 };
117
118 $('#file_enable').on('click', function(e){
119 e.preventDefault();
195 e.preventDefault();
120 hide_upload();
121 });
122
123 var renderer = "";
124 var reset_url = "${h.route_path('repo_files',repo_name=c.repo_name,commit_id=c.commit.raw_id,f_path=c.f_path)}";
125 var myCodeMirror = initCodeMirror('editor', reset_url, false);
126
127 var modes_select = $('#set_mode');
128 fillCodeMirrorOptions(modes_select);
129
130 var filename_selector = '#filename';
131 var callback = function(filename, mimetype, mode){
132 CodeMirrorPreviewEnable(mode);
133 };
134 // on change of select field set mode
135 setCodeMirrorModeFromSelect(
136 modes_select, filename_selector, myCodeMirror, callback);
137
138 // on entering the new filename set mode, from given extension
139 setCodeMirrorModeFromInput(
140 modes_select, filename_selector, myCodeMirror, callback);
141
142 // if the file is renderable set line wraps automatically
143 if (renderer !== ""){
144 var line_wrap = 'on';
145 $($('#line_wrap option[value="'+line_wrap+'"]')[0]).attr("selected", "selected");
146 setCodeMirrorLineWrap(myCodeMirror, true);
147 }
196 }
148
197
149 // on select line wraps change the editor
198 $('.upload-form').hide();
150 $('#line_wrap').on('change', function(e){
199 $('.file-upload-transaction-wrapper').show();
151 var selected = e.currentTarget;
200 myDropzone.processQueue();
152 var line_wraps = {'on': true, 'off': false}[selected.value];
201
153 setCodeMirrorLineWrap(myCodeMirror, line_wraps)
154 });
202 });
155
203
156 // render preview/edit button
157 $('#render_preview').on('click', function(e){
158 if($(this).hasClass('preview')){
159 $(this).removeClass('preview');
160 $(this).html("${_('Edit')}");
161 $('#editor_preview').show();
162 $(myCodeMirror.getWrapperElement()).hide();
163
164 var possible_renderer = {
165 'rst':'rst',
166 'markdown':'markdown',
167 'gfm': 'markdown'}[myCodeMirror.getMode().name];
168 var _text = myCodeMirror.getValue();
169 var _renderer = possible_renderer || DEFAULT_RENDERER;
170 var post_data = {'text': _text, 'renderer': _renderer, 'csrf_token': CSRF_TOKEN};
171 $('#editor_preview').html(_gettext('Loading ...'));
172 var url = pyroutes.url('repo_commit_comment_preview',
173 {'repo_name': '${c.repo_name}',
174 'commit_id': '${c.commit.raw_id}'});
175
176 ajaxPOST(url, post_data, function(o){
177 $('#editor_preview').html(o);
178 })
179 }
180 else{
181 $(this).addClass('preview');
182 $(this).html("${_('Preview')}");
183 $('#editor_preview').hide();
184 $(myCodeMirror.getWrapperElement()).show();
185 }
186 });
204 });
187 $('#filename').focus();
188
205
189 </script>
206 </script>
190 </%def>
207 </%def>
General Comments 0
You need to be logged in to leave comments. Login now