files: add pre-commit checks on file operations to prevent loosing content while editing when repositories change....
dan -
r4302:4c157b04 default
Not Reviewed
Show More
Add another comment
TODOs: 0 unresolved 0 Resolved
COMMENTS: 0 General 0 Inline
@@ -156,6 +156,10
156 pattern='/{repo_name:.*?[^/]}/authors/{commit_id}/{f_path:.*}', repo_route=True)
156 pattern='/{repo_name:.*?[^/]}/authors/{commit_id}/{f_path:.*}', repo_route=True)
157
157
158 config.add_route(
158 config.add_route(
159 name='repo_files_check_head',
160 pattern='/{repo_name:.*?[^/]}/check_head/{commit_id}/{f_path:.*}',
161 repo_route=True)
162 config.add_route(
159 name='repo_files_remove_file',
163 name='repo_files_remove_file',
160 pattern='/{repo_name:.*?[^/]}/remove_file/{commit_id}/{f_path:.*}',
164 pattern='/{repo_name:.*?[^/]}/remove_file/{commit_id}/{f_path:.*}',
161 repo_route=True)
165 repo_route=True)
@@ -1114,6 +1114,42
1114 @LoginRequired()
1114 @LoginRequired()
1115 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1115 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1116 @view_config(
1116 @view_config(
1117 route_name='repo_files_check_head', request_method='POST',
1118 renderer='json_ext', xhr=True)
1119 def repo_files_check_head(self):
1120 self.load_default_context()
1121
1122 commit_id, f_path = self._get_commit_and_path()
1123 _branch_name, _sha_commit_id, is_head = \
1124 self._is_valid_head(commit_id, self.rhodecode_vcs_repo)
1125
1126 new_path = self.request.POST.get('path')
1127 operation = self.request.POST.get('operation')
1128 path_exist = ''
1129
1130 if new_path and operation in ['create', 'upload']:
1131 new_f_path = os.path.join(f_path.lstrip('/'), new_path)
1132 try:
1133 commit_obj = self.rhodecode_vcs_repo.get_commit(commit_id)
1134 # NOTE(dan): construct whole path without leading /
1135 file_node = commit_obj.get_node(new_f_path)
1136 if file_node is not None:
1137 path_exist = new_f_path
1138 except EmptyRepositoryError:
1139 pass
1140 except Exception:
1141 pass
1142
1143 return {
1144 'branch': _branch_name,
1145 'sha': _sha_commit_id,
1146 'is_head': is_head,
1147 'path_exists': path_exist
1148 }
1149
1150 @LoginRequired()
1151 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1152 @view_config(
1117 route_name='repo_files_remove_file', request_method='GET',
1153 route_name='repo_files_remove_file', request_method='GET',
1118 renderer='rhodecode:templates/files/files_delete.mako')
1154 renderer='rhodecode:templates/files/files_delete.mako')
1119 def repo_files_remove_file(self):
1155 def repo_files_remove_file(self):
@@ -2532,6 +2532,10
2532 white-space: nowrap;
2532 white-space: nowrap;
2533 }
2533 }
2534
2534
2535 .upload-form {
2536 margin-top: 46px;
2537 }
2538
2535 .location-path {
2539 .location-path {
2536 width: -moz-available; /* WebKit-based browsers will ignore this. */
2540 width: -moz-available; /* WebKit-based browsers will ignore this. */
2537 width: -webkit-fill-available; /* Mozilla-based browsers will ignore this. */
2541 width: -webkit-fill-available; /* Mozilla-based browsers will ignore this. */
@@ -204,6 +204,7
204 pyroutes.register('repo_file_download:legacy', '/%(repo_name)s/rawfile/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
204 pyroutes.register('repo_file_download:legacy', '/%(repo_name)s/rawfile/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
205 pyroutes.register('repo_file_history', '/%(repo_name)s/history/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
205 pyroutes.register('repo_file_history', '/%(repo_name)s/history/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
206 pyroutes.register('repo_file_authors', '/%(repo_name)s/authors/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
206 pyroutes.register('repo_file_authors', '/%(repo_name)s/authors/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
207 pyroutes.register('repo_files_check_head', '/%(repo_name)s/check_head/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
207 pyroutes.register('repo_files_remove_file', '/%(repo_name)s/remove_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
208 pyroutes.register('repo_files_remove_file', '/%(repo_name)s/remove_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
208 pyroutes.register('repo_files_delete_file', '/%(repo_name)s/delete_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
209 pyroutes.register('repo_files_delete_file', '/%(repo_name)s/delete_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
209 pyroutes.register('repo_files_edit_file', '/%(repo_name)s/edit_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
210 pyroutes.register('repo_files_edit_file', '/%(repo_name)s/edit_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
@@ -521,3 +521,80
521 return FileEditor;
521 return FileEditor;
522 });
522 });
523
523
524
525 var checkFileHead = function($editForm, commit_id, f_path, operation) {
526 function getFormData($form){
527 var unindexed_array = $form.serializeArray();
528 var indexed_array = {};
529
530 $.map(unindexed_array, function(n, i){
531 indexed_array[n['name']] = n['value'];
532 });
533
534 return indexed_array;
535 }
536
537 $editForm.on('submit', function (e) {
538
539 var validHead = $editForm.find('#commit_btn').data('validHead');
540 if (validHead === true){
541 return true
542 }
543
544 // no marker, we do "checks"
545 e.preventDefault();
546 var formData = getFormData($editForm);
547 var new_path = formData['filename'];
548
549 var success = function(data) {
550
551 if (data['is_head'] === true && data['path_exists'] === "") {
552
553 $editForm.find('#commit_btn').data('validHead', true);
554 $editForm.find('#commit_btn').val('Committing...');
555 $editForm.submit();
556
557 } else {
558 var message = '';
559 var urlTmpl = '<a target="_blank" href="{0}">here</a>';
560 $editForm.find('#commit_btn').val('Commit aborted');
561
562 if (operation === 'edit') {
563 var new_url = urlTmpl.format(pyroutes.url('repo_files_edit_file', {"repo_name": templateContext.repo_name, "commit_id": data['branch'], "f_path": f_path}));
564 message = _gettext('File `{0}` has a newer version available, or has been removed. Click {1} to see the latest version.'.format(f_path, new_url));
565 } else if (operation === 'delete') {
566 var new_url = urlTmpl.format(pyroutes.url('repo_files_remove_file', {"repo_name": templateContext.repo_name, "commit_id": data['branch'], "f_path": f_path}));
567 message = _gettext('File `{0}` has a newer version available, or has been removed. Click {1} to see the latest version.'.format(f_path, new_url));
568 } else if (operation === 'create') {
569 if (data['path_exists'] !== "") {
570 message = _gettext('There is an existing path `{0}` at this commit.'.format(data['path_exists']));
571 } else {
572 var new_url = urlTmpl.format(pyroutes.url('repo_files_add_file', {"repo_name": templateContext.repo_name, "commit_id": data['branch'], "f_path": f_path, "filename": new_path}));
573 message = _gettext('There is a later version of file tree available. Click {0} to create a file at the latest tree.'.format(new_url));
574 }
575 }
576
577 var payload = {
578 message: {
579 message: message,
580 level: 'warning',
581 force: true
582 }
583 };
584 $.Topic('/notifications').publish(payload);
585 }
586 };
587
588 // lock button
589 $editForm.find('#commit_btn').attr('disabled', 'disabled');
590 $editForm.find('#commit_btn').val('Checking commit...');
591
592 var postData = {'csrf_token': CSRF_TOKEN, 'path': new_path, 'operation': operation};
593 ajaxPOST(pyroutes.url('repo_files_check_head',
594 {'repo_name': templateContext.repo_name, 'commit_id': commit_id, 'f_path': f_path}),
595 postData, success);
596
597 return false
598
599 });
600 };
@@ -41,7 +41,7
41 </div>
41 </div>
42 </li>
42 </li>
43 <li class="location-path">
43 <li class="location-path">
44 <input class="file-name-input input-small" type="text" value="" name="filename" id="filename" placeholder="${_('Filename e.g example.py, or docs/readme.md')}">
44 <input class="file-name-input input-small" type="text" value="${request.GET.get('filename')}" name="filename" id="filename" placeholder="${_('Filename e.g example.py, or docs/readme.md')}">
45 </li>
45 </li>
46 </ul>
46 </ul>
47 </div>
47 </div>
@@ -50,7 +50,6
50
50
51 <div class="table">
51 <div class="table">
52 <div>
52 <div>
53
54 <div id="codeblock" class="codeblock">
53 <div id="codeblock" class="codeblock">
55 <div class="editor-items">
54 <div class="editor-items">
56 <div class="editor-action active show-editor pull-left" onclick="fileEditor.showEditor(); return false">
55 <div class="editor-action active show-editor pull-left" onclick="fileEditor.showEditor(); return false">
@@ -108,6 +107,10
108
107
109 $('#filename').focus();
108 $('#filename').focus();
110
109
110 var commit_id = "${c.commit.raw_id}";
111 var f_path = "${c.f_path}";
112
113 checkFileHead($('#eform'), commit_id, f_path, 'create')
111 });
114 });
112
115
113 </script>
116 </script>
@@ -69,7 +69,7
69 </div>
69 </div>
70 </div>
70 </div>
71 <div class="pull-left">
71 <div class="pull-left">
72 ${h.submit('commit',_('Commit changes'),class_="btn btn-small btn-danger-action")}
72 ${h.submit('commit_btn',_('Commit changes'),class_="btn btn-small btn-danger-action")}
73 </div>
73 </div>
74 </div>
74 </div>
75 ${h.end_form()}
75 ${h.end_form()}
@@ -82,6 +82,10
82
82
83 fileEditor = new FileEditor('#editor');
83 fileEditor = new FileEditor('#editor');
84
84
85 var commit_id = "${c.commit.raw_id}";
86 var f_path = "${c.f_path}";
87
88 checkFileHead($('#eform'), commit_id, f_path, 'delete');
85 });
89 });
86
90
87 </script>
91 </script>
@@ -117,6 +117,11
117 // on entering the new filename set mode, from given extension
117 // on entering the new filename set mode, from given extension
118 setCodeMirrorModeFromInput(modes_select, filename_selector, fileEditor.cm, null);
118 setCodeMirrorModeFromInput(modes_select, filename_selector, fileEditor.cm, null);
119
119
120 var commit_id = "${c.commit.raw_id}";
121 var f_path = "${c.f_path}";
122
123 checkFileHead($('#eform'), commit_id, f_path, 'edit')
124
120 });
125 });
121
126
122
127
@@ -106,7 +106,7
106
106
107 <div class="file-upload-transaction-wrapper" style="display: none">
107 <div class="file-upload-transaction-wrapper" style="display: none">
108 <div class="file-upload-transaction">
108 <div class="file-upload-transaction">
109 <h3>${_('Commiting...')}</h3>
109 <h3>${_('Committing...')}</h3>
110 <p>${_('Please wait while the files are being uploaded')}</p>
110 <p>${_('Please wait while the files are being uploaded')}</p>
111 <p class="error" style="display: none">
111 <p class="error" style="display: none">
112
112
Comments 0
You need to be logged in to leave comments. Login now