diff --git a/routers/web/repo/editor.go b/routers/web/repo/editor.go index 4ff86b5a6647..9396115b0d24 100644 --- a/routers/web/repo/editor.go +++ b/routers/web/repo/editor.go @@ -317,7 +317,7 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b case git.EntryModeBlob: ctx.RenderWithErr(ctx.Tr("repo.editor.directory_is_a_file", fileErr.Path), tplEditFile, &form) default: - ctx.Error(http.StatusInternalServerError, err.Error()) + ctx.RenderWithErr(ctx.Tr("repo.editor.filename_is_invalid", fileErr.Path), tplEditFile, &form) } } else { ctx.Error(http.StatusInternalServerError, err.Error()) diff --git a/web_src/js/features/repo-editor.ts b/web_src/js/features/repo-editor.ts index dadbd802bc7f..e4d179d3aeab 100644 --- a/web_src/js/features/repo-editor.ts +++ b/web_src/js/features/repo-editor.ts @@ -75,23 +75,62 @@ export function initRepoEditor() { } filenameInput.addEventListener('input', function () { const parts = filenameInput.value.split('/'); + const links = Array.from(document.querySelectorAll('.breadcrumb span.section')); + const dividers = Array.from(document.querySelectorAll('.breadcrumb .breadcrumb-divider')); + let warningDiv = document.querySelector('.ui.warning.message.flash-message.flash-warning.space-related'); + let containSpace = false; if (parts.length > 1) { for (let i = 0; i < parts.length; ++i) { const value = parts[i]; + const trimValue = value.trim(); + if (trimValue === '..') { + // remove previous tree path + if (links.length > 0) { + const link = links.pop(); + const divider = dividers.pop(); + link.remove(); + divider.remove(); + } + continue; + } if (i < parts.length - 1) { - if (value.length) { - filenameInput.before(createElementFromHTML( + if (trimValue.length) { + const linkElement = createElementFromHTML( `${htmlEscape(value)}`, - )); - filenameInput.before(createElementFromHTML( + ); + const dividerElement = createElementFromHTML( ``, - )); + ); + links.push(linkElement); + dividers.push(dividerElement); + filenameInput.before(linkElement); + filenameInput.before(dividerElement); } } else { filenameInput.value = value; } this.setSelectionRange(0, 0); + containSpace |= (trimValue !== value && trimValue !== ''); + } + } + containSpace |= Array.from(links).some((link) => { + const value = link.querySelector('a').textContent; + return value.trim() !== value; + }); + containSpace |= parts[parts.length - 1].trim() !== parts[parts.length - 1]; + if (containSpace) { + if (!warningDiv) { + warningDiv = document.createElement('div'); + warningDiv.classList.add('ui', 'warning', 'message', 'flash-message', 'flash-warning', 'space-related'); + warningDiv.innerHTML = '

File path contains leading or trailing whitespace.

'; + // Add display 'block' because display is set to 'none' in formantic\build\semantic.css + warningDiv.style.display = 'block'; + const inputContainer = document.querySelector('.repo-editor-header'); + inputContainer.insertAdjacentElement('beforebegin', warningDiv); } + showElem(warningDiv); + } else if (warningDiv) { + hideElem(warningDiv); } joinTreePath(); });