Bugzilla – Attachment 130074 Details for
Bug 27378
Enable compliance with EU Cookie Legislation via cookie consent
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 27378: Add JS to power modaljs
Bug-27378-Add-JS-to-power-modaljs.patch (text/plain), 15.94 KB, created by
Martin Renvoize (ashimema)
on 2022-02-02 10:04:24 UTC
(
hide
)
Description:
Bug 27378: Add JS to power modaljs
Filename:
MIME Type:
Creator:
Martin Renvoize (ashimema)
Created:
2022-02-02 10:04:24 UTC
Size:
15.94 KB
patch
obsolete
>From 07b8416e6fe1fb63d770733552ee9bcd65674700 Mon Sep 17 00:00:00 2001 >From: Andrew Isherwood <andrew.isherwood@ptfs-europe.com> >Date: Thu, 14 Jan 2021 15:20:09 +0000 >Subject: [PATCH] Bug 27378: Add JS to power modaljs > >This commit adds the Javascript that provides the functionality within >the consentJS modaljs modal. It provides the ability to display the >current state of any consentJS values and to amend them, as well as add >new ones. > >There is also some refactoring of the existing modalselect functions to >allow re-use of existing code between modalselect & modaljs. This >refactoring has been minimal and consists of abstracting functionality >into functions to allow more wide use. > >Styles for the consentJS UI were also added. > >Signed-ff-by: Barry Cannon <bc@interleaf.ie> >--- > .../prog/css/src/staff-global.scss | 31 ++ > .../prog/js/pages/preferences.js | 303 +++++++++++++++--- > 2 files changed, 288 insertions(+), 46 deletions(-) > >diff --git a/koha-tmpl/intranet-tmpl/prog/css/src/staff-global.scss b/koha-tmpl/intranet-tmpl/prog/css/src/staff-global.scss >index fa3421dee1..6bc9c0beb6 100644 >--- a/koha-tmpl/intranet-tmpl/prog/css/src/staff-global.scss >+++ b/koha-tmpl/intranet-tmpl/prog/css/src/staff-global.scss >@@ -4562,6 +4562,37 @@ div .suggestion_note { > } > } > >+#consentJSItems { >+ #consentJSWarning { >+ margin: 20px 0; >+ text-align: center; >+ font-weight: 800; >+ font-size: 120%; >+ } >+ .consentJSItem { >+ margin-bottom: 30px; >+ padding: 10px; >+ border: 1px solid #ccc; >+ } >+ .consentRow { >+ display: flex; >+ margin-bottom: 20px; >+ .consentItem { >+ margin-right: 40px; >+ } >+ .consentItem:last-child { >+ margin-right: 0; >+ } >+ } >+ .codeRow { >+ display: block; >+ } >+ .consentDelete { >+ display: block; >+ text-align: right; >+ } >+} >+ > @media (min-width: 200px) { > > } >diff --git a/koha-tmpl/intranet-tmpl/prog/js/pages/preferences.js b/koha-tmpl/intranet-tmpl/prog/js/pages/preferences.js >index add0d978f3..e5cc0b02a2 100644 >--- a/koha-tmpl/intranet-tmpl/prog/js/pages/preferences.js >+++ b/koha-tmpl/intranet-tmpl/prog/js/pages/preferences.js >@@ -64,6 +64,80 @@ window.onbeforeunload = function () { > } > }; > >+// Add event handlers to any elements with .expand-textarea classes >+// this allows the CodeMirror widget to be displayed >+function addExpandHandler() { >+ // Don't duplicate click event handlers >+ const ev = $._data($('.expand-textarea'), 'events'); >+ if (ev && ev.click) { >+ return; >+ } >+ $(".expand-textarea").on("click", function (e) { >+ e.preventDefault(); >+ $(this).hide(); >+ var target = $(this).data("target"); >+ var syntax = $(this).data("syntax"); >+ $("#collapse_" + target).show(); >+ if (syntax) { >+ var editor = CodeMirror.fromTextArea(document.getElementById("pref_" + target), { >+ lineNumbers: true, >+ mode: syntax, >+ lineWrapping: true, >+ viewportMargin: Infinity, >+ gutters: ["CodeMirror-lint-markers"], >+ lint: true >+ }); >+ editor.on("change", function () { >+ mark_modified.call($("#pref_" + target)[0]); >+ }); >+ editor.on("blur", function () { >+ editor.save(); >+ }); >+ } else { >+ $("#pref_" + target).show(); >+ } >+ }); >+} >+ >+// Add event handlers to any elements with .collapse-textarea classes >+// this allows the hiding of the CodeMirror widget >+function addCollapseHandler() { >+ // Don't duplicate click event handlers >+ const ev = $._data($('.collapse-textarea'), 'events'); >+ if (ev && ev.click) { >+ return; >+ } >+ $(".collapse-textarea").on("click", function (e) { >+ e.preventDefault(); >+ $(this).hide(); >+ var target = $(this).data("target"); >+ var syntax = $(this).data("syntax"); >+ $("#expand_" + target).show(); >+ if (syntax) { >+ var editor = $("#pref_" + target).next(".CodeMirror")[0].CodeMirror; >+ editor.toTextArea(); >+ } >+ $("#pref_" + target).hide(); >+ }); >+} >+ >+// Add a handler for any consent delete links >+function addConsentDeleteHandler() { >+ // Don't duplicate click event handlers >+ const ev = $._data($('.consentDelete'), 'events'); >+ if (ev && ev.click) { >+ return; >+ } >+ $('.consentDelete').on('click', function (e) { >+ e.preventDefault(); >+ const target = $(this).data('target'); >+ const proceed = confirm(__('Are you sure you want to delete this consent item?')); >+ if (proceed) { >+ $('#' + target).remove(); >+ } >+ }); >+} >+ > $( document ).ready( function () { > > $("table.preferences").dataTable($.extend(true, {}, dataTablesDefaults, { >@@ -107,44 +181,9 @@ $( document ).ready( function () { > return false; > }); > >- $( ".expand-textarea" ).on("click", function(e){ >- e.preventDefault(); >- $(this).hide(); >- var target = $(this).data("target"); >- var syntax = $(this).data("syntax"); >- $("#collapse_" + target ).show(); >- if( syntax ){ >- var editor = CodeMirror.fromTextArea( document.getElementById( "pref_" + target ), { >- lineNumbers: true, >- mode: syntax, >- lineWrapping: true, >- viewportMargin: Infinity, >- gutters: ["CodeMirror-lint-markers"], >- lint: true >- }); >- editor.on("change", function(){ >- mark_modified.call( $("#pref_" + target )[0]); >- }); >- editor.on("blur", function(){ >- editor.save(); >- }); >- } else { >- $("#pref_" + target ).show(); >- } >- }); >+ addExpandHandler(); > >- $( ".collapse-textarea" ).on("click", function(e){ >- e.preventDefault(); >- $(this).hide(); >- var target = $(this).data("target"); >- var syntax = $(this).data("syntax"); >- $("#expand_" + target ).show(); >- if( syntax ){ >- var editor = $("#pref_" + target ).next(".CodeMirror")[0].CodeMirror; >- editor.toTextArea(); >- } >- $("#pref_" + target ).hide(); >- }); >+ addCollapseHandler(); > > $("h3").attr("class", "expanded").attr("title", __("Click to collapse this section")); > var collapsible = $(".collapsed,.expanded"); >@@ -264,28 +303,200 @@ $( document ).ready( function () { > }).appendTo("#prefModalForm"); > }); > $("#saveModalPrefs").data("target", this.id ); >+ $("#saveModalPrefs").data("type", "modalselect" ); > $("#prefModalLabel").text( pref_name ); > $("#prefModal").modal("show"); > }); > >- $(".modaljs").on("click", function () { >- console.log('modaljs clicked'); >+ // Initialise the content of our modal, using the function >+ // specified in the data-initiator attribute >+ $('.modaljs').on('click', function () { >+ const init = $(this).data('initiator'); >+ if (init) { >+ window[init](this); >+ $("#prefModal").modal("show"); >+ } > }); > >- $("#saveModalPrefs").on("click", function(){ >- var formfieldid = $("#" + $(this).data("target") ); >+ // Initialise the content of our modal, if we are dealing >+ // with a modalselect modal >+ function prepareModalSelect(formfieldid) { > var prefs = []; > $("#prefModal input[type='checkbox']").each(function(){ > if( $(this).prop("checked") ){ > prefs.push( this.value ); > } > }); >+ return prefs.join("|"); >+ } >+ >+ // Return a checkbox with an appropriate checked state >+ function checkBox(id, className, state) { >+ return state ? >+ '<input id="' + id + '" type="checkbox" class="' + className + '" checked>' : >+ '<input id="' + id + '" type="checkbox" class="' + className + '">'; >+ } >+ >+ // Create a consentJS item, correctly populated >+ function createConsentJSItem(item, idx) { >+ const id = 'ConsentJS_' + idx; >+ const code = item.code && item.code.length > 0 ? atob(item.code) : ''; >+ const itemId = item.id && item.id.length > 0 ? item.id : ''; >+ return '<div id="' + id + '" class="consentJSItem" data-id="' + itemId + '"><div class="consentRow"><div class="consentItem"><label class="required" for="name_' + id + '">' + __('Name') + ':</label><input id="name_' + id + '" class="metaName" type="text" value="' + item.name + '"><span class="required">' + __('Required') + '</span></div><div class="consentItem"><label class="required" for="description_' + id + '">' + __('Description') + ':</label><input id="description_' + id + '" class="metaDescription" type="text" value="' + item.description + '"><span class="required">' + __('Required') + '</span></div><div class="consentItem"><label for="opacConsent_' + id + '">' + __('Requires consent in OPAC') + ':</label>' + checkBox('opacConsent_' + id, 'opacConsent', item.opacConsent) + '</div><div class="consentItem"><label for="staffConsent_' + id + '">' + __('Requires consent in staff interface') + ':</label>' + checkBox('staffConsent_' + id, 'staffConsent', item.staffConsent) + '</div></div><div class="consentRow codeRow"><textarea style="display:none;" id="pref_' + id + '" class="preference preference-code codemirror" rows="10" cols="40">' + code + '</textarea><div><a id="expand_' + id + '" class="expand-textarea" data-target="' + id + '" data-syntax="javascript" href="#">' + __('Click to expand') + '</a><a id="collapse_' + id + '" class="collapse-textarea" data-target="' + id + '" data-syntax="javascript" href="#" style="display:none">' + __('Click to collapse') + '</a></div></div><a class="consentDelete" data-target="' + id + '" href="#">Delete</a></div>'; >+ } >+ >+ // Return the markup for all consentJS items concatenated >+ function populateConsentMarkup(items) { >+ return items.reduce(function (acc, current, idx) { >+ return acc + createConsentJSItem(current, idx); >+ }, ''); >+ } >+ >+ // Return the markup for a validation warning >+ function populateValidationWarning() { >+ return '<div id="consentJSWarning" class="error" style="display:none;">' + __('You must complete all fields') + '</div>'; >+ } >+ >+ // Return a new, empty consent item >+ function emptyConsentItem() { >+ return { >+ name: '', >+ description: '', >+ code: '', >+ consentInOpac: false, >+ consentInStaff: false >+ }; >+ } >+ >+ // Add the handler for a new empty consent item >+ function addNewHandler() { >+ $("#consentJSAddNew").on("click", function (e) { >+ e.preventDefault(); >+ const currentLen = $('.consentJSItem').length; >+ const newItem = emptyConsentItem(); >+ const markup = createConsentJSItem(newItem, currentLen); >+ $('#prefModal .modal-body #consentJSItems').append($(markup)); >+ addExpandHandler(); >+ addCollapseHandler(); >+ addConsentDeleteHandler(); >+ }); >+ } >+ >+ // Populate the consentJS modal, we also initialise any >+ // event handlers that are required. This function is added >+ // to the window object so we can call it if we are passed it's name >+ // as a data-initiator attribute >+ // (e.g.) >+ // const f = 'populateConsentJS'; >+ // window[f](); >+ window.populateConsentJS = function(el) { >+ let items = []; >+ let decoded = ''; >+ if (el.value && el.value.length > 0) { >+ try { >+ decoded = atob(el.value); >+ } catch (err) { >+ throw (__( >+ 'Unable to Base64 decode value stored in ConsentJS syspref: ' + >+ err.message >+ )); >+ } >+ try { >+ items = JSON.parse(decoded); >+ } catch (err) { >+ throw (__( >+ 'Unable to JSON parse decoded value stored in ConsentJS syspref: ' + >+ err.message >+ )); >+ } >+ } >+ const markup = populateConsentMarkup(items); >+ const validationWarning = populateValidationWarning(); >+ const pref_name = el.id.replace(/pref_/, ''); >+ $('#saveModalPrefs').data('target', el.id); >+ $('#prefModalLabel').text( pref_name ); >+ $('#prefModal .modal-body').html($('<div id="consentJSItems">' + validationWarning + markup + '</div><div><a href="#" id="consentJSAddNew">' + __('Add new code') + '</a></div>')); >+ addExpandHandler(); >+ addCollapseHandler(); >+ addNewHandler(); >+ addConsentDeleteHandler(); >+ } >+ >+ // Prepare the data in the UI for sending back as a syspref. >+ // We validate that everything is what we expect. This function is added >+ // to the window object so we can call it if we are passed it's name >+ // as a data-initiator attribute >+ // e.g. >+ // const f = 'prepareConsentJS'; >+ // window[f](); >+ window.prepareConsentJS = function () { >+ const items = $('.consentJSItem'); >+ const invalid = []; >+ const valid = []; >+ items.each(function () { >+ const id = $(this).data('id').length > 0 ? >+ $(this).data('id') : >+ '_' + Math.random().toString(36).substr(2, 9); >+ const name = $(this).find('.metaName').val(); >+ const desc = $(this).find('.metaDescription').val(); >+ const opacConsent = $(this).find('.opacConsent').is(':checked') >+ const staffConsent = $(this).find('.staffConsent').is(':checked'); >+ const code = $(this).find('.preference-code').val(); >+ // If the name, description and code are empty, then they've >+ // added a new entry, but not filled it in, we can skip it >+ if (name.length === 0 && desc.length === 0 && code.length === 0) { >+ return; >+ } >+ // They've filled in at least some info >+ if ( >+ (name.length === 0) || >+ (desc.length === 0) || >+ (code.length === 0) >+ ) { >+ invalid.push(this); >+ } else { >+ const obj = { >+ id: id, >+ name: name, >+ description: desc, >+ opacConsent: opacConsent, >+ staffConsent: staffConsent, >+ code: btoa(code) >+ } >+ valid.push(obj); >+ } >+ }); >+ // We failed validation >+ if (invalid.length > 0) { >+ $('#consentJSWarning').show(); >+ return false; >+ } >+ $('#consentJSWarning').hide(); >+ if (valid.length === 0) { >+ return ''; >+ } >+ const json = JSON.stringify(valid); >+ const base64 = btoa(json); >+ return base64; >+ } > >- formfieldid.val( prefs.join("|") ) >- .addClass("modified"); >- mark_modified.call( formfieldid ); >- KOHA.Preferences.Save( formfieldid.closest("form") ); >- $("#prefModal").modal("hide"); >+ $("#saveModalPrefs").on("click", function(){ >+ var formfieldid = $("#" + $(this).data("target")); >+ let finalString = ""; >+ if ($(this).data("type") == "modalselect") { >+ finalString = prepareModalSelect(formfieldid); >+ } else { >+ const processor = $(".modaljs").data("processor"); >+ finalString = window[processor](); >+ } >+ // A processor can return false if any of the submitted >+ // data has failed validation >+ if (finalString !== false) { >+ formfieldid.val(finalString).addClass("modified"); >+ mark_modified.call( formfieldid ); >+ KOHA.Preferences.Save( formfieldid.closest("form") ); >+ $("#prefModal").modal("hide"); >+ } > }); > > $("#prefModal").on("hide.bs.modal", function(){ >-- >2.20.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 27378
:
115544
|
115545
|
115546
|
115547
|
115548
|
115756
|
116397
|
116398
|
116399
|
116400
|
116401
|
116402
|
116403
|
116416
|
116417
|
116418
|
116419
|
116420
|
116421
|
116422
|
116423
|
116978
|
116979
|
116980
|
116981
|
116982
|
116983
|
116984
|
116985
|
116986
|
121728
|
121729
|
121730
|
121731
|
121732
|
121733
|
121734
|
121735
|
121995
|
121996
|
121997
|
121998
|
121999
|
122000
|
122001
|
122002
|
122635
|
122636
|
122637
|
122638
|
122639
|
122640
|
122641
|
122642
|
124957
|
124958
|
124959
|
124960
|
124961
|
124962
|
124963
|
124964
|
124965
|
130072
|
130073
|
130074
|
130075
|
130076
|
130077
|
130078
|
130079
|
130080
|
135309
|
135310
|
135311
|
135312
|
135313
|
135314
|
135315
|
135316
|
135317
|
135318
|
151735
|
151736
|
151737
|
151738
|
151739
|
151740
|
151741
|
151742
|
151743
|
151744
|
151745
|
151746
|
151747
|
151748
|
151749
|
151750
|
151751
|
151752
|
151753
|
151826
|
151827
|
151828
|
151829
|
151830
|
151831
|
151832
|
151833
|
151834
|
151835
|
151836
|
151837
|
151838
|
151839
|
151840
|
151841
|
151842
|
151843
|
151844
|
153326
|
153327
|
153328
|
153329
|
153330
|
153331
|
153332
|
153333
|
153334
|
153335
|
153336
|
154015
|
154016
|
154017
|
154018
|
154019
|
154020
|
154021
|
154022
|
154023
|
154024
|
154025
|
154093
|
154264
|
154265
|
154266
|
154267
|
154268
|
154269
|
154270
|
154271
|
154272
|
154273
|
154274
|
154275
|
154276