Release of v5.1.4
This commit is contained in:
937 files changed
+66265
-53777
No files matched your search
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* @package Joomla.Component.Builder
|
||||
*
|
||||
* @created 30th April, 2015
|
||||
* @author Llewellyn van der Merwe <https://dev.vdm.io>
|
||||
* @git Joomla Component Builder <https://git.vdm.dev/joomla/Component-Builder>
|
||||
* @copyright Copyright (C) 2015 Vast Development Method. All rights reserved.
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
/* CSS Document */
|
||||
|
||||
|
||||
@@ -11,6 +11,6 @@
|
||||
/* CSS Document */
|
||||
|
||||
|
||||
.translation_text_area {
|
||||
.form-control.translation_text_area {
|
||||
width: 100%;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* @package Joomla.Component.Builder
|
||||
*
|
||||
* @created 30th April, 2015
|
||||
* @author Llewellyn van der Merwe <https://dev.vdm.io>
|
||||
* @git Joomla Component Builder <https://git.vdm.dev/joomla/Component-Builder>
|
||||
* @copyright Copyright (C) 2015 Vast Development Method. All rights reserved.
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
/* CSS Document */
|
||||
|
||||
html.dark .no-select,
|
||||
html.dark .no-select td {
|
||||
color: #515151;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
html.light .no-select,
|
||||
html.light .no-select td {
|
||||
color: #999;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
@@ -148,7 +148,7 @@ class InitializationManager {
|
||||
}
|
||||
|
||||
_bindRepoButtons() {
|
||||
document.querySelectorAll('.select-repo-to-initialize').forEach(button => {
|
||||
document.querySelectorAll('.select-repo-to-load').forEach(button => {
|
||||
button.addEventListener('click', (e) => this._handleRepoClick(e));
|
||||
});
|
||||
}
|
||||
@@ -162,17 +162,6 @@ class InitializationManager {
|
||||
}
|
||||
}
|
||||
|
||||
_getInitFunctionName(area) {
|
||||
const powers = [
|
||||
'Joomla.Fieldtype',
|
||||
'Joomla.Power',
|
||||
'Repository',
|
||||
'Power'
|
||||
];
|
||||
|
||||
return powers.includes(area) ? 'initSelectedPowers' : 'initSelectedPackages';
|
||||
}
|
||||
|
||||
async _handleRepoClick(event) {
|
||||
const button = event.currentTarget;
|
||||
const repo = button?.dataset?.repo;
|
||||
@@ -244,7 +233,7 @@ class InitializationManager {
|
||||
const area = this.currentArea || 'error';
|
||||
const repo = this.currentRepo || 'error';
|
||||
|
||||
const func = this._getInitFunctionName(area);
|
||||
const func = 'initSelectedPackages';
|
||||
|
||||
try {
|
||||
// Convert selected items to form data
|
||||
|
||||
@@ -0,0 +1,402 @@
|
||||
/**
|
||||
* @package Joomla.Component.Builder
|
||||
*
|
||||
* @created 30th April, 2015
|
||||
* @author Llewellyn van der Merwe <https://dev.vdm.io>
|
||||
* @git Joomla Component Builder <https://git.vdm.dev/joomla/Component-Builder>
|
||||
* @copyright Copyright (C) 2015 Vast Development Method. All rights reserved.
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
/* JS Document */
|
||||
|
||||
const memorypull = [];
|
||||
|
||||
function setSessionMemory(key, values, merge = true) {
|
||||
if (merge) {
|
||||
values = mergeSessionMemory(key, values);
|
||||
} else {
|
||||
values = JSON.stringify(values);
|
||||
}
|
||||
|
||||
if (typeof Storage !== "undefined") {
|
||||
sessionStorage.setItem(key, values);
|
||||
} else {
|
||||
memorypull[key] = values;
|
||||
}
|
||||
}
|
||||
|
||||
function mergeSessionMemory(key, values) {
|
||||
const oldValues = getSessionMemory(key);
|
||||
|
||||
if (oldValues) {
|
||||
values = { ...oldValues, ...values };
|
||||
}
|
||||
|
||||
return JSON.stringify(values);
|
||||
}
|
||||
|
||||
function getSessionMemory(key, defaultValue = null) {
|
||||
if (typeof Storage !== "undefined") {
|
||||
const localValue = sessionStorage.getItem(key);
|
||||
|
||||
if (isJsonString(localValue)) {
|
||||
defaultValue = JSON.parse(localValue);
|
||||
}
|
||||
} else if (typeof memorypull[key] !== "undefined") {
|
||||
const localValue = memorypull[key];
|
||||
|
||||
if (isJsonString(localValue)) {
|
||||
defaultValue = JSON.parse(localValue);
|
||||
}
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
function isJsonString(str) {
|
||||
try {
|
||||
JSON.parse(str);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function getArrayFormat(items) {
|
||||
// Check if items is an object and not an array
|
||||
if (typeof items === 'object' && !Array.isArray(items)) {
|
||||
return Object.values(items);
|
||||
}
|
||||
return items;
|
||||
}
|
||||
class PullManager {
|
||||
#repoArea = document.getElementById('select-repo-area');
|
||||
#powersArea = document.getElementById('select-powers-area');
|
||||
#pullButton = document.getElementById('pull-selected-powers');
|
||||
#backButton = document.getElementById('back-to-select-repo');
|
||||
#loadingDiv = window.loadingDiv || null;
|
||||
#buildTable = typeof buildPowerSelectionTable === 'function' ? buildPowerSelectionTable : null;
|
||||
#drawTable = typeof drawPowerSelectionTable === 'function' ? drawPowerSelectionTable : null;
|
||||
|
||||
currentRepo = null;
|
||||
currentArea = null;
|
||||
|
||||
constructor() {
|
||||
this._bindRepoButtons();
|
||||
this._bindPullSelectedPowers();
|
||||
this._updatePullButtonState();
|
||||
}
|
||||
|
||||
/** Getter for selected items using global window reference. */
|
||||
get selectedItems() {
|
||||
if (!Array.isArray(window.selectedPowerItems)) {
|
||||
window.selectedPowerItems = [];
|
||||
}
|
||||
return window.selectedPowerItems;
|
||||
}
|
||||
|
||||
/** Setter for selected items with sync to window and button state update. */
|
||||
set selectedItems(items) {
|
||||
window.selectedPowerItems = items;
|
||||
this._updatePullButtonState();
|
||||
}
|
||||
|
||||
/** Add items to selection if not already selected. */
|
||||
addSelectedItems(data) {
|
||||
if (!data || typeof data.length !== 'number') return;
|
||||
|
||||
const updated = [...this.selectedItems];
|
||||
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const item = data[i];
|
||||
if (!updated.some(existing => this.#isSameItem(existing, item))) {
|
||||
updated.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
this.selectedItems = updated;
|
||||
}
|
||||
|
||||
/** Remove items from selection based on identity comparison. */
|
||||
removeSelectedItems(data) {
|
||||
if (!data || typeof data.length !== 'number') return;
|
||||
|
||||
const updated = this.selectedItems.filter(existing => {
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
if (this.#isSameItem(existing, data[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
this.selectedItems = updated;
|
||||
}
|
||||
|
||||
/** Check if two items are the same using GUID. */
|
||||
#isSameItem(a, b) {
|
||||
return a?.guid && b?.guid && a.guid === b.guid;
|
||||
}
|
||||
|
||||
/** Enable/disable the pull button based on selection state. */
|
||||
_updatePullButtonState() {
|
||||
if (this.#pullButton) {
|
||||
this.#pullButton.disabled = this.selectedItems.length === 0;
|
||||
}
|
||||
}
|
||||
|
||||
_bindRepoButtons() {
|
||||
document.querySelectorAll('.select-repo-to-load').forEach(button => {
|
||||
button.addEventListener('click', (e) => this._handleRepoClick(e));
|
||||
});
|
||||
}
|
||||
|
||||
_bindPullSelectedPowers() {
|
||||
if (this.#pullButton) {
|
||||
this.#pullButton.addEventListener('click', () => this._handlePullSelectedPowers());
|
||||
}
|
||||
if (this.#backButton) {
|
||||
this.#backButton.addEventListener('click', () => this._handleBackToRepos());
|
||||
}
|
||||
}
|
||||
|
||||
async _handleRepoClick(event) {
|
||||
const button = event.currentTarget;
|
||||
const repo = button?.dataset?.repo;
|
||||
const area = button?.dataset?.area;
|
||||
|
||||
if (!repo || !area) {
|
||||
this._notify(Joomla.Text._("COM_COMPONENTBUILDER_MISSING_REPOSITORY_OR_AREA_DATA"), "danger");
|
||||
return;
|
||||
}
|
||||
|
||||
this._showLoading();
|
||||
clearPowerSelectionTable();
|
||||
|
||||
const url = `${UrlAjax}getRepoIndex&repo=${encodeURIComponent(repo)}&area=${encodeURIComponent(area)}`;
|
||||
|
||||
try {
|
||||
const response = await fetch(url);
|
||||
|
||||
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success && data.index && this.#buildTable) {
|
||||
const repoData = data.index[0];
|
||||
const {
|
||||
path = 'joomla/super-powers',
|
||||
read_branch = 'master',
|
||||
target,
|
||||
base = 'https://git.vdm.dev'
|
||||
} = repoData;
|
||||
|
||||
const repo_base = (target === 'github') ? 'https://github.com' : base;
|
||||
const repo_path = (target === 'github') ? 'tree' : 'src/branch';
|
||||
|
||||
window.targetPowerRepoUrl = `${repo_base}/${path}/${repo_path}/${read_branch}/`;
|
||||
|
||||
this.#buildTable(repoData.index);
|
||||
|
||||
setTimeout(() => {
|
||||
this._transitionTo(this.#repoArea, this.#powersArea);
|
||||
this._hideLoading();
|
||||
}, 500);
|
||||
|
||||
this.currentRepo = repoData.guid;
|
||||
this.currentArea = area;
|
||||
} else {
|
||||
this._notify(data.message || Joomla.Text._("COM_COMPONENTBUILDER_FAILED_TO_RETRIEVE_REPOSITORY_INDEX"), "danger");
|
||||
this._hideLoading();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Fetch error:", error);
|
||||
this._hideLoading();
|
||||
this._notify(Joomla.Text._("COM_COMPONENTBUILDER_NETWORK_OR_SERVER_ERROR_OCCURRED_WHILE_FETCHING_INDEX"), "danger");
|
||||
}
|
||||
}
|
||||
|
||||
_handleBackToRepos() {
|
||||
this._transitionTo(this.#powersArea, this.#repoArea);
|
||||
}
|
||||
|
||||
async _handlePullSelectedPowers() {
|
||||
if (!Array.isArray(this.selectedItems) || this.selectedItems.length === 0) {
|
||||
this._notify(Joomla.Text._("COM_COMPONENTBUILDER_NO_ITEMS_SELECTED"), "warning");
|
||||
return;
|
||||
}
|
||||
|
||||
this._showLoading();
|
||||
|
||||
const area = this.currentArea || 'error';
|
||||
const repo = this.currentRepo || 'error';
|
||||
|
||||
const func = 'pullSelectedPackages';
|
||||
|
||||
try {
|
||||
// Convert selected items to form data
|
||||
const formData = new FormData();
|
||||
|
||||
// Assuming Joomla expects `selected[]` for multiple values
|
||||
for (const item of this.selectedItems) {
|
||||
formData.append('selected[]', item.guid); // Only sending GUIDs
|
||||
}
|
||||
formData.append('area', area);
|
||||
formData.append('repo', repo);
|
||||
|
||||
const response = await fetch(`${UrlAjax}${func}`, {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (!data.success) {
|
||||
this._notify(data.message || Joomla.Text._("COM_COMPONENTBUILDER_FAILED_TO_PULL_SELECTED_POWERS"), "danger");
|
||||
} else {
|
||||
this._handleResultLog(data.result_log || {});
|
||||
}
|
||||
|
||||
this._hideLoading();
|
||||
this._transitionTo(this.#powersArea, this.#repoArea);
|
||||
} catch (error) {
|
||||
console.error("Submission error:", error);
|
||||
this._notify(Joomla.Text._("COM_COMPONENTBUILDER_ERROR_OCCURRED_WHILE_PULLING_POWERS"), "danger");
|
||||
|
||||
this._hideLoading();
|
||||
this._transitionTo(this.#powersArea, this.#repoArea);
|
||||
}
|
||||
}
|
||||
|
||||
_handleResultLog(resultLog) {
|
||||
const localGuids = this._normalizeGuids(resultLog.local);
|
||||
const notFoundGuids = this._normalizeGuids(resultLog.not_found);
|
||||
const addedGuids = this._normalizeGuids(resultLog.added);
|
||||
|
||||
if (localGuids.length > 0) {
|
||||
this._notify(
|
||||
this._generateResultMessage(localGuids, 'local'),
|
||||
'info'
|
||||
);
|
||||
}
|
||||
|
||||
if (notFoundGuids.length > 0) {
|
||||
this._notify(
|
||||
this._generateResultMessage(notFoundGuids, 'not_found'),
|
||||
'info'
|
||||
);
|
||||
}
|
||||
|
||||
if (addedGuids.length > 0) {
|
||||
this._notify(
|
||||
this._generateResultMessage(addedGuids, 'added'),
|
||||
'success'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
_normalizeGuids(value) {
|
||||
if (!value) return [];
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (typeof value === 'object') {
|
||||
return Object.keys(value);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
_generateResultMessage(guids, type) {
|
||||
const messages = {
|
||||
local: Joomla.Text._('COM_COMPONENTBUILDER_THESE_ITEMS_WERE_ALREADY_PRESENT_LOCALLY_AND_WERE_NOT_PULLED'),
|
||||
not_found: Joomla.Text._('COM_COMPONENTBUILDER_THESE_ITEMS_COULD_NOT_BE_FOUND_IN_THE_REMOTE_REPOSITORY_AND_WERE_NOT_PULLED'),
|
||||
added: Joomla.Text._('COM_COMPONENTBUILDER_THESE_ITEMS_WERE_SUCCESSFULLY_PULLED')
|
||||
};
|
||||
|
||||
const names = [];
|
||||
|
||||
for (const guid of guids) {
|
||||
const item = this.selectedItems.find(i => i.guid === guid);
|
||||
if (item?.name) {
|
||||
names.push(item.name);
|
||||
}
|
||||
}
|
||||
|
||||
if (names.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return `${messages[type]}\n<br>- ${names.join('\n<br>- ')}`;
|
||||
}
|
||||
|
||||
_transitionTo(hideEl, showEl) {
|
||||
if (hideEl && showEl) {
|
||||
UIkit.util.ready(() => {
|
||||
UIkit.util.removeClass(hideEl, 'uk-animation-slide-top-small');
|
||||
UIkit.util.removeClass(showEl, 'uk-animation-slide-bottom-small');
|
||||
UIkit.util.addClass(hideEl, 'uk-animation-slide-top-small');
|
||||
|
||||
setTimeout(() => {
|
||||
hideEl.style.display = 'none';
|
||||
showEl.style.display = '';
|
||||
UIkit.util.addClass(showEl, 'uk-animation-slide-bottom-small');
|
||||
if (this.#drawTable) this.#drawTable();
|
||||
}, 300);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_showLoading() {
|
||||
if (this.#loadingDiv) this.#loadingDiv.style.display = 'block';
|
||||
}
|
||||
|
||||
_hideLoading() {
|
||||
if (this.#loadingDiv) this.#loadingDiv.style.display = 'none';
|
||||
}
|
||||
|
||||
_notify(message, type = 'info') {
|
||||
const alertTypes = {
|
||||
primary: 'alert-primary',
|
||||
info: 'alert-info',
|
||||
success: 'alert-success',
|
||||
warning: 'alert-warning',
|
||||
danger: 'alert-danger',
|
||||
};
|
||||
|
||||
const alertClass = alertTypes[type] || alertTypes.primary;
|
||||
|
||||
let container = document.getElementById('alert-container');
|
||||
if (!container) {
|
||||
container = document.createElement('div');
|
||||
container.id = 'alert-container';
|
||||
container.className = 'position-fixed top-0 start-50 translate-middle-x p-3';
|
||||
container.style.zIndex = '1060';
|
||||
document.body.appendChild(container);
|
||||
}
|
||||
|
||||
const alert = document.createElement('div');
|
||||
alert.className = `alert ${alertClass} alert-dismissible fade show`;
|
||||
alert.setAttribute('role', 'alert');
|
||||
alert.innerHTML = `
|
||||
${message}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
`;
|
||||
|
||||
container.appendChild(alert);
|
||||
|
||||
setTimeout(() => {
|
||||
alert.classList.remove('show');
|
||||
alert.classList.add('hide');
|
||||
alert.addEventListener('transitionend', () => {
|
||||
alert.remove();
|
||||
});
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
+18
-10
@@ -396,7 +396,8 @@ const setValueCheck = (row, field, table) => {
|
||||
const setValue = async (row, field, table) => {
|
||||
try {
|
||||
// get the value from the editor
|
||||
let value = editorObject.getValue();
|
||||
const editor = await GetEditor();
|
||||
let value = editor ? editor.getValue() : '';
|
||||
|
||||
// build form
|
||||
const formData = new FormData();
|
||||
@@ -439,7 +440,10 @@ const addSelectedItem = async (value, table, row, field, line) => {
|
||||
if (value.length > 1)
|
||||
{
|
||||
// add value to editor
|
||||
editorObject.setValue(value);
|
||||
const editor = await GetEditor();
|
||||
if (editor) {
|
||||
editor.setValue(value);
|
||||
}
|
||||
|
||||
// set item details notice area
|
||||
itemNoticeObject.style.display = '';
|
||||
@@ -454,13 +458,14 @@ const addSelectedItem = async (value, table, row, field, line) => {
|
||||
buttonUpdateItemObject.style.display = '';
|
||||
buttonUpdateItemObject.setAttribute('onclick',"setValueCheck(" + row + ", '" + field + "', '" + table + "');");
|
||||
// Get line info from current state.
|
||||
const line_info = editorObject.instance.state.doc.line(line);
|
||||
editorObject.instance.dispatch({
|
||||
// Set selection to that entire line.
|
||||
selection: { head: line_info.from, anchor: line_info.to },
|
||||
// Ensure the selection is shown in viewport
|
||||
scrollIntoView: true
|
||||
});
|
||||
const editor = await GetEditor();
|
||||
if (editor) {
|
||||
const lineInfo = editor.instance.state.doc.line(line);
|
||||
editor.instance.dispatch({
|
||||
selection: { head: lineInfo.from, anchor: lineInfo.to },
|
||||
scrollIntoView: true
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// no line so no data we can't save this data
|
||||
buttonUpdateItemObject.setAttribute('onclick', "");
|
||||
@@ -474,7 +479,10 @@ const addSelectedItem = async (value, table, row, field, line) => {
|
||||
*/
|
||||
const clearSelectedItem = async () => {
|
||||
// display area
|
||||
editorObject.setValue('');
|
||||
const editor = await GetEditor();
|
||||
if (editor) {
|
||||
editor.setValue('');
|
||||
}
|
||||
// clear notice area
|
||||
itemNoticeObject.style.display = 'none';
|
||||
itemEditButtonObject.innerHTML = '...';
|
||||
|
||||
Reference in new issue
Block a user