Bugzilla – Attachment 185104 Details for
Bug 35716
Duplicated assets in both koha-tmpl/intra-tmpl and koha-tmpl/opac-tmpl
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 35716: (another alternate) Use rspack to manage shared JS assets between OPAC and Staff
Bug-35716-another-alternate-Use-rspack-to-manage-s.patch (text/plain), 11.36 KB, created by
Paul Derscheid
on 2025-08-04 14:43:02 UTC
(
hide
)
Description:
Bug 35716: (another alternate) Use rspack to manage shared JS assets between OPAC and Staff
Filename:
MIME Type:
Creator:
Paul Derscheid
Created:
2025-08-04 14:43:02 UTC
Size:
11.36 KB
patch
obsolete
>From 949f27612df79ce2e0c272a4c22c2a72f4320df4 Mon Sep 17 00:00:00 2001 >From: Paul Derscheid <paul.derscheid@lmscloud.de> >Date: Mon, 4 Aug 2025 16:22:51 +0200 >Subject: [PATCH] Bug 35716: (another alternate) Use rspack to manage shared JS > assets between OPAC and Staff > >This POC introduces a shared assets directory and configures rspack to >build (currently) identical JS files from a single source to both OPAC and Staff >locations, eliminating code duplication and preventing drift. > >The POC uses cookieconsent.js which already started to drift into >diverging implementations (OPAC: 257 lines, Staff: 199 lines). Here, >this is due to different requirements in Staff Interface vs OPAC. One >way to handle this is to have aliases configured in rspack that import >what's needed for the respective bundle via module imports. >If needed I can provide an extended POC for that feature specifically >but wanted to show first how this could work. > >How it works: >- Source files live in koha-tmpl/shared/js/ (this is totally arbitrary) >- Rspack builds twice: once to intranet-tmpl/prog/js/, once to opac-tmpl/bootstrap/js/ >- Templates continue using [% Asset.js() %] unchanged > >Test plan: >1. Apply patch >2. Run: yarn js:build >3. Verify koha-tmpl/intranet-tmpl/prog/js/cookieconsent.js exists >4. Verify koha-tmpl/opac-tmpl/bootstrap/js/cookieconsent.js exists >5. Confirm both files are identical >6. Test cookie consent functionality works in both OPAC and Staff interface (with the caveats brought up earlier) >--- > koha-tmpl/shared/js/cookieconsent.js | 199 +++++++++++++++++++++++++++ > rspack.config.js | 78 +++++++++++ > 2 files changed, 277 insertions(+) > create mode 100644 koha-tmpl/shared/js/cookieconsent.js > >diff --git a/koha-tmpl/shared/js/cookieconsent.js b/koha-tmpl/shared/js/cookieconsent.js >new file mode 100644 >index 00000000000..834dc1fd626 >--- /dev/null >+++ b/koha-tmpl/shared/js/cookieconsent.js >@@ -0,0 +1,199 @@ >+(function () { >+ // Has the user previously consented, establish our >+ // initial state >+ let selected = []; >+ let existingConsent = ""; >+ // The presence of a 'cookieConsent' local storage item indicates >+ // that previous consent has been set. If the value is empty, then >+ // no consent to non-essential cookies was given >+ const hasStoredConsent = localStorage.getItem("cookieConsent"); >+ getExistingConsent(); >+ >+ // The consent bar may not be in the DOM if it is not being used >+ const consentBar = $("#cookieConsentBar"); >+ if (!consentBar) { >+ return; >+ } >+ >+ // Initialize the consent modal and prevent the modal >+ // from being closed with anything but the buttons >+ const cookieConsentModal = new bootstrap.Modal( >+ document.getElementById("cookieConsentModal"), >+ { >+ backdrop: "static", >+ keyboard: false, >+ focus: true, >+ show: true, >+ } >+ ); >+ >+ if (hasStoredConsent === null) { >+ showConsentBar(); >+ } >+ addButtonHandlers(); >+ >+ // When the modal is opened, populate our state based on currently >+ // selected values >+ const cookieConsentModalEl = document.getElementById("cookieConsentModal"); >+ cookieConsentModalEl.addEventListener("shown.bs.modal", () => { >+ initialiseSelected(); >+ }); >+ >+ // Initialise existing consent based on local storage >+ function getExistingConsent() { >+ existingConsent = localStorage.getItem("cookieConsent") >+ ? localStorage.getItem("cookieConsent") >+ : []; >+ } >+ >+ function showConsentBar() { >+ const consentBar = $("#cookieConsentBar"); >+ const langmenu = $("#changelanguage"); >+ if (langmenu) { >+ const height = langmenu.height(); >+ consentBar.css("bottom", height); >+ } >+ consentBar.attr("aria-hidden", "false"); >+ consentBar.css("display", "flex"); >+ } >+ >+ function hideConsentBar() { >+ const consentBar = $("#cookieConsentBar"); >+ consentBar.attr("aria-hidden", "true"); >+ consentBar.hide(); >+ } >+ >+ // Hides the appropriate consent container, depending on what >+ // is currently visible >+ function hideContainer() { >+ if ($(cookieConsentModalEl).is(":visible")) { >+ cookieConsentModal.hide(); >+ } else { >+ hideConsentBar(); >+ } >+ } >+ >+ // Initialise our state of selected item and enable/disable >+ // the "Accept selected" button appropriately >+ function initialiseSelected() { >+ selected = []; >+ $(".consentCheckbox").each(function () { >+ const val = $(this).val(); >+ if (existingConsent.indexOf(val) > -1) { >+ $(this).prop("checked", true); >+ selected.push(val); >+ } else { >+ $(this).prop("checked", false); >+ } >+ }); >+ enableDisableSelectedButton(); >+ } >+ >+ // Maintain our state of selected items and enable/disable >+ // the "Accept selected" button appropriately >+ function maintainSelected() { >+ selected = []; >+ $(".consentCheckbox:checked").each(function () { >+ selected.push($(this).val()); >+ }); >+ enableDisableSelectedButton(); >+ } >+ >+ // Set the enabled / disabled state of the >+ // "Accept selected button based on the checkbox >+ // states >+ function enableDisableSelectedButton() { >+ $("#consentAcceptSelected").prop("disabled", selected.length === 0); >+ } >+ >+ function runConsentedCode() { >+ getExistingConsent(); >+ $(".consentCode").each(function () { >+ // The user has explicitly consented to this code, or the >+ // code doesn't require consent in the staff view >+ if ( >+ existingConsent.indexOf($(this).data("consent-id")) > -1 || >+ !$(this).data("requires-consent") >+ ) { >+ const code = atob($(this).data("consent-code")); >+ const func = Function(code); >+ func(); >+ } else { >+ // This code doesn't have consent to run, we may need to remove >+ // any cookies it has previously set >+ const matchPattern = $(this).data("consent-match-pattern"); >+ const cookieDomain = $(this).data("consent-cookie-domain"); >+ const cookiePath = $(this).data("consent-cookie-path"); >+ if (matchPattern.length > 0) { >+ const regex = new RegExp(matchPattern); >+ const allCookies = document.cookie.split("; "); >+ allCookies.forEach(function (cookie) { >+ const name = cookie.split("=")[0]; >+ if (regex.test(name)) { >+ document.cookie = >+ name + >+ "=; expires=Thu, 01 Jan 1970 00:00:01 GMT; domain=" + >+ cookieDomain + >+ "; path=" + >+ cookiePath; >+ } >+ }); >+ } >+ } >+ }); >+ } >+ >+ function addButtonHandlers() { >+ // "Accept all" handler >+ $(".consentAcceptAll").on("click", function (e) { >+ e.preventDefault(); >+ let toSave = []; >+ $(".consentCheckbox").each(function () { >+ const val = $(this).val(); >+ toSave.push(val); >+ }); >+ localStorage.setItem("cookieConsent", toSave); >+ hideContainer(); >+ runConsentedCode(); >+ }); >+ >+ // "Accept essential" handler >+ $(".consentAcceptEssential").on("click", function (e) { >+ e.preventDefault(); >+ localStorage.setItem("cookieConsent", []); >+ hideContainer(); >+ runConsentedCode(); >+ }); >+ >+ // "Accept selected" handler >+ $("#consentAcceptSelected").on("click", function (e) { >+ e.preventDefault(); >+ const toSave = selected.length > 0 ? selected : []; >+ localStorage.setItem("cookieConsent", toSave); >+ hideContainer(); >+ runConsentedCode(); >+ }); >+ >+ // "Cancel" handler >+ $(".consentCloseModal").on("click", function (e) { >+ e.preventDefault(); >+ hideContainer(); >+ showConsentBar(); >+ }); >+ >+ // "More information" handler >+ $("#consentMoreInfo, #viewCookieConsents").on("click", function (e) { >+ e.preventDefault(); >+ hideConsentBar(); >+ // Ensure we're up to date with the existing consent >+ getExistingConsent(); >+ cookieConsentModal.show(); >+ }); >+ $(".consentCheckbox").on("click", function () { >+ maintainSelected(); >+ }); >+ } >+ >+ // On page load, run any code that has been given consent >+ runConsentedCode(); >+})(); >diff --git a/rspack.config.js b/rspack.config.js >index 9b10583d51b..b98e1137346 100644 >--- a/rspack.config.js >+++ b/rspack.config.js >@@ -3,7 +3,85 @@ const { VueLoaderPlugin } = require("vue-loader"); > const path = require("path"); > const rspack = require("@rspack/core"); > >+const sharedAssetsStaffConfig = { >+ name: "shared-assets-staff", >+ entry: { >+ cookieconsent: "./koha-tmpl/shared/js/cookieconsent.js", >+ // Future shared modules: >+ // coce: "./koha-tmpl/shared/js/coce.js", >+ // "ill-availability": "./koha-tmpl/shared/js/ill-availability.js", >+ // and so on... >+ }, >+ output: { >+ filename: "[name].js", >+ path: path.resolve(__dirname, "koha-tmpl/intranet-tmpl/prog/js/"), >+ }, >+ module: { >+ rules: [ >+ { >+ test: /\.js$/, >+ loader: "builtin:swc-loader", >+ options: { >+ jsc: { >+ parser: { >+ syntax: "ecmascript", >+ }, >+ }, >+ }, >+ exclude: [/node_modules/], >+ type: "javascript/auto", >+ }, >+ ], >+ }, >+ externals: { >+ jquery: "jQuery", >+ }, >+ optimization: { >+ minimize: process.env.NODE_ENV === "production", >+ }, >+}; >+ >+const sharedAssetsOpacConfig = { >+ name: "shared-assets-opac", >+ entry: { >+ cookieconsent: "./koha-tmpl/shared/js/cookieconsent.js", >+ // Future shared modules: >+ // coce: "./koha-tmpl/shared/js/coce.js", >+ // "ill-availability": "./koha-tmpl/shared/js/ill-availability.js", >+ // and so on... >+ }, >+ output: { >+ filename: "[name].js", >+ path: path.resolve(__dirname, "koha-tmpl/opac-tmpl/bootstrap/js/"), >+ }, >+ module: { >+ rules: [ >+ { >+ test: /\.js$/, >+ loader: "builtin:swc-loader", >+ options: { >+ jsc: { >+ parser: { >+ syntax: "ecmascript", >+ }, >+ }, >+ }, >+ exclude: [/node_modules/], >+ type: "javascript/auto", >+ }, >+ ], >+ }, >+ externals: { >+ jquery: "jQuery", >+ }, >+ optimization: { >+ minimize: process.env.NODE_ENV === "production", >+ }, >+}; >+ > module.exports = [ >+ sharedAssetsStaffConfig, >+ sharedAssetsOpacConfig, > { > resolve: { > alias: { >-- >2.50.1
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 35716
:
164381
|
174481
|
174482
|
174483
|
174484
| 185104