Merge pull request '[PORT] Enable no-jquery/no-parse-html-literal and fix violation (gitea#31684)' (#4719) from gusted/forgejo-gt-31684 into forgejo

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/4719
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
This commit is contained in:
Gusted 2024-07-29 14:18:03 +00:00
commit 0851aca863
4 changed files with 44 additions and 13 deletions

View file

@ -460,7 +460,7 @@ rules:
no-jquery/no-param: [2] no-jquery/no-param: [2]
no-jquery/no-parent: [0] no-jquery/no-parent: [0]
no-jquery/no-parents: [2] no-jquery/no-parents: [2]
no-jquery/no-parse-html-literal: [0] no-jquery/no-parse-html-literal: [2]
no-jquery/no-parse-html: [2] no-jquery/no-parse-html: [2]
no-jquery/no-parse-json: [2] no-jquery/no-parse-json: [2]
no-jquery/no-parse-xml: [2] no-jquery/no-parse-xml: [2]

View file

@ -1,7 +1,7 @@
import $ from 'jquery'; import $ from 'jquery';
import {htmlEscape} from 'escape-goat'; import {htmlEscape} from 'escape-goat';
import {createCodeEditor} from './codeeditor.js'; import {createCodeEditor} from './codeeditor.js';
import {hideElem, showElem} from '../utils/dom.js'; import {hideElem, showElem, createElementFromHTML} from '../utils/dom.js';
import {initMarkupContent} from '../markup/content.js'; import {initMarkupContent} from '../markup/content.js';
import {attachRefIssueContextPopup} from './contextpopup.js'; import {attachRefIssueContextPopup} from './contextpopup.js';
import {POST} from '../modules/fetch.js'; import {POST} from '../modules/fetch.js';
@ -9,7 +9,9 @@ import {POST} from '../modules/fetch.js';
function initEditPreviewTab($form) { function initEditPreviewTab($form) {
const $tabMenu = $form.find('.tabular.menu'); const $tabMenu = $form.find('.tabular.menu');
$tabMenu.find('.item').tab(); $tabMenu.find('.item').tab();
const $previewTab = $tabMenu.find(`.item[data-tab="${$tabMenu.data('preview')}"]`); const $previewTab = $tabMenu.find(
`.item[data-tab="${$tabMenu.data('preview')}"]`,
);
if ($previewTab.length) { if ($previewTab.length) {
$previewTab.on('click', async function () { $previewTab.on('click', async function () {
const $this = $(this); const $this = $(this);
@ -24,12 +26,17 @@ function initEditPreviewTab($form) {
const formData = new FormData(); const formData = new FormData();
formData.append('mode', mode); formData.append('mode', mode);
formData.append('context', context); formData.append('context', context);
formData.append('text', $form.find(`.tab[data-tab="${$tabMenu.data('write')}"] textarea`).val()); formData.append(
'text',
$form.find(`.tab[data-tab="${$tabMenu.data('write')}"] textarea`).val(),
);
formData.append('file_path', $treePathEl.val()); formData.append('file_path', $treePathEl.val());
try { try {
const response = await POST($this.data('url'), {data: formData}); const response = await POST($this.data('url'), {data: formData});
const data = await response.text(); const data = await response.text();
const $previewPanel = $form.find(`.tab[data-tab="${$tabMenu.data('preview')}"]`); const $previewPanel = $form.find(
`.tab[data-tab="${$tabMenu.data('preview')}"]`,
);
renderPreviewPanelContent($previewPanel, data); renderPreviewPanelContent($previewPanel, data);
} catch (error) { } catch (error) {
console.error('Error:', error); console.error('Error:', error);
@ -96,8 +103,14 @@ export function initRepoEditor() {
const value = parts[i]; const value = parts[i];
if (i < parts.length - 1) { if (i < parts.length - 1) {
if (value.length) { if (value.length) {
$(`<span class="section"><a href="#">${htmlEscape(value)}</a></span>`).insertBefore($(this)); $editFilename[0].before(
$('<div class="breadcrumb-divider">/</div>').insertBefore($(this)); createElementFromHTML(
`<span class="section"><a href="#">${htmlEscape(value)}</a></span>`,
),
);
$editFilename[0].before(
createElementFromHTML(`<div class="breadcrumb-divider">/</div>`),
);
} }
} else { } else {
$(this).val(value); $(this).val(value);
@ -113,7 +126,11 @@ export function initRepoEditor() {
const $section = $('.breadcrumb span.section'); const $section = $('.breadcrumb span.section');
// Jump back to last directory once the filename is empty // Jump back to last directory once the filename is empty
if (e.code === 'Backspace' && getCursorPosition($(this)) === 0 && $section.length > 0) { if (
e.code === 'Backspace' &&
getCursorPosition($(this)) === 0 &&
$section.length > 0
) {
e.preventDefault(); e.preventDefault();
const $divider = $('.breadcrumb .breadcrumb-divider'); const $divider = $('.breadcrumb .breadcrumb-divider');
const value = $section.last().find('a').text(); const value = $section.last().find('a').text();
@ -164,11 +181,13 @@ export function initRepoEditor() {
commitButton?.addEventListener('click', (e) => { commitButton?.addEventListener('click', (e) => {
// A modal which asks if an empty file should be committed // A modal which asks if an empty file should be committed
if (!$editArea.val()) { if (!$editArea.val()) {
$('#edit-empty-content-modal').modal({ $('#edit-empty-content-modal')
onApprove() { .modal({
$('.edit.form').trigger('submit'); onApprove() {
}, $('.edit.form').trigger('submit');
}).modal('show'); },
})
.modal('show');
e.preventDefault(); e.preventDefault();
} }
}); });

View file

@ -296,3 +296,10 @@ export function replaceTextareaSelection(textarea, text) {
textarea.dispatchEvent(new CustomEvent('change', {bubbles: true, cancelable: true})); textarea.dispatchEvent(new CustomEvent('change', {bubbles: true, cancelable: true}));
} }
} }
// Warning: Do not enter any unsanitized variables here
export function createElementFromHTML(htmlString) {
const div = document.createElement('div');
div.innerHTML = htmlString.trim();
return div.firstChild;
}

View file

@ -0,0 +1,5 @@
import {createElementFromHTML} from './dom.js';
test('createElementFromHTML', () => {
expect(createElementFromHTML('<a>foo<span>bar</span></a>').outerHTML).toEqual('<a>foo<span>bar</span></a>');
});