From 3d369bb014d7dff01677c72a71ff5b46d6c5f925 Mon Sep 17 00:00:00 2001 From: wxiaoguang <wxiaoguang@gmail.com> Date: Wed, 2 Mar 2022 19:00:26 +0800 Subject: [PATCH 01/11] Show messages for users if the ROOT_URL is wrong --- templates/base/footer.tmpl | 2 +- templates/base/head.tmpl | 1 + web_src/js/features/common-global.js | 27 ++++++++++++++++++++++++++- web_src/js/index.js | 3 +++ 4 files changed, 31 insertions(+), 2 deletions(-) diff --git a/templates/base/footer.tmpl b/templates/base/footer.tmpl index 1aabfa2f5c644..9bf16f8aa5b55 100644 --- a/templates/base/footer.tmpl +++ b/templates/base/footer.tmpl @@ -22,7 +22,7 @@ <script src='https://hcaptcha.com/1/api.js' async></script> {{end}} {{end}} - <script src="{{AssetUrlPrefix}}/js/index.js?v={{MD5 AppVer}}"></script> + <script src="{{AssetUrlPrefix}}/js/index.js?v={{MD5 AppVer}}" onerror="alert('Failed to load asset files from ' + this.src + ', please make sure the asset files can be accessed and the ROOT_URL setting in app.ini is correct.')"></script> {{template "custom/footer" .}} </body> </html> diff --git a/templates/base/head.tmpl b/templates/base/head.tmpl index 32e206a95d58e..8c2f10051a4ad 100644 --- a/templates/base/head.tmpl +++ b/templates/base/head.tmpl @@ -19,6 +19,7 @@ <!-- /* eslint-disable */ --> window.config = { appVer: '{{AppVer}}', + appUrl: '{{AppUrl}}', appSubUrl: '{{AppSubUrl}}', assetUrlPrefix: '{{AssetUrlPrefix}}', runModeIsProd: {{.RunModeIsProd}}, diff --git a/web_src/js/features/common-global.js b/web_src/js/features/common-global.js index a9baf9be0c683..eab213a395396 100644 --- a/web_src/js/features/common-global.js +++ b/web_src/js/features/common-global.js @@ -3,8 +3,9 @@ import 'jquery.are-you-sure'; import {mqBinarySearch} from '../utils.js'; import createDropzone from './dropzone.js'; import {initCompColorPicker} from './comp/ColorPicker.js'; +import {htmlEscape} from 'escape-goat'; -const {csrfToken} = window.config; +const {appUrl, csrfToken} = window.config; export function initGlobalFormDirtyLeaveConfirm() { // Warn users that try to leave a page after entering data into a form. @@ -343,3 +344,27 @@ export function initGlobalButtons() { }); }); } + +/** + * Too many users set their ROOT_URL to wrong value, and it causes a lot of problems: + * * Cross-origin API request without correct cookie + * * Incorrect href in <a> + * * ... + * So we check whether current URL starts with AppUrl(ROOT_URL). + * If they don't match, show a warning to users. + */ +export function checkAppUrl() { + const curUrl = window.location.href; + if (curUrl.startsWith(appUrl)) { + return; + } + const $pageContent = $('.page-content'); + if (!$pageContent.length) { + return; + } + const $tip = $(`<div class="ui container negative message center aligned"> + Your ROOT_URL in app.ini is ${htmlEscape(appUrl)} but you are visiting ${htmlEscape(curUrl)}<br /> + You should set ROOT_URL correctly, otherwise the web may not work correctly. + </div>`); + $($pageContent[0]).prepend($tip); +} diff --git a/web_src/js/index.js b/web_src/js/index.js index b7eba5e6649a4..64c485d283219 100644 --- a/web_src/js/index.js +++ b/web_src/js/index.js @@ -39,6 +39,7 @@ import { } from './features/repo-issue.js'; import {initRepoEllipsisButton, initRepoCommitLastCommitLoader} from './features/repo-commit.js'; import { + checkAppUrl, initFootLanguageMenu, initGlobalButtonClickOnEnter, initGlobalButtons, @@ -169,4 +170,6 @@ $(document).ready(() => { initUserAuthWebAuthn(); initUserAuthWebAuthnRegister(); initUserSettings(); + + checkAppUrl(); }); From 94e6862d0519a19a60f437e9d95668425cf5f40f Mon Sep 17 00:00:00 2001 From: wxiaoguang <wxiaoguang@gmail.com> Date: Tue, 29 Mar 2022 16:45:58 +0800 Subject: [PATCH 02/11] add global error handler --- templates/base/head.tmpl | 1 + web_src/js/features/common-global.js | 33 ++++++++++++++++++++-------- web_src/js/index.js | 3 ++- 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/templates/base/head.tmpl b/templates/base/head.tmpl index 8c2f10051a4ad..4b294a6a2c37c 100644 --- a/templates/base/head.tmpl +++ b/templates/base/head.tmpl @@ -17,6 +17,7 @@ {{end}} <script> <!-- /* eslint-disable */ --> + window.addEventListener('error', function(e) {window._globalHandlerErrors = window._globalHandlerErrors||[]; window._globalHandlerErrors.push(e);}); window.config = { appVer: '{{AppVer}}', appUrl: '{{AppUrl}}', diff --git a/web_src/js/features/common-global.js b/web_src/js/features/common-global.js index eab213a395396..64143934aecd1 100644 --- a/web_src/js/features/common-global.js +++ b/web_src/js/features/common-global.js @@ -345,6 +345,26 @@ export function initGlobalButtons() { }); } +function showErrorMessageHtml(msgHtml) { + const $pageContent = $('.page-content'); + if (!$pageContent.length) { + return; + } + const $tip = $(`<div class="ui container negative message center aligned">${msgHtml}</div>`); + $($pageContent[0]).prepend($tip); +} + +function processWindowErrorEvent(e) { + showErrorMessageHtml(`JavaScript error: ${e.message} (${e.filename} @ ${e.lineno}:${e.colno}). Open browser console to see more details.`); +} + +export function initGlobalErrorHandler() { + for (const e of window._globalHandlerErrors || []) { + processWindowErrorEvent(e); + } + window._globalHandlerErrors = {'push': (e) => processWindowErrorEvent(e)}; +} + /** * Too many users set their ROOT_URL to wrong value, and it causes a lot of problems: * * Cross-origin API request without correct cookie @@ -358,13 +378,8 @@ export function checkAppUrl() { if (curUrl.startsWith(appUrl)) { return; } - const $pageContent = $('.page-content'); - if (!$pageContent.length) { - return; - } - const $tip = $(`<div class="ui container negative message center aligned"> - Your ROOT_URL in app.ini is ${htmlEscape(appUrl)} but you are visiting ${htmlEscape(curUrl)}<br /> - You should set ROOT_URL correctly, otherwise the web may not work correctly. - </div>`); - $($pageContent[0]).prepend($tip); + showErrorMessageHtml(` +Your ROOT_URL in app.ini is ${htmlEscape(appUrl)} but you are visiting ${htmlEscape(curUrl)}<br /> +You should set ROOT_URL correctly, otherwise the web may not work correctly. +`); } diff --git a/web_src/js/index.js b/web_src/js/index.js index 64c485d283219..60613c8b2231a 100644 --- a/web_src/js/index.js +++ b/web_src/js/index.js @@ -45,7 +45,7 @@ import { initGlobalButtons, initGlobalCommon, initGlobalDropzone, - initGlobalEnterQuickSubmit, + initGlobalEnterQuickSubmit, initGlobalErrorHandler, initGlobalFormDirtyLeaveConfirm, initGlobalLinkActions, initHeadNavbarContentToggle, @@ -85,6 +85,7 @@ $.fn.checkbox.settings.enableEnterKey = false; initVueEnv(); $(document).ready(() => { + initGlobalErrorHandler(); initGlobalCommon(); initGlobalButtonClickOnEnter(); From 223900ef35578d56b392a9a013f864debd686e14 Mon Sep 17 00:00:00 2001 From: wxiaoguang <wxiaoguang@gmail.com> Date: Tue, 29 Mar 2022 17:04:18 +0800 Subject: [PATCH 03/11] add comments --- templates/base/head.tmpl | 2 +- web_src/js/features/common-global.js | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/templates/base/head.tmpl b/templates/base/head.tmpl index b7d4917e44b1c..8101f9aa6b754 100644 --- a/templates/base/head.tmpl +++ b/templates/base/head.tmpl @@ -21,7 +21,7 @@ {{end}} <script> <!-- /* eslint-disable */ --> - window.addEventListener('error', function(e) {window._globalHandlerErrors = window._globalHandlerErrors||[]; window._globalHandlerErrors.push(e);}); + window.addEventListener('error', function(e) {window._globalHandlerErrors=window._globalHandlerErrors||[]; window._globalHandlerErrors.push(e);}); window.config = { appVer: '{{AppVer}}', appUrl: '{{AppUrl}}', diff --git a/web_src/js/features/common-global.js b/web_src/js/features/common-global.js index 64143934aecd1..09bf2d3fb11d6 100644 --- a/web_src/js/features/common-global.js +++ b/web_src/js/features/common-global.js @@ -354,14 +354,21 @@ function showErrorMessageHtml(msgHtml) { $($pageContent[0]).prepend($tip); } +/** + * @param {ErrorEvent} e + */ function processWindowErrorEvent(e) { showErrorMessageHtml(`JavaScript error: ${e.message} (${e.filename} @ ${e.lineno}:${e.colno}). Open browser console to see more details.`); } export function initGlobalErrorHandler() { + // we added an event handler for window error at the very beginning of head.tmpl + // the handler calls `_globalHandlerErrors.push` (array method) to record all errors occur before this init + // then in this init, we can collect all error events and show them for (const e of window._globalHandlerErrors || []) { processWindowErrorEvent(e); } + // then, change _globalHandlerErrors to an object with push method, to process further error events directly window._globalHandlerErrors = {'push': (e) => processWindowErrorEvent(e)}; } From 7228a3a86b84cd12324d45295365a50628f879a9 Mon Sep 17 00:00:00 2001 From: wxiaoguang <wxiaoguang@gmail.com> Date: Tue, 29 Mar 2022 17:15:26 +0800 Subject: [PATCH 04/11] use specialized CSS class "js-global-error", end users still have a chance to hide error messages by customized CSS styles. --- web_src/js/features/common-global.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/web_src/js/features/common-global.js b/web_src/js/features/common-global.js index 09bf2d3fb11d6..6acaf1214706f 100644 --- a/web_src/js/features/common-global.js +++ b/web_src/js/features/common-global.js @@ -345,12 +345,13 @@ export function initGlobalButtons() { }); } -function showErrorMessageHtml(msgHtml) { +function showGlobalErrorMessageHtml(msgHtml) { const $pageContent = $('.page-content'); if (!$pageContent.length) { return; } - const $tip = $(`<div class="ui container negative message center aligned">${msgHtml}</div>`); + // here we use a specialized CSS class "js-global-error", then end users still have a chance to hide these error messages by customized CSS styles. + const $tip = $(`<div class="ui container negative message center aligned js-global-error">${msgHtml}</div>`); $($pageContent[0]).prepend($tip); } @@ -358,7 +359,7 @@ function showErrorMessageHtml(msgHtml) { * @param {ErrorEvent} e */ function processWindowErrorEvent(e) { - showErrorMessageHtml(`JavaScript error: ${e.message} (${e.filename} @ ${e.lineno}:${e.colno}). Open browser console to see more details.`); + showGlobalErrorMessageHtml(`JavaScript error: ${e.message} (${e.filename} @ ${e.lineno}:${e.colno}). Open browser console to see more details.`); } export function initGlobalErrorHandler() { @@ -385,7 +386,7 @@ export function checkAppUrl() { if (curUrl.startsWith(appUrl)) { return; } - showErrorMessageHtml(` + showGlobalErrorMessageHtml(` Your ROOT_URL in app.ini is ${htmlEscape(appUrl)} but you are visiting ${htmlEscape(curUrl)}<br /> You should set ROOT_URL correctly, otherwise the web may not work correctly. `); From 8f42b72d7a835b744e43f296169994182a02f905 Mon Sep 17 00:00:00 2001 From: wxiaoguang <wxiaoguang@gmail.com> Date: Tue, 29 Mar 2022 18:00:16 +0800 Subject: [PATCH 05/11] make sure error handler can always be executed --- web_src/js/bootstrap.js | 40 ++++++++++++++++++++++++++++ web_src/js/features/common-global.js | 29 +------------------- web_src/js/index.js | 7 +++-- web_src/js/publicpath.js | 6 ----- 4 files changed, 44 insertions(+), 38 deletions(-) create mode 100644 web_src/js/bootstrap.js delete mode 100644 web_src/js/publicpath.js diff --git a/web_src/js/bootstrap.js b/web_src/js/bootstrap.js new file mode 100644 index 0000000000000..b8db8b8290015 --- /dev/null +++ b/web_src/js/bootstrap.js @@ -0,0 +1,40 @@ +import {joinPaths} from './utils.js'; + +// DO NOT IMPORT window.config HERE! +// to make sure the error handler always works, we should never import `window.config`, because some user's custom template breaks it. + +// This sets up the URL prefix used in webpack's chunk loading. +// This file must be imported before any lazy-loading is being attempted. +__webpack_public_path__ = joinPaths(window?.config?.assetUrlPrefix ?? '/', '/'); + +export function showGlobalErrorMessageHtml(msgHtml) { + const pageContent = document.querySelector('.page-content'); + if (!pageContent) return; + const el = document.createElement('div'); + el.innerHTML = `<div class="ui container negative message center aligned js-global-error">${msgHtml}</div>`; + pageContent.prepend(el.childNodes[0]); +} + +/** + * @param {ErrorEvent} e + */ +function processWindowErrorEvent(e) { + showGlobalErrorMessageHtml(`JavaScript error: ${e.message} (${e.filename} @ ${e.lineno}:${e.colno}). Open browser console to see more details.`); +} + +function initGlobalErrorHandler() { + if (!window.config) { + showGlobalErrorMessageHtml(`Gitea JavaScript code couldn't run correctly, please check your custom templates`); + } + + // we added an event handler for window error at the very beginning of head.tmpl + // the handler calls `_globalHandlerErrors.push` (array method) to record all errors occur before this init + // then in this init, we can collect all error events and show them + for (const e of window._globalHandlerErrors || []) { + processWindowErrorEvent(e); + } + // then, change _globalHandlerErrors to an object with push method, to process further error events directly + window._globalHandlerErrors = {'push': (e) => processWindowErrorEvent(e)}; +} + +initGlobalErrorHandler(); diff --git a/web_src/js/features/common-global.js b/web_src/js/features/common-global.js index 6acaf1214706f..240bd78685149 100644 --- a/web_src/js/features/common-global.js +++ b/web_src/js/features/common-global.js @@ -4,6 +4,7 @@ import {mqBinarySearch} from '../utils.js'; import createDropzone from './dropzone.js'; import {initCompColorPicker} from './comp/ColorPicker.js'; import {htmlEscape} from 'escape-goat'; +import {showGlobalErrorMessageHtml} from '../bootstrap.js'; const {appUrl, csrfToken} = window.config; @@ -345,34 +346,6 @@ export function initGlobalButtons() { }); } -function showGlobalErrorMessageHtml(msgHtml) { - const $pageContent = $('.page-content'); - if (!$pageContent.length) { - return; - } - // here we use a specialized CSS class "js-global-error", then end users still have a chance to hide these error messages by customized CSS styles. - const $tip = $(`<div class="ui container negative message center aligned js-global-error">${msgHtml}</div>`); - $($pageContent[0]).prepend($tip); -} - -/** - * @param {ErrorEvent} e - */ -function processWindowErrorEvent(e) { - showGlobalErrorMessageHtml(`JavaScript error: ${e.message} (${e.filename} @ ${e.lineno}:${e.colno}). Open browser console to see more details.`); -} - -export function initGlobalErrorHandler() { - // we added an event handler for window error at the very beginning of head.tmpl - // the handler calls `_globalHandlerErrors.push` (array method) to record all errors occur before this init - // then in this init, we can collect all error events and show them - for (const e of window._globalHandlerErrors || []) { - processWindowErrorEvent(e); - } - // then, change _globalHandlerErrors to an object with push method, to process further error events directly - window._globalHandlerErrors = {'push': (e) => processWindowErrorEvent(e)}; -} - /** * Too many users set their ROOT_URL to wrong value, and it causes a lot of problems: * * Cross-origin API request without correct cookie diff --git a/web_src/js/index.js b/web_src/js/index.js index 60613c8b2231a..18b949e4e62f7 100644 --- a/web_src/js/index.js +++ b/web_src/js/index.js @@ -1,4 +1,5 @@ -import './publicpath.js'; +// bootstrap module must be the first one to be imported, it handles webpack lazy-loading and global errors +import './bootstrap.js'; import $ from 'jquery'; import {initVueEnv} from './components/VueComponentLoader.js'; @@ -45,7 +46,7 @@ import { initGlobalButtons, initGlobalCommon, initGlobalDropzone, - initGlobalEnterQuickSubmit, initGlobalErrorHandler, + initGlobalEnterQuickSubmit, initGlobalFormDirtyLeaveConfirm, initGlobalLinkActions, initHeadNavbarContentToggle, @@ -83,9 +84,7 @@ $.fn.tab.settings.silent = true; $.fn.checkbox.settings.enableEnterKey = false; initVueEnv(); - $(document).ready(() => { - initGlobalErrorHandler(); initGlobalCommon(); initGlobalButtonClickOnEnter(); diff --git a/web_src/js/publicpath.js b/web_src/js/publicpath.js deleted file mode 100644 index 44448a8447c49..0000000000000 --- a/web_src/js/publicpath.js +++ /dev/null @@ -1,6 +0,0 @@ -// This sets up the URL prefix used in webpack's chunk loading. -// This file must be imported before any lazy-loading is being attempted. -import {joinPaths} from './utils.js'; -const {assetUrlPrefix} = window.config; - -__webpack_public_path__ = joinPaths(assetUrlPrefix, '/'); From dd95b554cdeca21801355709bcdd2b03da2f4980 Mon Sep 17 00:00:00 2001 From: wxiaoguang <wxiaoguang@gmail.com> Date: Tue, 29 Mar 2022 18:07:56 +0800 Subject: [PATCH 06/11] Some people like to modify the `head.tmpl`, so we separate the script part, then it's much safer. --- templates/base/head.tmpl | 47 +++------------------------------ templates/base/head_script.tmpl | 44 ++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 44 deletions(-) create mode 100644 templates/base/head_script.tmpl diff --git a/templates/base/head.tmpl b/templates/base/head.tmpl index 8101f9aa6b754..35157e9b954b4 100644 --- a/templates/base/head.tmpl +++ b/templates/base/head.tmpl @@ -19,53 +19,12 @@ <link rel="alternate" type="application/atom+xml" title="" href="{{.FeedURL}}.atom"> <link rel="alternate" type="application/rss+xml" title="" href="{{.FeedURL}}.rss"> {{end}} - <script> - <!-- /* eslint-disable */ --> - window.addEventListener('error', function(e) {window._globalHandlerErrors=window._globalHandlerErrors||[]; window._globalHandlerErrors.push(e);}); - window.config = { - appVer: '{{AppVer}}', - appUrl: '{{AppUrl}}', - appSubUrl: '{{AppSubUrl}}', - assetUrlPrefix: '{{AssetUrlPrefix}}', - runModeIsProd: {{.RunModeIsProd}}, - customEmojis: {{CustomEmojis}}, - useServiceWorker: {{UseServiceWorker}}, - csrfToken: '{{.CsrfToken}}', - pageData: {{.PageData}}, - requireTribute: {{.RequireTribute}}, - notificationSettings: {{NotificationSettings}}, {{/*a map provided by NewFuncMap in helper.go*/}} - enableTimeTracking: {{EnableTimetracking}}, - {{if .RequireTribute}} - tributeValues: Array.from(new Map([ - {{ range .Participants }} - ['{{.Name}}', {key: '{{.Name}} {{.FullName}}', value: '{{.Name}}', - name: '{{.Name}}', fullname: '{{.FullName}}', avatar: '{{.AvatarLink}}'}], - {{ end }} - {{ range .Assignees }} - ['{{.Name}}', {key: '{{.Name}} {{.FullName}}', value: '{{.Name}}', - name: '{{.Name}}', fullname: '{{.FullName}}', avatar: '{{.AvatarLink}}'}], - {{ end }} - {{ range .MentionableTeams }} - ['{{$.MentionableTeamsOrg}}/{{.Name}}', {key: '{{$.MentionableTeamsOrg}}/{{.Name}}', value: '{{$.MentionableTeamsOrg}}/{{.Name}}', - name: '{{$.MentionableTeamsOrg}}/{{.Name}}', avatar: '{{$.MentionableTeamsOrgAvatar}}'}], - {{ end }} - ]).values()), - {{end}} - mermaidMaxSourceCharacters: {{MermaidMaxSourceCharacters}}, - {{/* this global i18n object should only contain general texts. for specialized texts, it should be provided inside the related modules by: (1) API response (2) HTML data-attribute (3) PageData */}} - i18n: { - copy_success: '{{.i18n.Tr "copy_success"}}', - copy_error: '{{.i18n.Tr "copy_error"}}', - error_occurred: '{{.i18n.Tr "error.occurred"}}', - network_error: '{{.i18n.Tr "error.network_error"}}', - }, - }; - {{/* in case some pages don't render the pageData, we make sure it is an object to prevent null access */}} - window.config.pageData = window.config.pageData || {}; - </script> <link rel="icon" href="{{AssetUrlPrefix}}/img/logo.svg" type="image/svg+xml"> <link rel="alternate icon" href="{{AssetUrlPrefix}}/img/favicon.png" type="image/png"> <link rel="stylesheet" href="{{AssetUrlPrefix}}/css/index.css?v={{MD5 AppVer}}"> + + {{template "base/head_script" .}} + <noscript> <style> .dropdown:hover > .menu { display: block; } diff --git a/templates/base/head_script.tmpl b/templates/base/head_script.tmpl new file mode 100644 index 0000000000000..4e4f3ea319c6b --- /dev/null +++ b/templates/base/head_script.tmpl @@ -0,0 +1,44 @@ +<script> + <!-- /* eslint-disable */ --> + window.addEventListener('error', function(e) {window._globalHandlerErrors=window._globalHandlerErrors||[]; window._globalHandlerErrors.push(e);}); + window.config = { + appVer: '{{AppVer}}', + appUrl: '{{AppUrl}}', + appSubUrl: '{{AppSubUrl}}', + assetUrlPrefix: '{{AssetUrlPrefix}}', + runModeIsProd: {{.RunModeIsProd}}, + customEmojis: {{CustomEmojis}}, + useServiceWorker: {{UseServiceWorker}}, + csrfToken: '{{.CsrfToken}}', + pageData: {{.PageData}}, + requireTribute: {{.RequireTribute}}, + notificationSettings: {{NotificationSettings}}, {{/*a map provided by NewFuncMap in helper.go*/}} + enableTimeTracking: {{EnableTimetracking}}, + {{if .RequireTribute}} + tributeValues: Array.from(new Map([ + {{ range .Participants }} + ['{{.Name}}', {key: '{{.Name}} {{.FullName}}', value: '{{.Name}}', + name: '{{.Name}}', fullname: '{{.FullName}}', avatar: '{{.AvatarLink}}'}], + {{ end }} + {{ range .Assignees }} + ['{{.Name}}', {key: '{{.Name}} {{.FullName}}', value: '{{.Name}}', + name: '{{.Name}}', fullname: '{{.FullName}}', avatar: '{{.AvatarLink}}'}], + {{ end }} + {{ range .MentionableTeams }} + ['{{$.MentionableTeamsOrg}}/{{.Name}}', {key: '{{$.MentionableTeamsOrg}}/{{.Name}}', value: '{{$.MentionableTeamsOrg}}/{{.Name}}', + name: '{{$.MentionableTeamsOrg}}/{{.Name}}', avatar: '{{$.MentionableTeamsOrgAvatar}}'}], + {{ end }} + ]).values()), + {{end}} + mermaidMaxSourceCharacters: {{MermaidMaxSourceCharacters}}, + {{/* this global i18n object should only contain general texts. for specialized texts, it should be provided inside the related modules by: (1) API response (2) HTML data-attribute (3) PageData */}} + i18n: { + copy_success: '{{.i18n.Tr "copy_success"}}', + copy_error: '{{.i18n.Tr "copy_error"}}', + error_occurred: '{{.i18n.Tr "error.occurred"}}', + network_error: '{{.i18n.Tr "error.network_error"}}', + }, + }; + {{/* in case some pages don't render the pageData, we make sure it is an object to prevent null access */}} + window.config.pageData = window.config.pageData || {}; +</script> From 1dfbe4db72b73471b349b829a0354e65f83b5ea2 Mon Sep 17 00:00:00 2001 From: wxiaoguang <wxiaoguang@gmail.com> Date: Tue, 29 Mar 2022 18:11:59 +0800 Subject: [PATCH 07/11] fix lint --- templates/base/head_script.tmpl | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/templates/base/head_script.tmpl b/templates/base/head_script.tmpl index 4e4f3ea319c6b..c853f8e60586a 100644 --- a/templates/base/head_script.tmpl +++ b/templates/base/head_script.tmpl @@ -3,7 +3,6 @@ window.addEventListener('error', function(e) {window._globalHandlerErrors=window._globalHandlerErrors||[]; window._globalHandlerErrors.push(e);}); window.config = { appVer: '{{AppVer}}', - appUrl: '{{AppUrl}}', appSubUrl: '{{AppSubUrl}}', assetUrlPrefix: '{{AssetUrlPrefix}}', runModeIsProd: {{.RunModeIsProd}}, @@ -14,24 +13,24 @@ requireTribute: {{.RequireTribute}}, notificationSettings: {{NotificationSettings}}, {{/*a map provided by NewFuncMap in helper.go*/}} enableTimeTracking: {{EnableTimetracking}}, - {{if .RequireTribute}} + {{if .RequireTribute}} tributeValues: Array.from(new Map([ - {{ range .Participants }} + {{ range .Participants }} ['{{.Name}}', {key: '{{.Name}} {{.FullName}}', value: '{{.Name}}', - name: '{{.Name}}', fullname: '{{.FullName}}', avatar: '{{.AvatarLink}}'}], - {{ end }} - {{ range .Assignees }} + name: '{{.Name}}', fullname: '{{.FullName}}', avatar: '{{.AvatarLink}}'}], + {{ end }} + {{ range .Assignees }} ['{{.Name}}', {key: '{{.Name}} {{.FullName}}', value: '{{.Name}}', - name: '{{.Name}}', fullname: '{{.FullName}}', avatar: '{{.AvatarLink}}'}], - {{ end }} - {{ range .MentionableTeams }} - ['{{$.MentionableTeamsOrg}}/{{.Name}}', {key: '{{$.MentionableTeamsOrg}}/{{.Name}}', value: '{{$.MentionableTeamsOrg}}/{{.Name}}', + name: '{{.Name}}', fullname: '{{.FullName}}', avatar: '{{.AvatarLink}}'}], + {{ end }} + {{ range .MentionableTeams }} + ['{{$.MentionableTeamsOrg}}/{{.Name}}', {key: '{{$.MentionableTeamsOrg}}/{{.Name}}', value: '{{$.MentionableTeamsOrg}}/{{.Name}}', name: '{{$.MentionableTeamsOrg}}/{{.Name}}', avatar: '{{$.MentionableTeamsOrgAvatar}}'}], - {{ end }} + {{ end }} ]).values()), - {{end}} + {{end}} mermaidMaxSourceCharacters: {{MermaidMaxSourceCharacters}}, - {{/* this global i18n object should only contain general texts. for specialized texts, it should be provided inside the related modules by: (1) API response (2) HTML data-attribute (3) PageData */}} + {{/* this global i18n object should only contain general texts. for specialized texts, it should be provided inside the related modules by: (1) API response (2) HTML data-attribute (3) PageData */}} i18n: { copy_success: '{{.i18n.Tr "copy_success"}}', copy_error: '{{.i18n.Tr "copy_error"}}', @@ -39,6 +38,6 @@ network_error: '{{.i18n.Tr "error.network_error"}}', }, }; - {{/* in case some pages don't render the pageData, we make sure it is an object to prevent null access */}} + {{/* in case some pages don't render the pageData, we make sure it is an object to prevent null access */}} window.config.pageData = window.config.pageData || {}; </script> From 48eaaee41c22ef135d94821f7b4d57afdd488047 Mon Sep 17 00:00:00 2001 From: wxiaoguang <wxiaoguang@gmail.com> Date: Tue, 29 Mar 2022 18:16:55 +0800 Subject: [PATCH 08/11] Add ==== DO NOT EDIT ==== warning to head_script.tmpl --- templates/base/head_script.tmpl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/templates/base/head_script.tmpl b/templates/base/head_script.tmpl index c853f8e60586a..459b1151e5c92 100644 --- a/templates/base/head_script.tmpl +++ b/templates/base/head_script.tmpl @@ -1,3 +1,8 @@ +{{/* +==== DO NOT EDIT ==== +If you are customizing Gitea, please do not change this file. +If you introduce mistakes in it, Gitea JavaScript code wouldn't run correctly. +*/}} <script> <!-- /* eslint-disable */ --> window.addEventListener('error', function(e) {window._globalHandlerErrors=window._globalHandlerErrors||[]; window._globalHandlerErrors.push(e);}); From e1236a9b7a7c71770837788f330a27a2f5356d2c Mon Sep 17 00:00:00 2001 From: wxiaoguang <wxiaoguang@gmail.com> Date: Tue, 29 Mar 2022 18:20:02 +0800 Subject: [PATCH 09/11] fix comment --- web_src/js/bootstrap.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web_src/js/bootstrap.js b/web_src/js/bootstrap.js index b8db8b8290015..d0182d36706f5 100644 --- a/web_src/js/bootstrap.js +++ b/web_src/js/bootstrap.js @@ -27,7 +27,7 @@ function initGlobalErrorHandler() { showGlobalErrorMessageHtml(`Gitea JavaScript code couldn't run correctly, please check your custom templates`); } - // we added an event handler for window error at the very beginning of head.tmpl + // we added an event handler for window error at the very beginning of <script> of page head // the handler calls `_globalHandlerErrors.push` (array method) to record all errors occur before this init // then in this init, we can collect all error events and show them for (const e of window._globalHandlerErrors || []) { From 93446c6f90324d414173c93bb304423dd71571d5 Mon Sep 17 00:00:00 2001 From: wxiaoguang <wxiaoguang@gmail.com> Date: Tue, 29 Mar 2022 18:52:41 +0800 Subject: [PATCH 10/11] add appUrl to window.config again --- templates/base/head_script.tmpl | 1 + 1 file changed, 1 insertion(+) diff --git a/templates/base/head_script.tmpl b/templates/base/head_script.tmpl index 459b1151e5c92..e6a8060a16f3e 100644 --- a/templates/base/head_script.tmpl +++ b/templates/base/head_script.tmpl @@ -8,6 +8,7 @@ If you introduce mistakes in it, Gitea JavaScript code wouldn't run correctly. window.addEventListener('error', function(e) {window._globalHandlerErrors=window._globalHandlerErrors||[]; window._globalHandlerErrors.push(e);}); window.config = { appVer: '{{AppVer}}', + appUrl: '{{AppUrl}}', appSubUrl: '{{AppSubUrl}}', assetUrlPrefix: '{{AssetUrlPrefix}}', runModeIsProd: {{.RunModeIsProd}}, From ce5a9b7238d1fe6e456c7c65dae9d3b1a42c8c1b Mon Sep 17 00:00:00 2001 From: wxiaoguang <wxiaoguang@gmail.com> Date: Tue, 29 Mar 2022 19:05:31 +0800 Subject: [PATCH 11/11] No necessary to support html msg, so just use pre-line to render new line. --- web_src/js/bootstrap.js | 9 +++++---- web_src/js/features/common-global.js | 9 +++------ 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/web_src/js/bootstrap.js b/web_src/js/bootstrap.js index d0182d36706f5..cf13b2a559d30 100644 --- a/web_src/js/bootstrap.js +++ b/web_src/js/bootstrap.js @@ -7,11 +7,12 @@ import {joinPaths} from './utils.js'; // This file must be imported before any lazy-loading is being attempted. __webpack_public_path__ = joinPaths(window?.config?.assetUrlPrefix ?? '/', '/'); -export function showGlobalErrorMessageHtml(msgHtml) { +export function showGlobalErrorMessage(msg) { const pageContent = document.querySelector('.page-content'); if (!pageContent) return; const el = document.createElement('div'); - el.innerHTML = `<div class="ui container negative message center aligned js-global-error">${msgHtml}</div>`; + el.innerHTML = `<div class="ui container negative message center aligned js-global-error" style="white-space: pre-line;"></div>`; + el.childNodes[0].textContent = msg; pageContent.prepend(el.childNodes[0]); } @@ -19,12 +20,12 @@ export function showGlobalErrorMessageHtml(msgHtml) { * @param {ErrorEvent} e */ function processWindowErrorEvent(e) { - showGlobalErrorMessageHtml(`JavaScript error: ${e.message} (${e.filename} @ ${e.lineno}:${e.colno}). Open browser console to see more details.`); + showGlobalErrorMessage(`JavaScript error: ${e.message} (${e.filename} @ ${e.lineno}:${e.colno}). Open browser console to see more details.`); } function initGlobalErrorHandler() { if (!window.config) { - showGlobalErrorMessageHtml(`Gitea JavaScript code couldn't run correctly, please check your custom templates`); + showGlobalErrorMessage(`Gitea JavaScript code couldn't run correctly, please check your custom templates`); } // we added an event handler for window error at the very beginning of <script> of page head diff --git a/web_src/js/features/common-global.js b/web_src/js/features/common-global.js index 240bd78685149..60af4d0d67e0e 100644 --- a/web_src/js/features/common-global.js +++ b/web_src/js/features/common-global.js @@ -3,8 +3,7 @@ import 'jquery.are-you-sure'; import {mqBinarySearch} from '../utils.js'; import createDropzone from './dropzone.js'; import {initCompColorPicker} from './comp/ColorPicker.js'; -import {htmlEscape} from 'escape-goat'; -import {showGlobalErrorMessageHtml} from '../bootstrap.js'; +import {showGlobalErrorMessage} from '../bootstrap.js'; const {appUrl, csrfToken} = window.config; @@ -359,8 +358,6 @@ export function checkAppUrl() { if (curUrl.startsWith(appUrl)) { return; } - showGlobalErrorMessageHtml(` -Your ROOT_URL in app.ini is ${htmlEscape(appUrl)} but you are visiting ${htmlEscape(curUrl)}<br /> -You should set ROOT_URL correctly, otherwise the web may not work correctly. -`); + showGlobalErrorMessage(`Your ROOT_URL in app.ini is ${appUrl} but you are visiting ${curUrl} +You should set ROOT_URL correctly, otherwise the web may not work correctly.`); }