diff --git a/rhodecode/apps/repository/__init__.py b/rhodecode/apps/repository/__init__.py --- a/rhodecode/apps/repository/__init__.py +++ b/rhodecode/apps/repository/__init__.py @@ -156,6 +156,10 @@ def includeme(config): pattern='/{repo_name:.*?[^/]}/authors/{commit_id}/{f_path:.*}', repo_route=True) config.add_route( + name='repo_files_check_head', + pattern='/{repo_name:.*?[^/]}/check_head/{commit_id}/{f_path:.*}', + repo_route=True) + config.add_route( name='repo_files_remove_file', pattern='/{repo_name:.*?[^/]}/remove_file/{commit_id}/{f_path:.*}', repo_route=True) diff --git a/rhodecode/apps/repository/views/repo_files.py b/rhodecode/apps/repository/views/repo_files.py --- a/rhodecode/apps/repository/views/repo_files.py +++ b/rhodecode/apps/repository/views/repo_files.py @@ -1114,6 +1114,42 @@ class RepoFilesView(RepoAppView): @LoginRequired() @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin') @view_config( + route_name='repo_files_check_head', request_method='POST', + renderer='json_ext', xhr=True) + def repo_files_check_head(self): + self.load_default_context() + + commit_id, f_path = self._get_commit_and_path() + _branch_name, _sha_commit_id, is_head = \ + self._is_valid_head(commit_id, self.rhodecode_vcs_repo) + + new_path = self.request.POST.get('path') + operation = self.request.POST.get('operation') + path_exist = '' + + if new_path and operation in ['create', 'upload']: + new_f_path = os.path.join(f_path.lstrip('/'), new_path) + try: + commit_obj = self.rhodecode_vcs_repo.get_commit(commit_id) + # NOTE(dan): construct whole path without leading / + file_node = commit_obj.get_node(new_f_path) + if file_node is not None: + path_exist = new_f_path + except EmptyRepositoryError: + pass + except Exception: + pass + + return { + 'branch': _branch_name, + 'sha': _sha_commit_id, + 'is_head': is_head, + 'path_exists': path_exist + } + + @LoginRequired() + @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin') + @view_config( route_name='repo_files_remove_file', request_method='GET', renderer='rhodecode:templates/files/files_delete.mako') def repo_files_remove_file(self): diff --git a/rhodecode/public/css/main.less b/rhodecode/public/css/main.less --- a/rhodecode/public/css/main.less +++ b/rhodecode/public/css/main.less @@ -2532,6 +2532,10 @@ h3.files_location{ white-space: nowrap; } + .upload-form { + margin-top: 46px; + } + .location-path { width: -moz-available; /* WebKit-based browsers will ignore this. */ width: -webkit-fill-available; /* Mozilla-based browsers will ignore this. */ diff --git a/rhodecode/public/js/rhodecode/routes.js b/rhodecode/public/js/rhodecode/routes.js --- a/rhodecode/public/js/rhodecode/routes.js +++ b/rhodecode/public/js/rhodecode/routes.js @@ -204,6 +204,7 @@ function registerRCRoutes() { pyroutes.register('repo_file_download:legacy', '/%(repo_name)s/rawfile/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']); pyroutes.register('repo_file_history', '/%(repo_name)s/history/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']); pyroutes.register('repo_file_authors', '/%(repo_name)s/authors/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']); + pyroutes.register('repo_files_check_head', '/%(repo_name)s/check_head/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']); pyroutes.register('repo_files_remove_file', '/%(repo_name)s/remove_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']); pyroutes.register('repo_files_delete_file', '/%(repo_name)s/delete_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']); pyroutes.register('repo_files_edit_file', '/%(repo_name)s/edit_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']); diff --git a/rhodecode/public/js/src/rhodecode/files.js b/rhodecode/public/js/src/rhodecode/files.js --- a/rhodecode/public/js/src/rhodecode/files.js +++ b/rhodecode/public/js/src/rhodecode/files.js @@ -521,3 +521,80 @@ var showAuthors = function(elem, annotat return FileEditor; }); + +var checkFileHead = function($editForm, commit_id, f_path, operation) { + function getFormData($form){ + var unindexed_array = $form.serializeArray(); + var indexed_array = {}; + + $.map(unindexed_array, function(n, i){ + indexed_array[n['name']] = n['value']; + }); + + return indexed_array; + } + + $editForm.on('submit', function (e) { + + var validHead = $editForm.find('#commit_btn').data('validHead'); + if (validHead === true){ + return true + } + + // no marker, we do "checks" + e.preventDefault(); + var formData = getFormData($editForm); + var new_path = formData['filename']; + + var success = function(data) { + + if (data['is_head'] === true && data['path_exists'] === "") { + + $editForm.find('#commit_btn').data('validHead', true); + $editForm.find('#commit_btn').val('Committing...'); + $editForm.submit(); + + } else { + var message = ''; + var urlTmpl = 'here'; + $editForm.find('#commit_btn').val('Commit aborted'); + + if (operation === 'edit') { + var new_url = urlTmpl.format(pyroutes.url('repo_files_edit_file', {"repo_name": templateContext.repo_name, "commit_id": data['branch'], "f_path": f_path})); + 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)); + } else if (operation === 'delete') { + var new_url = urlTmpl.format(pyroutes.url('repo_files_remove_file', {"repo_name": templateContext.repo_name, "commit_id": data['branch'], "f_path": f_path})); + 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)); + } else if (operation === 'create') { + if (data['path_exists'] !== "") { + message = _gettext('There is an existing path `{0}` at this commit.'.format(data['path_exists'])); + } else { + 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})); + message = _gettext('There is a later version of file tree available. Click {0} to create a file at the latest tree.'.format(new_url)); + } + } + + var payload = { + message: { + message: message, + level: 'warning', + force: true + } + }; + $.Topic('/notifications').publish(payload); + } + }; + + // lock button + $editForm.find('#commit_btn').attr('disabled', 'disabled'); + $editForm.find('#commit_btn').val('Checking commit...'); + + var postData = {'csrf_token': CSRF_TOKEN, 'path': new_path, 'operation': operation}; + ajaxPOST(pyroutes.url('repo_files_check_head', + {'repo_name': templateContext.repo_name, 'commit_id': commit_id, 'f_path': f_path}), + postData, success); + + return false + + }); +}; diff --git a/rhodecode/templates/files/files_add.mako b/rhodecode/templates/files/files_add.mako --- a/rhodecode/templates/files/files_add.mako +++ b/rhodecode/templates/files/files_add.mako @@ -41,7 +41,7 @@