first commit

This commit is contained in:
2025-06-17 11:53:18 +02:00
commit 9f0f7ba12b
8804 changed files with 1369176 additions and 0 deletions

View File

@ -0,0 +1,79 @@
/**
* @copyright (C) 2021 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
/**
* Provides the manual-run functionality for tasks over the com_scheduler administrator backend.
*
* @package Joomla.Components
* @subpackage Scheduler.Tasks
*
* @since 4.1.0
*/
if (!window.Joomla) {
throw new Error('Joomla API was not properly initialised');
}
const initRunner = () => {
const paths = Joomla.getOptions('system.paths');
const token = Joomla.getOptions('com_scheduler.test-task.token');
const uri = `${paths ? `${paths.base}/index.php` : window.location.pathname}?option=com_ajax&format=json&plugin=RunSchedulerTest&group=system&id=%d${token ? `&${token}=1` : ''}`;
const modal = document.getElementById('scheduler-test-modal');
// Task output template
const template = `
<h4 class="scheduler-headline">${Joomla.Text._('COM_SCHEDULER_TEST_RUN_TASK')}</h4>
<div>${Joomla.Text._('COM_SCHEDULER_TEST_RUN_STATUS_STARTED')}</div>
<div class="mt-3 text-center"><span class="fa fa-spinner fa-spin fa-lg"></span></div>
`;
const sanitiseTaskOutput = text => text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#039;').replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1<br>$2');
// Trigger the task through a GET request, populate the modal with output on completion.
const triggerTaskAndShowOutput = e => {
const button = e.relatedTarget;
const id = parseInt(button.dataset.id, 10);
const {
title
} = button.dataset;
modal.querySelector('.modal-title').innerHTML = Joomla.Text._('COM_SCHEDULER_TEST_RUN_TITLE').replace('%d', id.toString());
modal.querySelector('.modal-body > div').innerHTML = template.replace('%s', title);
Joomla.request({
url: uri.replace('%d', id.toString()),
onSuccess: (data, xhr) => {
[].slice.call(modal.querySelectorAll('.modal-body > div > div')).forEach(el => {
el.parentNode.removeChild(el);
});
const output = JSON.parse(data);
if (output && output.success && output.data) {
modal.querySelector('.modal-body > div').innerHTML += `<div>${Joomla.Text._('COM_SCHEDULER_TEST_RUN_STATUS_COMPLETED')}</div>`;
if (output.data.duration > 0) {
modal.querySelector('.modal-body > div').innerHTML += `<div>${Joomla.Text._('COM_SCHEDULER_TEST_RUN_DURATION').replace('%s', output.data.duration.toFixed(2))}</div>`;
}
if (output.data.output) {
const result = Joomla.sanitizeHtml(output.data.output, null, sanitiseTaskOutput);
// Can use an indication for non-0 exit codes
modal.querySelector('.modal-body > div').innerHTML += `<div>${Joomla.Text._('COM_SCHEDULER_TEST_RUN_OUTPUT').replace('%s', result)}</div>`;
}
} else {
modal.querySelector('.modal-body > div').innerHTML += `<div>${Joomla.Text._('COM_SCHEDULER_TEST_RUN_STATUS_TERMINATED')}</div>`;
modal.querySelector('.modal-body > div').innerHTML += `<div>${Joomla.Text._('COM_SCHEDULER_TEST_RUN_OUTPUT').replace('%s', Joomla.Text._('JLIB_JS_AJAX_ERROR_OTHER').replace('%s', xhr.status))}</div>`;
}
},
onError: xhr => {
modal.querySelector('.modal-body > div').innerHTML += `<div>${Joomla.Text._('COM_SCHEDULER_TEST_RUN_STATUS_TERMINATED')}</div>`;
const msg = Joomla.ajaxErrorsMessages(xhr);
modal.querySelector('.modal-body > div').innerHTML += `<div>${Joomla.Text._('COM_SCHEDULER_TEST_RUN_OUTPUT').replace('%s', msg.error)}</div>`;
}
});
};
const reloadOnClose = () => {
window.location.href = `${paths ? `${paths.base}/index.php` : window.location.pathname}?option=com_scheduler&view=tasks`;
};
if (modal) {
modal.addEventListener('show.bs.modal', triggerTaskAndShowOutput);
modal.addEventListener('hidden.bs.modal', reloadOnClose);
}
document.removeEventListener('DOMContentLoaded', initRunner);
};
document.addEventListener('DOMContentLoaded', initRunner);

View File

@ -0,0 +1 @@
if(!window.Joomla)throw new Error("Joomla API was not properly initialised");const initRunner=()=>{const e=Joomla.getOptions("system.paths"),o=Joomla.getOptions("com_scheduler.test-task.token"),t=`${e?`${e.base}/index.php`:window.location.pathname}?option=com_ajax&format=json&plugin=RunSchedulerTest&group=system&id=%d${o?`&${o}=1`:""}`,a=document.getElementById("scheduler-test-modal"),d=`\n <h4 class="scheduler-headline">${Joomla.Text._("COM_SCHEDULER_TEST_RUN_TASK")}</h4>\n <div>${Joomla.Text._("COM_SCHEDULER_TEST_RUN_STATUS_STARTED")}</div>\n <div class="mt-3 text-center"><span class="fa fa-spinner fa-spin fa-lg"></span></div>\n `,n=e=>e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;").replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g,"$1<br>$2"),r=e=>{const o=e.relatedTarget,r=parseInt(o.dataset.id,10),{title:l}=o.dataset;a.querySelector(".modal-title").innerHTML=Joomla.Text._("COM_SCHEDULER_TEST_RUN_TITLE").replace("%d",r.toString()),a.querySelector(".modal-body > div").innerHTML=d.replace("%s",l),Joomla.request({url:t.replace("%d",r.toString()),onSuccess:(e,o)=>{[].slice.call(a.querySelectorAll(".modal-body > div > div")).forEach((e=>{e.parentNode.removeChild(e)}));const t=JSON.parse(e);if(t&&t.success&&t.data){if(a.querySelector(".modal-body > div").innerHTML+=`<div>${Joomla.Text._("COM_SCHEDULER_TEST_RUN_STATUS_COMPLETED")}</div>`,t.data.duration>0&&(a.querySelector(".modal-body > div").innerHTML+=`<div>${Joomla.Text._("COM_SCHEDULER_TEST_RUN_DURATION").replace("%s",t.data.duration.toFixed(2))}</div>`),t.data.output){const e=Joomla.sanitizeHtml(t.data.output,null,n);a.querySelector(".modal-body > div").innerHTML+=`<div>${Joomla.Text._("COM_SCHEDULER_TEST_RUN_OUTPUT").replace("%s",e)}</div>`}}else a.querySelector(".modal-body > div").innerHTML+=`<div>${Joomla.Text._("COM_SCHEDULER_TEST_RUN_STATUS_TERMINATED")}</div>`,a.querySelector(".modal-body > div").innerHTML+=`<div>${Joomla.Text._("COM_SCHEDULER_TEST_RUN_OUTPUT").replace("%s",Joomla.Text._("JLIB_JS_AJAX_ERROR_OTHER").replace("%s",o.status))}</div>`},onError:e=>{a.querySelector(".modal-body > div").innerHTML+=`<div>${Joomla.Text._("COM_SCHEDULER_TEST_RUN_STATUS_TERMINATED")}</div>`;const o=Joomla.ajaxErrorsMessages(e);a.querySelector(".modal-body > div").innerHTML+=`<div>${Joomla.Text._("COM_SCHEDULER_TEST_RUN_OUTPUT").replace("%s",o.error)}</div>`}})},l=()=>{window.location.href=`${e?`${e.base}/index.php`:window.location.pathname}?option=com_scheduler&view=tasks`};a&&(a.addEventListener("show.bs.modal",r),a.addEventListener("hidden.bs.modal",l)),document.removeEventListener("DOMContentLoaded",initRunner)};document.addEventListener("DOMContentLoaded",initRunner);

View File

@ -0,0 +1,103 @@
/**
* @copyright (C) 2021 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
/**
* Add a keyboard event listener to the Select a Task Type search element.
*
* IMPORTANT! This script is meant to be loaded deferred. This means that a. it's non-blocking
* (the browser can load it whenever) and b. it doesn't need an on DOMContentLoaded event handler
* because the browser is guaranteed to execute it only after the DOM content has loaded, the
* whole point of it being deferred.
*
* The search box has a keyboard handler that fires every time you press a keyboard button or send
* a keypress with a touch / virtual keyboard. We then iterate all task type cards and check if
* the plain text (HTML stripped out) representation of the task title or description partially
* matches the text you entered in the search box. If it doesn't we add a Bootstrap class to hide
* the task.
*
* This way we limit the displayed tasks only to those searched.
*
* This feature follows progressive enhancement. The search box is hidden by default and only
* displayed when this JavaScript here executes. Furthermore, session storage is only used if it
* is available in the browser. That's a bit of a pain but makes sure things won't break in older
* browsers.
*
* Furthermore and to facilitate the user experience we auto-focus the search element which has a
* suitable title so that non-sighted users are not startled. This way we address both UX concerns
* and accessibility.
*
* Finally, the search string is saved into session storage on the assumption that the user is
* probably going to be creating multiple instances of the same task, one after another, as is
* typical when building a new Joomla! site.
*/
// Make sure the element exists i.e. a template override has not removed it.
const elSearch = document.getElementById('comSchedulerSelectSearch');
const elSearchContainer = document.getElementById('comSchedulerSelectSearchContainer');
const elSearchHeader = document.getElementById('comSchedulerSelectTypeHeader');
const elSearchResults = document.getElementById('comSchedulerSelectResultsContainer');
const alertElement = document.querySelector('.tasks-alert');
const elCards = [].slice.call(document.querySelectorAll('.comSchedulerSelectCard'));
if (elSearch && elSearchContainer) {
// Add the keyboard event listener which performs the live search in the cards
elSearch.addEventListener('keyup', ({
target
}) => {
/** @type {KeyboardEvent} event */
const partialSearch = target.value;
let hasSearchResults = false; // Save the search string into session storage
if (typeof sessionStorage !== 'undefined') {
sessionStorage.setItem('Joomla.com_scheduler.new.search', partialSearch);
}
// Iterate over all the task cards
elCards.forEach(card => {
// First remove the class which hide the task cards
card.classList.remove('d-none');
// An empty search string means that we should show everything
if (!partialSearch) {
return;
}
const cardHeader = card.querySelector('.new-task-title');
const cardBody = card.querySelector('.new-task-caption');
const title = cardHeader ? cardHeader.textContent : '';
const description = cardBody ? cardBody.textContent : '';
// If the task title and description dont match add a class to hide it.
if (title && !title.toLowerCase().includes(partialSearch.toLowerCase()) && description && !description.toLowerCase().includes(partialSearch.toLowerCase())) {
card.classList.add('d-none');
} else {
hasSearchResults = true;
}
});
if (hasSearchResults || !partialSearch) {
alertElement.classList.add('d-none');
elSearchHeader.classList.remove('d-none');
elSearchResults.classList.remove('d-none');
} else {
alertElement.classList.remove('d-none');
elSearchHeader.classList.add('d-none');
elSearchResults.classList.add('d-none');
}
});
// For reasons of progressive enhancement the search box is hidden by default.
elSearchContainer.classList.remove('d-none');
// Focus the just show element
elSearch.focus();
try {
if (typeof sessionStorage !== 'undefined') {
// Load the search string from session storage
elSearch.value = sessionStorage.getItem('Joomla.com_scheduler.new.search') || '';
// Trigger the keyboard handler event manually to initiate the search
elSearch.dispatchEvent(new KeyboardEvent('keyup'));
}
} catch (e) {
// This is probably Internet Explorer which doesn't support the KeyboardEvent constructor :(
}
}

View File

@ -0,0 +1 @@
const elSearch=document.getElementById("comSchedulerSelectSearch"),elSearchContainer=document.getElementById("comSchedulerSelectSearchContainer"),elSearchHeader=document.getElementById("comSchedulerSelectTypeHeader"),elSearchResults=document.getElementById("comSchedulerSelectResultsContainer"),alertElement=document.querySelector(".tasks-alert"),elCards=[].slice.call(document.querySelectorAll(".comSchedulerSelectCard"));if(elSearch&&elSearchContainer){elSearch.addEventListener("keyup",(({target:e})=>{const t=e.value;let a=!1;"undefined"!=typeof sessionStorage&&sessionStorage.setItem("Joomla.com_scheduler.new.search",t),elCards.forEach((e=>{if(e.classList.remove("d-none"),!t)return;const s=e.querySelector(".new-task-title"),c=e.querySelector(".new-task-caption"),l=s?s.textContent:"",r=c?c.textContent:"";l&&!l.toLowerCase().includes(t.toLowerCase())&&r&&!r.toLowerCase().includes(t.toLowerCase())?e.classList.add("d-none"):a=!0})),a||!t?(alertElement.classList.add("d-none"),elSearchHeader.classList.remove("d-none"),elSearchResults.classList.remove("d-none")):(alertElement.classList.remove("d-none"),elSearchHeader.classList.add("d-none"),elSearchResults.classList.add("d-none"))})),elSearchContainer.classList.remove("d-none"),elSearch.focus();try{"undefined"!=typeof sessionStorage&&(elSearch.value=sessionStorage.getItem("Joomla.com_scheduler.new.search")||"",elSearch.dispatchEvent(new KeyboardEvent("keyup")))}catch(e){}}

View File

@ -0,0 +1,54 @@
/**
* @copyright (C) 2021 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
if (!window.Joomla) {
throw new Error('Joomla API was not properly initialised!');
}
const copyToClipboardFallback = input => {
input.focus();
input.select();
try {
const copy = document.execCommand('copy');
if (copy) {
Joomla.renderMessages({
message: [Joomla.Text._('COM_SCHEDULER_CONFIG_WEBCRON_LINK_COPY_SUCCESS')]
});
} else {
Joomla.renderMessages({
error: [Joomla.Text._('COM_SCHEDULER_CONFIG_WEBCRON_LINK_COPY_FAIL')]
});
}
} catch (err) {
Joomla.renderMessages({
error: [err]
});
}
};
const copyToClipboard = () => {
const button = document.getElementById('link-copy');
button.addEventListener('click', ({
currentTarget
}) => {
const input = currentTarget.previousElementSibling;
if (!navigator.clipboard) {
copyToClipboardFallback(input);
return;
}
navigator.clipboard.writeText(input.value).then(() => {
Joomla.renderMessages({
message: [Joomla.Text._('COM_SCHEDULER_CONFIG_WEBCRON_LINK_COPY_SUCCESS')]
});
}, () => {
Joomla.renderMessages({
error: [Joomla.Text._('COM_SCHEDULER_CONFIG_WEBCRON_LINK_COPY_FAIL')]
});
});
});
};
const onBoot = () => {
copyToClipboard();
document.removeEventListener('DOMContentLoaded', onBoot);
};
document.addEventListener('DOMContentLoaded', onBoot);

View File

@ -0,0 +1 @@
if(!window.Joomla)throw new Error("Joomla API was not properly initialised!");const copyToClipboardFallback=e=>{e.focus(),e.select();try{document.execCommand("copy")?Joomla.renderMessages({message:[Joomla.Text._("COM_SCHEDULER_CONFIG_WEBCRON_LINK_COPY_SUCCESS")]}):Joomla.renderMessages({error:[Joomla.Text._("COM_SCHEDULER_CONFIG_WEBCRON_LINK_COPY_FAIL")]})}catch(e){Joomla.renderMessages({error:[e]})}},copyToClipboard=()=>{document.getElementById("link-copy").addEventListener("click",(({currentTarget:e})=>{const o=e.previousElementSibling;navigator.clipboard?navigator.clipboard.writeText(o.value).then((()=>{Joomla.renderMessages({message:[Joomla.Text._("COM_SCHEDULER_CONFIG_WEBCRON_LINK_COPY_SUCCESS")]})}),(()=>{Joomla.renderMessages({error:[Joomla.Text._("COM_SCHEDULER_CONFIG_WEBCRON_LINK_COPY_FAIL")]})})):copyToClipboardFallback(o)}))},onBoot=()=>{document.getElementById("link-copy").addEventListener("click",(({currentTarget:e})=>{const o=e.previousElementSibling;navigator.clipboard?navigator.clipboard.writeText(o.value).then((()=>{Joomla.renderMessages({message:[Joomla.Text._("COM_SCHEDULER_CONFIG_WEBCRON_LINK_COPY_SUCCESS")]})}),(()=>{Joomla.renderMessages({error:[Joomla.Text._("COM_SCHEDULER_CONFIG_WEBCRON_LINK_COPY_FAIL")]})})):copyToClipboardFallback(o)})),document.removeEventListener("DOMContentLoaded",onBoot)};document.addEventListener("DOMContentLoaded",onBoot);

Binary file not shown.