diff --git a/htdocs/js/loris-scripts.js b/htdocs/js/loris-scripts.js index d07ae39f48..f49b6e74f9 100644 --- a/htdocs/js/loris-scripts.js +++ b/htdocs/js/loris-scripts.js @@ -1,5 +1,70 @@ /* eslint new-cap: ["error", {capIsNewExceptions: ["DynamicTable", "FileUpload"]}]*/ +/** + * Display the login modal when a request returns a 401 response. + */ +function handleUnauthorized() { + if (!window.$ || !window.loris) { + return; + } + + if (!$('#login-modal').length) { + return; + } + + if ($('#login-modal').hasClass('in')) { + $('#login-modal-error').show(); + return; + } + + $('#login-modal').modal('show'); + $('#modal-login') + .off('click.lorisFetch') + .on('click.lorisFetch', function(e) { + e.preventDefault(); + let data = { + username: $('#modal-username').val(), + password: $('#modal-password').val(), + login: 'Login', + }; + + window.lorisFetch(window.loris.BaseURL, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', + }, + body: new URLSearchParams(data), + }) + .then((response) => { + if (!response.ok) { + throw new Error('request_failed'); + } + $('#login-modal-error').hide(); + $('#login-modal').modal('hide'); + }) + .catch(() => { + $('#login-modal-error').show(); + }); + }); +} + +if (!window.lorisFetch) { + window.lorisFetch = function(input, init) { + const options = Object.assign( + { + credentials: 'same-origin', + }, + init || {} + ); + return fetch(input, options).then((response) => { + if (response.status === 401) { + handleUnauthorized(); + } + return response; + }); + }; +} + $(document).ready(function() { $('#menu-toggle').click(function(e) { e.preventDefault(); @@ -8,30 +73,3 @@ $(document).ready(function() { $('.dynamictable').DynamicTable(); $('.fileUpload').FileUpload(); }); - -$(document).ajaxError(function(event, jqxhr, settings, thrownError) { - if (jqxhr.status === 401) { - if ($('#login-modal').hasClass('in')) { - $('#login-modal-error').show(); - } else { - $('#login-modal').modal('show'); - $('#modal-login').click(function(e) { - e.preventDefault(); - let data = { - username: $('#modal-username').val(), - password: $('#modal-password').val(), - login: 'Login', - }; - $.ajax({ - type: 'post', - url: loris.BaseURL, - data: data, - success: function() { - $('#login-modal-error').hide(); - $('#login-modal').modal('hide'); - }, - }); - }); - } - } -}); diff --git a/jslib/lorisFetch.js b/jslib/lorisFetch.js new file mode 100644 index 0000000000..1aba03792c --- /dev/null +++ b/jslib/lorisFetch.js @@ -0,0 +1,81 @@ +/** + * Display the login modal when a request returns a 401 response. + */ +function handleUnauthorized() { + if (typeof window === 'undefined') { + return; + } + if (!window.$ || !window.loris) { + return; + } + + const $ = window.$; + if (!$('#login-modal').length) { + return; + } + + if ($('#login-modal').hasClass('in')) { + $('#login-modal-error').show(); + return; + } + + $('#login-modal').modal('show'); + $('#modal-login') + .off('click.lorisFetch') + .on('click.lorisFetch', function(e) { + e.preventDefault(); + let data = { + username: $('#modal-username').val(), + password: $('#modal-password').val(), + login: 'Login', + }; + + fetch(window.loris.BaseURL, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', + }, + body: new URLSearchParams(data), + credentials: 'same-origin', + }) + .then((response) => { + if (!response.ok) { + throw new Error('request_failed'); + } + $('#login-modal-error').hide(); + $('#login-modal').modal('hide'); + }) + .catch(() => { + $('#login-modal-error').show(); + }); + }); +} + +/** + * Wrapper around fetch that keeps credentials and handles 401s. + * + * @param {*} input + * @param {object=} init + * @return {Promise} + */ +function lorisFetch(input, init) { + const options = Object.assign( + { + credentials: 'same-origin', + }, + init || {} + ); + + return fetch(input, options).then((response) => { + if (response.status === 401) { + handleUnauthorized(); + } + return response; + }); +} + +if (typeof window !== 'undefined') { + window.lorisFetch = lorisFetch; +} + +export default lorisFetch; diff --git a/modules/configuration/js/cohort.js b/modules/configuration/js/cohort.js index aa7070bf74..fc0a8ff803 100644 --- a/modules/configuration/js/cohort.js +++ b/modules/configuration/js/cohort.js @@ -1,5 +1,6 @@ $(document).ready(function() { "use strict"; + var lorisFetch = window.lorisFetch || fetch; $('div').tooltip(); $(".savecohort").click(function(e) { var form = $(e.currentTarget).closest('form'); @@ -11,48 +12,58 @@ $(document).ready(function() { var recruitmentTarget = $(form.find(".cohortRecruitmentTarget")).val(); e.preventDefault(); - $.ajax( - { - "type" : "post", - "url" : loris.BaseURL + "/configuration/ajax/updateCohort.php", - "data" : { + lorisFetch(loris.BaseURL + "/configuration/ajax/updateCohort.php", { + method: "post", + headers: { + "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", + }, + body: new URLSearchParams({ "cohortID" : cohortID, "title" : title, "useEDC" : useEDC, "WindowDifference" : windowDifference, "RecruitmentTarget" : recruitmentTarget, - }, - "dataType": "json", - "success" : function(data) { - $(form.find(".saveStatus")) - .text(data.ok) - .css({ 'color': 'green'}) - .fadeIn(500) - .delay(1000); - if (cohortID === 'new') { - setTimeout(function(){ - location.reload(); - }, 1000); - } else { - var projectDiv = document.getElementById(`#cohort${cohortID}`); - var Name = projectDiv.innerText; - projectDiv.innerText = title; - var projectHeader = document.getElementById(`cohort${cohortID}`); - projectHeader.children[0].innerText = title + projectHeader.children[0].innerText.substring( - Name.length - ); - } - }, - "error" : function(data) { - $(form.find(".saveStatus")) - .text(data.responseJSON.error) - .css({ 'color': 'red'}) - .fadeIn(500) - .delay(1000); + }), + credentials: "same-origin", + }) + .then(async function(response) { + var text = await response.text(); + var data = null; + try { + data = JSON.parse(text); + } catch (err) { + data = null; } - } - - ); + if (!response.ok) { + throw {error: (data && data.error) ? data.error : text}; + } + $(form.find(".saveStatus")) + .text(data && data.ok ? data.ok : "Saved") + .css({ 'color': 'green'}) + .fadeIn(500) + .delay(1000); + if (cohortID === 'new') { + setTimeout(function(){ + location.reload(); + }, 1000); + } else { + var projectDiv = document.getElementById(`#cohort${cohortID}`); + var Name = projectDiv.innerText; + projectDiv.innerText = title; + var projectHeader = document.getElementById(`cohort${cohortID}`); + projectHeader.children[0].innerText = title + projectHeader.children[0].innerText.substring( + Name.length + ); + } + }) + .catch(function(data) { + var message = data && data.error ? data.error : 'Request failed.'; + $(form.find(".saveStatus")) + .text(message) + .css({ 'color': 'red'}) + .fadeIn(500) + .delay(1000); + }); }); diff --git a/modules/configuration/jsx/configuration_helper.js b/modules/configuration/jsx/configuration_helper.js index 010d406bea..144523badc 100644 --- a/modules/configuration/jsx/configuration_helper.js +++ b/modules/configuration/jsx/configuration_helper.js @@ -1,4 +1,5 @@ import swal from 'sweetalert2'; +import lorisFetch from 'jslib/lorisFetch'; $(function() { 'use strict'; @@ -59,11 +60,17 @@ $(function() { let id = $(this).attr('name'); let button = this; - $.ajax({ - type: 'post', - url: loris.BaseURL + '/configuration/ajax/process.php', - data: {remove: id}, - success: function() { + lorisFetch(loris.BaseURL + '/configuration/ajax/process.php', { + method: 'post', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', + }, + body: new URLSearchParams({remove: id}), + }) + .then((response) => { + if (!response.ok) { + throw new Error('request_failed'); + } if ($(button) .parent().parent().parent().children() .length > 1 @@ -83,12 +90,10 @@ $(function() { .addClass('remove-new') .removeClass('btn-remove'); } - }, - error: function(xhr, desc, err) { - console.error(xhr); - console.error('Details: ' + desc + '\nError:' + err); - }, - }); + }) + .catch((err) => { + console.error(err); + }); } }); }); @@ -102,23 +107,31 @@ $(function() { // Clear previous feedback $('.submit-area > label').remove(); - $.ajax({ - type: 'post', - url: loris.BaseURL + '/configuration/ajax/process.php', - data: form, - success: function() { + lorisFetch(loris.BaseURL + '/configuration/ajax/process.php', { + method: 'post', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', + }, + body: form, + }) + .then(async (response) => { + if (!response.ok) { + let text = await response.text(); + let error = new Error('request_failed'); + error.lorisMessage = text; + throw error; + } let html = ''; $(html) .hide() .appendTo('.submit-area') .fadeIn(500).delay(1000).fadeOut(500); location.reload(); - }, - error: function(xhr, desc, err) { - let html = ''; + }) + .catch((error) => { + let html = ''; $(html).hide().appendTo('.submit-area').fadeIn(500).delay(1000); - }, - }); + }); }); // On form reset, to delete the elements added with the "Add field" button that were not submitted.