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= |
|
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 |
|
|
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_ |
|
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 |
|
|
1231 | category='warning') | |
|
1232 | raise HTTPFound(default_redirect_url) | |||
|
1233 | ||||
1229 | try: |
|
1234 | try: | |
1230 | mapping = { |
|
1235 | mapping = { | |
1231 |
org_ |
|
1236 | org_node_path: { | |
1232 |
'org_filename': org_ |
|
1237 | 'org_filename': org_node_path, | |
1233 |
'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 |
|
|||
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 |
|
|||
1366 | if type(c.commit) is EmptyCommit: |
|
|||
1367 | redirect_url = h.route_path( |
|
|||
1368 | 'repo_summary', repo_name=self.db_repo_name) |
|
|||
1369 | else: |
|
|||
1370 | redirect_url = default_redirect_url |
|
|||
1371 |
|
||||
1372 | if not filename: |
|
1347 | if not filename: | |
1373 | h.flash(_('No filename'), category='warning') |
|
1348 | # If there's no commit, redirect to repo summary | |
|
1349 | if type(c.commit) is EmptyCommit: | |||
|
1350 | redirect_url = h.route_path( | |||
|
1351 | 'repo_summary', repo_name=self.db_repo_name) | |||
|
1352 | else: | |||
|
1353 | redirect_url = default_redirect_url | |||
|
1354 | h.flash(_('No filename specified'), 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 | |
|
1362 | nodes = { | |||
|
1363 | node_path: { | |||
|
1364 | 'content': content | |||
|
1365 | } | |||
|
1366 | } | |||
1386 |
|
1367 | |||
1387 | try: |
|
1368 | try: | |
1388 | nodes = { |
|
1369 | ||
1389 | node_path: { |
|
1370 | commit = ScmModel().create_nodes( | |
1390 | 'content': content |
|
|||
1391 | } |
|
|||
1392 | } |
|
|||
1393 | 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,14 +1376,16 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 ' |
|
1388 | 'contain .. in the path'), category='warning') | |
1409 | '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: | |
1412 | h.flash(_(h.escape(e)), category='error') |
|
1391 | h.flash(_(h.escape(e)), category='error') | |
@@ -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: |
|
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 | padding-top: 10px; | |||
280 | } |
|
281 | } | |
|
282 | ||||
281 | #gistid { |
|
283 | #gistid { | |
282 | margin-right: @padding; |
|
284 | margin-right: @padding; | |
283 | } |
|
285 | } | |
@@ -1327,9 +1329,9 b' table.integrations {' | |||||
1327 | } |
|
1329 | } | |
1328 | } |
|
1330 | } | |
1329 |
|
1331 | |||
1330 | #editor_container{ |
|
1332 | #editor_container { | |
1331 |
|
|
1333 | position: relative; | |
1332 |
|
|
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 |
|
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 |
. |
|
2706 | .dz-message { | |
2588 | font-weight: 700; |
|
2707 | font-weight: 700; | |
2589 | } |
|
2708 | text-align: center; | |
2590 |
|
2709 | margin: 2em 0; | ||
2591 | .dropzone .dz-message { |
|
2710 | } | |
2592 | text-align: center; |
|
2711 | ||
2593 | margin: 2em 0; |
|
|||
2594 | } |
|
2712 | } | |
2595 |
|
2713 | |||
2596 | .dz-preview { |
|
2714 | .dz-preview { | |
2597 |
margin: 10px 0 |
|
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 | var ta = $('#' + textAreadId).get(0); |
|
207 | if (textAreadId.substr(0,1) === "#"){ | |
|
208 | var ta = $(textAreadId).get(0); | |||
|
209 | }else { | |||
|
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 |
${_(' |
|
4 | ${_('{} Files Add').format(c.repo_name)} | |
5 | %if c.rhodecode_name: |
|
5 | %if c.rhodecode_name: | |
6 | · ${h.branding(c.rhodecode_name)} |
|
6 | · ${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', |
|
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=" |
|
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 |
</ |
|
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"> | |
59 | ${h.dropdownmenu('set_mode','plain',[('plain',_('plain'))],enable_filter=True)} |
|
53 | ${_('Edit')} | |
60 | <label for="line_wrap">${_('line wraps')}</label> |
|
54 | </div> | |
61 | ${h.dropdownmenu('line_wrap', 'off', [('on', _('on')), ('off', _('off')),])} |
|
55 | ||
|
56 | <div class="editor-action show-preview pull-left" onclick="fileEditor.showPreview(); return false"> | |||
|
57 | ${_('Preview')} | |||
|
58 | </div> | |||
62 |
|
59 | |||
63 | <div id="render_preview" class="btn btn-small preview hidden" >${_('Preview')}</div> |
|
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"> | |||
|
64 | ${h.dropdownmenu('set_mode','plain',[('plain', _('plain'))], enable_filter=True)} | |||
|
65 | </div> | |||
64 | </div> |
|
66 | </div> | |
65 | </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"> |
|
79 | <div class="message"> | |
78 | ${_('Commit Message')}: |
|
80 | <textarea id="commit" name="message" placeholder="${c.default_message}"></textarea> | |
79 | </div> |
|
|||
80 | <div class="right-content"> |
|
|||
81 | <div class="message"> |
|
|||
82 | <textarea id="commit" name="message" placeholder="${c.default_message}"></textarea> |
|
|||
83 | </div> |
|
|||
84 | </div> |
|
81 | </div> | |
85 | </div> |
|
82 | </div> | |
86 |
<div class="pull- |
|
83 | <div class="pull-left"> | |
87 |
${h. |
|
84 | ${h.submit('commit_btn',_('Commit changes'), class_="btn btn-small btn-success")} | |
88 | ${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 |
$( |
|
92 | $(document).ready(function () { | |
96 | var button = $(this); |
|
93 | var modes_select = $('#set_mode'); | |
97 | if (button.hasClass('clicked')) { |
|
94 | var filename_selector = '#filename'; | |
98 | button.attr('disabled', true); |
|
95 | fillCodeMirrorOptions(modes_select); | |
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 |
|
96 | |||
110 | $('#file_enable').on('click', function(e){ |
|
97 | fileEditor = new FileEditor('#editor'); | |
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'); |
|
|||
120 | fillCodeMirrorOptions(modes_select); |
|
|||
121 |
|
||||
122 | var filename_selector = '#filename'; |
|
|||
123 | var callback = function(filename, mimetype, mode){ |
|
|||
124 | CodeMirrorPreviewEnable(mode); |
|
|||
125 | }; |
|
|||
126 | // on change of select field set mode |
|
|||
127 | setCodeMirrorModeFromSelect( |
|
|||
128 | modes_select, filename_selector, myCodeMirror, callback); |
|
|||
129 |
|
||||
130 | // on entering the new filename set mode, from given extension |
|
|||
131 | setCodeMirrorModeFromInput( |
|
|||
132 | modes_select, filename_selector, myCodeMirror, callback); |
|
|||
133 |
|
98 | |||
134 | // if the file is renderable set line wraps automatically |
|
99 | // on change of select field set mode | |
135 | if (renderer !== ""){ |
|
100 | setCodeMirrorModeFromSelect(modes_select, filename_selector, fileEditor.cm, null); | |
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 |
|
101 | |||
156 | var possible_renderer = { |
|
102 | // on entering the new filename set mode, from given extension | |
157 | 'rst':'rst', |
|
103 | setCodeMirrorModeFromInput(modes_select, filename_selector, fileEditor.cm, null); | |
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 |
|
104 | |||
168 | ajaxPOST(url, post_data, function(o){ |
|
105 | $('#filename').focus(); | |
169 | $('#editor_preview').html(o); |
|
106 | ||
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 |
${_(' |
|
4 | ${_('{} Files Delete').format(c.repo_name)} | |
5 | %if c.rhodecode_name: |
|
5 | %if c.rhodecode_name: | |
6 | · ${h.branding(c.rhodecode_name)} |
|
6 | · ${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=" |
|
31 | <div class="path-items"> | |
30 | <div id="destination-label" class="left-label"> |
|
32 | <li class="breadcrumb-path"> | |
31 |
|
|
33 | <div> | |
32 | </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> / | |
33 | <div class="right-content"> |
|
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 '')} | |
34 | <span id="path-breadcrumbs">${h.files_breadcrumbs(c.repo_name,c.commit.raw_id,c.f_path, request.GET.get('at'))}</span> |
|
36 | </div> | |
35 |
</ |
|
37 | </li> | |
|
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> | |||
36 | </div> |
|
42 | </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"> |
|
63 | <div class="message"> | |
57 | ${_('Commit Message')}: |
|
64 | <textarea id="commit" name="message" placeholder="${c.default_message}"></textarea> | |
58 | </div> |
|
|||
59 | <div class="right-content"> |
|
|||
60 | <div class="message"> |
|
|||
61 | <textarea id="commit" name="message" placeholder="${c.default_message}"></textarea> |
|
|||
62 | </div> |
|
|||
63 | </div> |
|
65 | </div> | |
64 | </div> |
|
66 | </div> | |
65 |
<div class="pull- |
|
67 | <div class="pull-left"> | |
66 |
${h. |
|
68 | ${h.submit('commit',_('Commit changes'),class_="btn btn-small btn-danger-action")} | |
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 |
${_(' |
|
4 | ${_('{} Files Edit').format(c.repo_name)} | |
5 | %if c.rhodecode_name: |
|
5 | %if c.rhodecode_name: | |
6 | · ${h.branding(c.rhodecode_name)} |
|
6 | · ${h.branding(c.rhodecode_name)} | |
7 | %endif |
|
7 | %endif | |
@@ -11,184 +11,110 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=" |
|
31 | <div class="path-items"> | |
30 | <div id="destination-label" class="left-label"> |
|
32 | <ul> | |
31 | ${_('Path')}: |
|
33 | <li class="breadcrumb-path"> | |
32 |
< |
|
34 | <div> | |
33 | <div class="right-content"> |
|
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> / | |
34 | <div id="specify-custom-path-container"> |
|
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 '')} | |
35 | <span id="path-breadcrumbs">${h.files_breadcrumbs(c.repo_name,c.commit.raw_id,c.f_path, request.GET.get('at'))}</span> |
|
|||
36 | </div> |
|
37 | </div> | |
37 |
</ |
|
38 | </li> | |
|
39 | <li class="location-path"> | |||
|
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> | |||
38 | </div> |
|
44 | </div> | |
|
45 | ||||
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"> | |
43 | <div id="codeblock" class="codeblock" > |
|
|||
44 | <div class="code-header"> |
|
|||
45 | <div class="stats"> |
|
|||
46 | <i class="icon-file"></i> |
|
|||
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> |
|
|||
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 |
|
|
50 | ||
55 | % if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name): |
|
51 | <div id="codeblock" class="codeblock"> | |
56 | % if not c.file.is_binary: |
|
52 | <div class="editor-items"> | |
57 | %if True: |
|
53 | <div class="editor-action active show-editor pull-left" onclick="fileEditor.showEditor(); return false"> | |
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")} |
|
54 | ${_('Edit')} | |
59 |
|
|
55 | </div> | |
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")} |
|
56 | ||
61 | %endif |
|
57 | <div class="editor-action show-preview pull-left" onclick="fileEditor.showPreview(); return false"> | |
|
58 | ${_('Preview')} | |||
|
59 | </div> | |||
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 |
|
|
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 |
|
|
66 | </div> | |
69 | % endif |
|
|||
70 | % endif |
|
|||
71 | </div> |
|
67 | </div> | |
72 | </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)} |
|
69 | <div id="editor_container"> | |
79 | <label for="line_wrap">${_('line wraps')}</label> |
|
70 | <pre id="editor_pre"></pre> | |
80 | ${h.dropdownmenu('line_wrap', 'off', [('on', _('on')), ('off', _('off')),])} |
|
71 | <textarea id="editor" name="content" >${h.escape(c.file.content)|n}</textarea> | |
81 |
|
72 | <div id="editor_preview" ></div> | ||
82 | <div id="render_preview" class="btn btn-small preview hidden">${_('Preview')}</div> |
|
73 | </div> | |
83 | </div> |
|
|||
84 | </div> |
|
|||
85 | <div id="editor_container"> |
|
|||
86 | <pre id="editor_pre"></pre> |
|
|||
87 | <textarea id="editor" name="content" >${h.escape(c.file.content)|n}</textarea> |
|
|||
88 | <div id="editor_preview" ></div> |
|
|||
89 | </div> |
|
74 | </div> | |
90 | </div> |
|
75 | </div> | |
91 | </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"> |
|
80 | <div class="message"> | |
96 | ${_('Commit Message')}: |
|
81 | <textarea id="commit" name="message" placeholder="${c.default_message}"></textarea> | |
97 | </div> |
|
|||
98 | <div class="right-content"> |
|
|||
99 | <div class="message"> |
|
|||
100 | <textarea id="commit" name="message" placeholder="${c.default_message}"></textarea> |
|
|||
101 | </div> |
|
|||
102 | </div> |
|
82 | </div> | |
103 | </div> |
|
83 | </div> | |
104 |
<div class="pull- |
|
84 | <div class="pull-left"> | |
105 |
${h. |
|
85 | ${h.submit('commit_btn',_('Commit changes'), class_="btn btn-small btn-success")} | |
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"> | |
113 | $(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 |
|
92 | |||
|
93 | $(document).ready(function() { | |||
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( |
|
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 |
|
||||
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 | }); |
|
|||
157 |
|
115 | |||
158 | // render preview/edit button |
|
116 | }); | |
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 |
|
117 | |||
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 |
|
118 | |||
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 |
|
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 |
|
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 |
|
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 |
|
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 |
${_(' |
|
4 | ${_('{} Files Upload').format(c.repo_name)} | |
5 | %if c.rhodecode_name: |
|
5 | %if c.rhodecode_name: | |
6 | · ${h.branding(c.rhodecode_name)} |
|
6 | · ${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"> | |||
|
27 | ||||
|
28 | <div class="dz-filename"> | |||
|
29 | <span data-dz-name></span> | |||
|
30 | </div> | |||
|
31 | <div class="dz-filename-size"> | |||
|
32 | <span class="dz-size" data-dz-size></span> | |||
24 |
|
33 | |||
25 | <div class="edit-file-title"> |
|
34 | </div> | |
26 | ${self.breadcrumbs()} |
|
35 | ||
|
36 | <div class="dz-sending" style="display: none">${_('Uploading...')}</div> | |||
|
37 | <div class="dz-response" style="display: none"> | |||
|
38 | ${_('Uploaded')} 100% | |||
|
39 | </div> | |||
|
40 | ||||
|
41 | </div> | |||
|
42 | <div class="dz-progress"> | |||
|
43 | <span class="dz-upload" data-dz-uploadprogress></span> | |||
|
44 | </div> | |||
|
45 | ||||
|
46 | <div class="dz-error-message"> | |||
|
47 | </div> | |||
|
48 | </div> | |||
27 | </div> |
|
49 | </div> | |
28 |
|
50 | |||
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)} |
|
51 | <div class="edit-file-title"> | |
|
52 | <span class="title-heading">${_('Upload new file')} @ <code>${h.show_id(c.commit)}</code></span> | |||
|
53 | <span class="tag branchtag"><i class="icon-branch"></i> ${c.commit.branch}</span> | |||
|
54 | </div> | |||
|
55 | ||||
|
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)} | |||
30 | <div class="edit-file-fieldset"> |
|
58 | <div class="edit-file-fieldset"> | |
31 |
<div class=" |
|
59 | <div class="path-items"> | |
32 | <div id="destination-label" class="left-label"> |
|
60 | <ul> | |
33 | ${_('Path')}: |
|
61 | <li class="breadcrumb-path"> | |
34 | </div> |
|
|||
35 | <div class="right-content"> |
|
|||
36 | <div> |
|
62 | <div> | |
37 | ${h.files_breadcrumbs(c.repo_name,c.commit.raw_id,c.f_path, request.GET.get('at'))} / |
|
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> / | |
38 | <input type="input-small" value="${c.f_path}" size="46" name="location" id="location"> |
|
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 '')} | |
39 | </div> |
|
65 | </div> | |
40 |
</ |
|
66 | </li> | |
|
67 | <li class="location-path"> | |||
|
68 | ||||
|
69 | </li> | |||
|
70 | </ul> | |||
41 | </div> |
|
71 | </div> | |
42 |
|
72 | |||
43 | <div id="upload_file_container" class="fieldset"> |
|
73 | </div> | |
44 | <div class="filename-label left-label"> |
|
74 | ||
45 | ${_('Filename')}: |
|
75 | <div class="upload-form table"> | |
46 | </div> |
|
76 | <div id="files_data"> | |
47 | <div class="right-content"> |
|
|||
48 | <input class="input-small" type="text" value="" size="46" name="filename_upload" id="filename_upload" placeholder="${_('No file selected')}"> |
|
|||
49 | </div> |
|
|||
50 | <div class="filename-label left-label file-upload-label"> |
|
|||
51 | ${_('Upload file')}: |
|
|||
52 | </div> |
|
|||
53 | <div class="right-content file-upload-input"> |
|
|||
54 | <label for="upload_file" class="btn btn-default">Browse</label> |
|
|||
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 |
|
92 | <div class="fieldset"> | |
64 |
<div |
|
93 | <div class="message"> | |
65 | <div class="code-header form" id="set_mode_header"> |
|
94 | <textarea id="commit" name="message" placeholder="${c.default_message}"></textarea> | |
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> |
|
95 | </div> | |
74 | <div id="editor_container"> |
|
96 | </div> | |
75 | <pre id="editor_pre"></pre> |
|
97 | <div class="pull-left"> | |
76 | <textarea id="editor" name="content" ></textarea> |
|
98 | ${h.submit('commit_btn',_('Commit changes'), class_="btn btn-small btn-success")} | |
77 | <div id="editor_preview"></div> |
|
|||
78 | </div> |
|
|||
79 | </div> |
|
|||
80 | </div> |
|
99 | </div> | |
81 | </div> |
|
100 | </div> | |
|
101 | ##${h.end_form()} | |||
82 |
|
102 | |||
83 | <div class="edit-file-fieldset"> |
|
103 | <div class="file-upload-transaction-wrapper" style="display: none"> | |
84 |
|
|
104 | <div class="file-upload-transaction"> | |
85 | <div id="commit-message-label" class="commit-message-label left-label"> |
|
105 | <h3>${_('Commiting...')}</h3> | |
86 | ${_('Commit Message')}: |
|
106 | <p>${_('Please wait while the files are being uploaded')}</p> | |
87 | </div> |
|
107 | <p class="error" style="display: none"> | |
88 | <div class="right-content"> |
|
108 | ||
89 | <div class="message"> |
|
109 | </p> | |
90 | <textarea id="commit" name="message" placeholder="${c.default_message}"></textarea> |
|
110 | <i class="icon-spin animate-spin"></i> | |
91 | </div> |
|
111 | <p></p> | |
92 | </div> |
|
|||
93 | </div> |
|
|||
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")} |
|
|||
97 | </div> |
|
|||
98 | </div> |
|
112 | </div> | |
99 | ${h.end_form()} |
|
113 | </div> | |
|
114 | ||||
100 | </div> |
|
115 | </div> | |
|
116 | ||||
101 | <script type="text/javascript"> |
|
117 | <script type="text/javascript"> | |
102 |
|
118 | |||
103 |
$( |
|
119 | $(document).ready(function () { | |
104 | var button = $(this); |
|
|||
105 | if (button.hasClass('clicked')) { |
|
|||
106 | button.attr('disabled', true); |
|
|||
107 | } else { |
|
|||
108 | button.addClass('clicked'); |
|
|||
109 | } |
|
|||
110 | }); |
|
|||
111 |
|
||||
112 | var hide_upload = function(){ |
|
|||
113 | $('#files_data').show(); |
|
|||
114 | $('#upload_file_container').hide(); |
|
|||
115 | $('#filename_container').show(); |
|
|||
116 | }; |
|
|||
117 |
|
120 | |||
118 | $('#file_enable').on('click', function(e){ |
|
121 | //see: https://www.dropzonejs.com/#configuration | |
119 | e.preventDefault(); |
|
122 | myDropzone = new Dropzone("div#file-uploader", { | |
120 | hide_upload(); |
|
123 | url: "${form_url}", | |
121 | }); |
|
124 | headers: {"X-CSRF-Token": CSRF_TOKEN}, | |
122 |
|
125 | paramName: function () { | ||
123 | var renderer = ""; |
|
126 | return "files_upload" | |
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)}"; |
|
127 | }, // The name that will be used to transfer the file | |
125 | var myCodeMirror = initCodeMirror('editor', reset_url, false); |
|
128 | parallelUploads: 20, | |
126 |
|
129 | maxFiles: 20, | ||
127 | var modes_select = $('#set_mode'); |
|
130 | uploadMultiple: true, | |
128 | fillCodeMirrorOptions(modes_select); |
|
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) { | |||
129 |
|
141 | |||
130 | var filename_selector = '#filename'; |
|
142 | }); | |
131 | var callback = function(filename, mimetype, mode){ |
|
143 | ||
132 | CodeMirrorPreviewEnable(mode); |
|
144 | this.on("sending", function (file, xhr, formData) { | |
133 | }; |
|
145 | formData.append("message", $('#commit').val()); | |
134 | // on change of select field set mode |
|
146 | $(file.previewElement).find('.dz-sending').show(); | |
135 | setCodeMirrorModeFromSelect( |
|
147 | }); | |
136 | modes_select, filename_selector, myCodeMirror, callback); |
|
|||
137 |
|
148 | |||
138 | // on entering the new filename set mode, from given extension |
|
149 | this.on("success", function (file, response) { | |
139 | setCodeMirrorModeFromInput( |
|
150 | $(file.previewElement).find('.dz-sending').hide(); | |
140 | modes_select, filename_selector, myCodeMirror, callback); |
|
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 | |||
141 |
|
161 | |||
142 | // if the file is renderable set line wraps automatically |
|
162 | }); | |
143 | if (renderer !== ""){ |
|
163 | ||
144 | var line_wrap = 'on'; |
|
164 | this.on("error", function (file, errorMessage, xhr) { | |
145 | $($('#line_wrap option[value="'+line_wrap+'"]')[0]).attr("selected", "selected"); |
|
165 | var error = null; | |
146 | setCodeMirrorLineWrap(myCodeMirror, true); |
|
|||
147 | } |
|
|||
148 |
|
166 | |||
149 | // on select line wraps change the editor |
|
167 | if (xhr !== undefined){ | |
150 | $('#line_wrap').on('change', function(e){ |
|
168 | var httpStatus = xhr.status + " " + xhr.statusText; | |
151 | var selected = e.currentTarget; |
|
169 | if (xhr.status >= 500) { | |
152 | var line_wraps = {'on': true, 'off': false}[selected.value]; |
|
170 | error = httpStatus; | |
153 | setCodeMirrorLineWrap(myCodeMirror, line_wraps) |
|
171 | } | |
154 | }); |
|
172 | } | |
155 |
|
173 | |||
156 | // render preview/edit button |
|
174 | if (error === null) { | |
157 | $('#render_preview').on('click', function(e){ |
|
175 | error = errorMessage.error || errorMessage || httpStatus; | |
158 | if($(this).hasClass('preview')){ |
|
176 | } | |
159 | $(this).removeClass('preview'); |
|
177 | ||
160 | $(this).html("${_('Edit')}"); |
|
178 | $(file.previewElement).find('.dz-error-message').html('ERROR: {0}'.format(error)); | |
161 | $('#editor_preview').show(); |
|
179 | }); | |
162 | $(myCodeMirror.getWrapperElement()).hide(); |
|
180 | } | |
|
181 | }); | |||
163 |
|
182 | |||
164 | var possible_renderer = { |
|
183 | $('#commit_btn').on('click', function(e) { | |
165 | 'rst':'rst', |
|
184 | e.preventDefault(); | |
166 | 'markdown':'markdown', |
|
185 | var button = $(this); | |
167 | 'gfm': 'markdown'}[myCodeMirror.getMode().name]; |
|
186 | if (button.hasClass('clicked')) { | |
168 | var _text = myCodeMirror.getValue(); |
|
187 | button.attr('disabled', true); | |
169 | var _renderer = possible_renderer || DEFAULT_RENDERER; |
|
188 | } else { | |
170 | var post_data = {'text': _text, 'renderer': _renderer, 'csrf_token': CSRF_TOKEN}; |
|
189 | button.addClass('clicked'); | |
171 | $('#editor_preview').html(_gettext('Loading ...')); |
|
190 | } | |
172 | var url = pyroutes.url('repo_commit_comment_preview', |
|
|||
173 | {'repo_name': '${c.repo_name}', |
|
|||
174 | 'commit_id': '${c.commit.raw_id}'}); |
|
|||
175 |
|
191 | |||
176 | ajaxPOST(url, post_data, function(o){ |
|
192 | var files = myDropzone.getQueuedFiles(); | |
177 | $('#editor_preview').html(o); |
|
193 | if (files.length === 0) { | |
178 | }) |
|
194 | alert("Missing files"); | |
179 | } |
|
195 | e.preventDefault(); | |
180 |
|
|
196 | } | |
181 | $(this).addClass('preview'); |
|
197 | ||
182 | $(this).html("${_('Preview')}"); |
|
198 | $('.upload-form').hide(); | |
183 | $('#editor_preview').hide(); |
|
199 | $('.file-upload-transaction-wrapper').show(); | |
184 | $(myCodeMirror.getWrapperElement()).show(); |
|
200 | myDropzone.processQueue(); | |
185 | } |
|
201 | ||
|
202 | }); | |||
|
203 | ||||
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