Release of v5.1.1-rc1

Move all banners to GitHub. Adds library phpspreadsheet to JCB. Adds import item example to demo component. Updates the Superpower class with the GetRemote class in the plugin. Ensures the super power autoloader triggers the correct repositories. Adds the ModalSelect fieldtype to Joomla Component Builder - J5. Adds the Data Import Function to the Demo Component. Adds new country related tables and fields to the Demo Component. Resolves the Database Updating issue in the compiler. #1212,#1209. Adds the Component Commands Plugin to the CLI for Import of spreadsheet data-sets. Add edit and create options to the ModalSelect Field (in Joomla 5). Add all needed Powers to the release package, to speed-up the build of the demo component. Refactor initialization flow to accommodate future scalability and integration with all designated areas. Refactor the Creator Builders class. Refactor the FieldString and FieldXML classes. Add JCB new package engine. Enhance operator support in dynamic get system. Relates to issue #1226. Fixes issue with loading the Component Builder Wiki. Adds advanced version update notice to the Component Builder Dashboard. Completely refactors the class that builds the Component Dashboard. #1134. Fix the FieldXML interface mismatch. #1228. Adds Initialize, Reset, and Push functionality to the Repository entities. Completely refactors the SQL tweaks and SQL dump classes. Fix bug in the filter of Languages by linked entities. #1230.
This commit is contained in:
2025-07-02 19:03:37 +00:00
parent 1702bd1f0c
commit 087e3a7c19
26 changed files with 795 additions and 980 deletions

View File

@ -1,8 +1,4 @@
# v5.1.1-beta6
- Fix bug in the filter of Languages by linked entities. #1230
# v5.1.1-beta
# v5.1.1-rc1
- Move all banners to GitHub.
- Adds library phpspreadsheet to JCB.
@ -26,7 +22,8 @@
- Completely refactors the class that builds the Component Dashboard. #1134
- Fix the FieldXML interface mismatch. #1228
- Adds Initialize, Reset, and Push functionality to the Repository entities.
- Completely refactors the SQL tweaks and SQL dump classes.
- Completely refactors the SQL tweaks and SQL dump classes.
- Fix bug in the filter of Languages by linked entities. #1230
# v5.1.0

View File

@ -3292,7 +3292,7 @@ class Com_ComponentbuilderInstallerScript implements InstallerScriptInterface
echo '<div style="background-color: #fff;" class="alert alert-info"><a target="_blank" href="https://dev.vdm.io" title="Component Builder">
<img src="components/com_componentbuilder/assets/images/vdm-component.jpg"/>
</a>
<h3>Upgrade to Version 5.1.1-beta6 Was Successful! Let us know if anything is not working as expected.</h3></div>';
<h3>Upgrade to Version 5.1.1-rc1 Was Successful! Let us know if anything is not working as expected.</h3></div>';
// Add/Update component in the action logs extensions table.
$this->setActionLogsExtensions();

View File

@ -9,7 +9,7 @@ This is a professional-grade [Joomla 5.x](https://extensions.joomla.org/extensio
JCB generates native Joomla components, plugins, and modules for Joomla 3.x, 4.x, and 5.x - and is already prepared for Joomla 6. Every compiled project is tailored for the specific version without needing backward compatibility plugins. With integrated version-aware compiling, smart boilerplating, and Git-powered project syncing, JCB is much more than a code generator-it's a **full-stack development pipeline for Joomla extensions**.
You can install this component easily. The latest release (**5.1.1-beta6**) is available on [Releases](https://git.vdm.dev/joomla/pkg-component-builder/releases) and updated frequently with full source access.
You can install this component easily. The latest release (**5.1.1-rc1**) is available on [Releases](https://git.vdm.dev/joomla/pkg-component-builder/releases) and updated frequently with full source access.
Upgrades are seamless through Joomla's built-in extension update mechanism.
@ -229,9 +229,9 @@ JCB is developed by developers for developers. Its purpose is to democratize hig
* **Company:** [Vast Development Method](https://dev.vdm.io)
* **Author:** [Llewellyn van der Merwe](mailto:joomla@vdm.io)
* **Component:** [Component Builder](https://git.vdm.dev/joomla/Component-Builder)
* **Created:** 30th April, 2015 · **Last Build:** 1st July, 2025 · **Version:** 5.1.1-beta6
* **Created:** 30th April, 2015 · **Last Build:** 2nd July, 2025 · **Version:** 5.1.1-rc1
* **License:** GNU General Public License version 2 or later; see LICENSE.txt · **Copyright:** Copyright (C) 2015 Vast Development Method. All rights reserved.
* **Lines:** 1094999 · **Fields:** 2096 · **Files:** 7514 · **Folders:** 728
* **Lines:** 1094989 · **Fields:** 2096 · **Files:** 7515 · **Folders:** 728
> Generated with [JCB](https://www.joomlacomponentbuilder.com) — The Smartest Way to Build Joomla Extensions.

View File

@ -9,7 +9,7 @@ This is a professional-grade [Joomla 5.x](https://extensions.joomla.org/extensio
JCB generates native Joomla components, plugins, and modules for Joomla 3.x, 4.x, and 5.x - and is already prepared for Joomla 6. Every compiled project is tailored for the specific version without needing backward compatibility plugins. With integrated version-aware compiling, smart boilerplating, and Git-powered project syncing, JCB is much more than a code generator-it's a **full-stack development pipeline for Joomla extensions**.
You can install this component easily. The latest release (**5.1.1-beta6**) is available on [Releases](https://git.vdm.dev/joomla/pkg-component-builder/releases) and updated frequently with full source access.
You can install this component easily. The latest release (**5.1.1-rc1**) is available on [Releases](https://git.vdm.dev/joomla/pkg-component-builder/releases) and updated frequently with full source access.
Upgrades are seamless through Joomla's built-in extension update mechanism.
@ -229,9 +229,9 @@ JCB is developed by developers for developers. Its purpose is to democratize hig
* **Company:** [Vast Development Method](https://dev.vdm.io)
* **Author:** [Llewellyn van der Merwe](mailto:joomla@vdm.io)
* **Component:** [Component Builder](https://git.vdm.dev/joomla/Component-Builder)
* **Created:** 30th April, 2015 · **Last Build:** 1st July, 2025 · **Version:** 5.1.1-beta6
* **Created:** 30th April, 2015 · **Last Build:** 2nd July, 2025 · **Version:** 5.1.1-rc1
* **License:** GNU General Public License version 2 or later; see LICENSE.txt · **Copyright:** Copyright (C) 2015 Vast Development Method. All rights reserved.
* **Lines:** 1094999 · **Fields:** 2096 · **Files:** 7514 · **Folders:** 728
* **Lines:** 1094989 · **Fields:** 2096 · **Files:** 7515 · **Folders:** 728
> Generated with [JCB](https://www.joomlacomponentbuilder.com) — The Smartest Way to Build Joomla Extensions.

View File

@ -423,7 +423,7 @@ abstract class ###Component###Email
'<title>' . htmlspecialchars($subject) . '</title>',
'<style type="text/css">',
'#outlook a {padding:0;} .ExternalClass {width:100%;} .ExternalClass, .ExternalClass p, .ExternalClass span, .ExternalClass font, .ExternalClass td, .ExternalClass div {line-height:100%;}',
'p {margin: 0; padding: 0; font-size: 0px; line-height: 0px;}',
// 'p {margin: 0; padding: 0; font-size: 0px; line-height: 0px;}',
'table, table td {border-collapse: collapse;}',
'img {display:block; outline:none; text-decoration:none; -ms-interpolation-mode:bicubic;}',
'a img {border:none;} a {text-decoration:none; color:#000001;} a.phone {pointer-events:auto; cursor:default; color:#000001 !important;}',
@ -459,7 +459,7 @@ abstract class ###Component###Email
'<title>' . htmlspecialchars($subject) . '</title>',
'<style type="text/css">',
'#outlook a {padding:0;} .ExternalClass {width:100%;} .ExternalClass, .ExternalClass p, .ExternalClass span, .ExternalClass font, .ExternalClass td, .ExternalClass div {line-height:100%;}',
'p {margin: 0; padding: 0; font-size: 0px; line-height: 0px;}',
// 'p {margin: 0; padding: 0; font-size: 0px; line-height: 0px;}',
'table, table td {border-collapse: collapse;}',
'img {display:block; outline:none; text-decoration:none; -ms-interpolation-mode:bicubic;}',
'a img {border:none;} a {text-decoration:none; color:#000001;} a.phone {pointer-events:auto; cursor:default; color:#000001 !important;}',

View File

@ -51,6 +51,13 @@
multiple="false"
class="js-select-submit-on-change"
/>
<field
type="repositoriesfiltertype"
name="type"
label="COM_COMPONENTBUILDER_REPOSITORY_TYPE_LABEL"
multiple="false"
class="js-select-submit-on-change"
/>
<field
type="repositoriesfilterbase"
name="base"

View File

@ -1306,6 +1306,7 @@ COM_COMPONENTBUILDER_BGET_THE_KEY_FROMB_A_SSA="<b>Get the key from</b> <a %s>%s<
COM_COMPONENTBUILDER_BGET_THE_KEY_FROM_SB_FOR_A_SSA="<b>Get the key from %s</b> for <a %s>%s</a>"
COM_COMPONENTBUILDER_BIMAGESB_NOT_MOVED_TO_CORRECT_LOCATION="<b>Images</b> not moved to correct location!"
COM_COMPONENTBUILDER_BMULTIPLE_FIELD_REPEATABLE_MODEB_IDS_MISMATCH_IN_BFIELDSB_AND_WAS_EMREMOVEDEM_FROM_THE_FIELD="<b>Multiple Field (repeatable mode)</b> id:%s mismatch in <b>field:%s</b>, and was <em>removed</em> from the field."
COM_COMPONENTBUILDER_BOTH_BRANCHES_SET="Both Branches Set"
COM_COMPONENTBUILDER_BRANCH="Branch"
COM_COMPONENTBUILDER_BSBS_IN_BSB_HAS_ID_MISMATCH_SO_THE_BSB_WAS_REMOVED="<b>%s</b>->%s in <b>%s</b> has id mismatch. So the <b>%s</b> was removed!"
COM_COMPONENTBUILDER_BSB_COULD_NOT_BE_IMPORTEDS="<b>%s</b> could not be imported%s"
@ -4610,6 +4611,7 @@ COM_COMPONENTBUILDER_EXAMPLE="Example"
COM_COMPONENTBUILDER_EXIT_TRASH="Exit trash"
COM_COMPONENTBUILDER_EXPLORE_LEARN_AND_CREATE_WITH_LLEWELLYN_ON_YOUTUBE_YOUR_GATEWAY_TO_INSPIRATION="Explore, Learn, and Create with Llewellyn on YouTube: Your Gateway to Inspiration!"
COM_COMPONENTBUILDER_EXPORTIMPORT_DATA="Export/Import Data"
COM_COMPONENTBUILDER_EXPORT_TRANSLATIONS="Export Translations"
COM_COMPONENTBUILDER_EXTENDS="Extends"
COM_COMPONENTBUILDER_EXTRA_PROPERTIES_LIKE_LISTCLASS_ESCAPE_DISPLAY_VALIDATEBR_SMALLHERE_YOU_CAN_SET_THE_EXTRA_PROPERTIES_FOR_THIS_FIELDSMALL="Extra properties like (listclass, escape, display, validate)<br /><small>Here you can set the extra properties for this field</small>"
COM_COMPONENTBUILDER_FADE_IN="Fade In"
@ -7627,6 +7629,7 @@ COM_COMPONENTBUILDER_NO_ACCESS_GRANTED="No Access Granted!"
COM_COMPONENTBUILDER_NO_ACTIVE_REPOSITORIES_FOUND="No Active Repositories Found"
COM_COMPONENTBUILDER_NO_ACTIVE_REPOSITORIES_FOUND_FOR_THIS_AREA_YOU_CAN_ADD_REPOSITORIES_IN_THE_REPOSITORIES_SECTION_OF_JCB="No active repositories found for this area. You can add repositories in the Repositories section of JCB."
COM_COMPONENTBUILDER_NO_ADMIN_VIEWS_FOUND="No Admin Views Found"
COM_COMPONENTBUILDER_NO_BRANCHES_SET="No Branches Set"
COM_COMPONENTBUILDER_NO_CHANGE_S_ITEM_S_IN_REPO_S_IS_ALREADY_IN_SYNC="NO CHANGE: %s item [%s] in repo (%s) is already in sync."
COM_COMPONENTBUILDER_NO_COMPONENTS_FOUND="No Components Found"
COM_COMPONENTBUILDER_NO_COMPONENT_DETAILS_FOUND_SO_IT_IS_NOT_SAFE_TO_CONTINUE="No component details found, so it is not safe to continue!"
@ -7639,8 +7642,10 @@ COM_COMPONENTBUILDER_NO_ITEMS_SELECTED="No items selected."
COM_COMPONENTBUILDER_NO_ITEM_FOUND="No Item Found"
COM_COMPONENTBUILDER_NO_LANGUAGES_FOUND="No Languages Found"
COM_COMPONENTBUILDER_NO_LANGUAGES_UPDATE_SERVER_FOUND="No Languages Update server found."
COM_COMPONENTBUILDER_NO_LANGUAGE_STRINGS_FOUND="No language strings found."
COM_COMPONENTBUILDER_NO_NAMESPACE_FOUND="No Namespace Found"
COM_COMPONENTBUILDER_NO_PATHS_FOUND="No Paths Found"
COM_COMPONENTBUILDER_NO_READ_BRANCH_SET="No Read Branch Set"
COM_COMPONENTBUILDER_NO_RESULTS_MATCH="No results match"
COM_COMPONENTBUILDER_NO_ROWS_FOUND_FOR_THIS_TARGET_AREA="No rows found for this target area"
COM_COMPONENTBUILDER_NO_ROWS_WERE_PROCESSED="No rows were processed."
@ -7652,6 +7657,7 @@ COM_COMPONENTBUILDER_NO_TYPE="No Type"
COM_COMPONENTBUILDER_NO_UPLOAD_SELECTED="No upload selected"
COM_COMPONENTBUILDER_NO_VALIDATION_RULES_FOUND="No validation rules found."
COM_COMPONENTBUILDER_NO_VALID_MODE_HAS_BEEN_SPECIFIED="No valid mode has been specified!"
COM_COMPONENTBUILDER_NO_WRITE_BRANCH_SET="No Write Branch Set"
COM_COMPONENTBUILDER_OFFICIAL_VDM_PACKAGES="Official VDM Packages"
COM_COMPONENTBUILDER_ONLY_IN_ADMIN_LIST_VIEW="Only in Admin List View"
COM_COMPONENTBUILDER_ONLY_IN_LINKED_LIST_VIEWS="Only in Linked List Views"
@ -7976,6 +7982,8 @@ COM_COMPONENTBUILDER_PUSH_FAILED="Push Failed!"
COM_COMPONENTBUILDER_PUSH_UNSUCCESSFUL="Push Unsuccessful!"
COM_COMPONENTBUILDER_PUSH_WAS_UNSUCCESSFUL="Push Was Unsuccessful!"
COM_COMPONENTBUILDER_READY_TO_COMPILE_A_COMPONENT="Ready to compile a component"
COM_COMPONENTBUILDER_READ_BRANCH="Read Branch"
COM_COMPONENTBUILDER_READ_BRANCH_SET="Read Branch Set"
COM_COMPONENTBUILDER_REFRESH="Refresh"
COM_COMPONENTBUILDER_REFRESH_FEED="Refresh Feed"
COM_COMPONENTBUILDER_REGEX_SEARCH="Regex Search"
@ -8177,6 +8185,7 @@ COM_COMPONENTBUILDER_SELECT_A_PROPERTY="Select a property"
COM_COMPONENTBUILDER_SELECT_A_REPOSITORY_TO_FETCH_ITEMS_FOR_INITIALIZATION="Select a repository to fetch items for initialization"
COM_COMPONENTBUILDER_SELECT_A_SITE_VIEW="Select a site view"
COM_COMPONENTBUILDER_SELECT_A_SNIPPET="select a snippet"
COM_COMPONENTBUILDER_SELECT_BRANCH_STATE="Select Branch State"
COM_COMPONENTBUILDER_SELECT_BUILD_DATE="Select Build Date"
COM_COMPONENTBUILDER_SELECT_COMPONENT="Select Component"
COM_COMPONENTBUILDER_SELECT_EXTENSION="Select Extension"
@ -9683,6 +9692,8 @@ COM_COMPONENTBUILDER_WOULD_YOU_LIKE_TO_DO_A_REVERSE_SEARCH="Would you like to do
COM_COMPONENTBUILDER_WOULD_YOU_LIKE_TO_OVERRIDE_THE_BUILD_DATE="Would you like to override the build date."
COM_COMPONENTBUILDER_WOULD_YOU_LIKE_TO_REPEAT_THE_SAME_SEARCH="Would you like to repeat the same search?"
COM_COMPONENTBUILDER_WOULD_YOU_LIKE_TO_SEE_THE_ADVANCED_COMPILER_OPTIONS="Would you like to see the advanced compiler options?"
COM_COMPONENTBUILDER_WRITE_BRANCH="Write Branch"
COM_COMPONENTBUILDER_WRITE_BRANCH_SET="Write Branch Set"
COM_COMPONENTBUILDER_YES="Yes"
COM_COMPONENTBUILDER_YES_UPDATE_ALL="Yes! Update ALL"
COM_COMPONENTBUILDER_YOUR_ARE_ABOUT_TO_UPDATE_BALLB_VALUES_THAT_CAN_BE_FOUND_IN_THE_DATABASE="Your are about to update <b>ALL</b> values that can be found in the database."

View File

@ -0,0 +1,134 @@
<?php
/**
* @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
*/
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\CMS\HTML\HTMLHelper as Html;
use Joomla\CMS\Layout\LayoutHelper;
use VDM\Component\Componentbuilder\Administrator\Helper\ComponentbuilderHelper;
use VDM\Joomla\Utilities\StringHelper;
use Joomla\CMS\Session\Session;
use Joomla\CMS\Uri\Uri;
// No direct access to this file
defined('JPATH_BASE') or die;
// always load these files.
Html::_('stylesheet', "media/com_componentbuilder/datatable/css/datatables.min.css", ['version' => 'auto']);
Html::_('script', "media/com_componentbuilder/datatable/js/pdfmake.min.js", ['version' => 'auto']);
Html::_('script', "media/com_componentbuilder/datatable/js/vfs_fonts.js", ['version' => 'auto']);
Html::_('script', "media/com_componentbuilder/datatable/js/datatables.min.js", ['version' => 'auto']);
// set the table details
$table_id = StringHelper::random(7);
$headers = ComponentbuilderHelper::getLanguageTranslationsHeaders() ?? [];
$fields = array_keys($headers);
$items = 1;
// set the file name
$file_name = 'Language_Translations';
?>
<div style="display: none;">
<?php echo LayoutHelper::render('table',
[
'id' => $table_id,
'name' => $name,
'headers' => $headers,
'items' => $items,
'init' => false
]
); ?>
</div>
<script type="text/javascript">
document.addEventListener('DOMContentLoaded', function () {
// Create the toolbar export button using DOM methods
function createExportButton() {
const wrapper = document.createElement('joomla-toolbar-button');
wrapper.innerHTML = `
<button id="toolbar-export-language-translations" class="button-export btn btn-primary" type="button">
<span aria-hidden="true" class="icon-download"></span>
<?php echo Text::_('COM_COMPONENTBUILDER_EXPORT_TRANSLATIONS'); ?>
</button>
`;
return wrapper;
}
// Insert the export button next to the Edit button
function insertExportButton() {
const editButton = document.getElementById('toolbar-edit');
if (!editButton || !editButton.parentNode) {
console.warn('Edit button not found. Export button not inserted.');
return;
}
const exportButton = createExportButton();
editButton.parentNode.insertBefore(exportButton, editButton.nextSibling);
// Attach click event
const exportBtn = document.getElementById('toolbar-export-language-translations');
if (exportBtn) {
exportBtn.addEventListener('click', exportLanguageTranslations);
} else {
console.warn('Export button element not found for event binding.');
}
}
// Run insertion
insertExportButton();
});
function exportLanguageTranslations() {
document.getElementById("loading").style.display = 'block';
const filterExtension = (() => {
const val = document.getElementById('filter_extension')?.value;
return val !== undefined && val !== '' ? val : 0;
})();
const filterTranslated = (() => {
const val = document.getElementById('filter_translated')?.value;
return val !== undefined && val !== '' ? val : 0;
})();
const filterNotTranslated = (() => {
const val = document.getElementById('filter_not_translated')?.value;
return val !== undefined && val !== '' ? val : 0;
})();
const token = '<?php echo Session::getFormToken(); ?>=1';
const ajaxUrl = `<?php echo Uri::base(); ?>index.php?option=com_componentbuilder&task=ajax.exportLanguageTranslations&format=json&raw=true&${token}&filter_extension=${encodeURIComponent(filterExtension)}&filter_translated=${encodeURIComponent(filterTranslated)}&filter_not_translated=${encodeURIComponent(filterNotTranslated)}`;
const tableElement = document.getElementById('<?php echo $table_id; ?>');
if ($.fn.DataTable.isDataTable(tableElement)) {
const table = $(tableElement).DataTable();
table.ajax.url(ajaxUrl).load();
table.off('draw.dt');
table.on('draw.dt', function () {
table.button(`.buttons-excel`).trigger();
document.getElementById("loading").style.display = 'none';
});
} else {
const table = $(tableElement).DataTable({
dom: 'Bfrtip',
buttons: [
{
extend: 'excel',
text: 'Excel',
title: '<?php echo Text::_('COM_COMPONENTBUILDER_LANGUAGE_TRANSLATIONS'); ?>',
filename: '<?php echo $file_name; ?>'
}
],
select: false,
ajax: { url: ajaxUrl },
deferRender: true,
columns: [<?php foreach ($fields as $field): ?>
{ data: '<?php echo $field; ?>' },
<?php endforeach; ?>]
});
table.on('draw.dt', function () {
table.button(`.buttons-excel`).trigger();
document.getElementById("loading").style.display = 'none';
});
}
}
</script>

View File

@ -2911,84 +2911,86 @@ INSERT INTO `#__componentbuilder_fieldtype` (`id`, `description`, `name`, `prope
-- Dumping data for table `#__componentbuilder_language`
--
INSERT INTO `#__componentbuilder_language` (`id`, `langtag`, `name`, `params`, `published`, `created`, `modified`, `version`, `hits`, `ordering`) VALUES
(1, 'af-ZA', 'Afrikaans', '', 1, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(1, 'af-ZA', 'Afrikaans', '', 1, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(2, 'sq-AL', 'Albanian', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(3, 'ar-AA', 'Arabic Unitag', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(3, 'ar-AA', 'Arabic Unitag', '', 2, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(4, 'hy-AM', 'Armenian', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(5, 'id-ID', 'Indonesian, Bahasa Indonesia', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(6, 'eu-ES', 'Basque', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(7, 'be-BY', 'Belarusian', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(7, 'be-BY', 'Belarusian', '', 2, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(8, 'bn-BD', 'Bengali, Bangladesh', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(9, 'bs-BA', 'Bosnian', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(10, 'bg-BG', 'Bulgarian', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(11, 'ca-ES', 'Catalan', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(12, 'zh-CN', 'Chinese, Simplified', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(13, 'zh-TW', 'Chinese, Traditional', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(14, 'hr-HR', 'Croatian', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(15, 'cs-CZ', 'Czech', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(16, 'da-DK', 'Danish', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(10, 'bg-BG', 'Bulgarian', '', 2, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(11, 'ca-ES', 'Catalan', '', 2, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(12, 'zh-CN', 'Chinese, Simplified', '', 2, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(13, 'zh-TW', 'Chinese, Traditional', '', 2, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(14, 'hr-HR', 'Croatian', '', 2, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(15, 'cs-CZ', 'Czech', '', 2, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(16, 'da-DK', 'Danish', '', 2, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(17, 'prs-AF', 'Dari Persian', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(18, 'nl-NL', 'Dutch', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(18, 'nl-NL', 'Dutch', '', 1, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(19, 'dz-BT', 'Dzongkha', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(20, 'en-AU', 'English, Australia', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(21, 'en-CA', 'English, Canada', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(22, 'en-GB', 'English GB', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(23, 'en-NZ', 'English, New Zealand', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(24, 'en-US', 'English, USA', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(20, 'en-AU', 'English, Australia', '', 2, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(21, 'en-CA', 'English, Canada', '', 2, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(22, 'en-GB', 'English GB', '', 2, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(23, 'en-NZ', 'English, New Zealand', '', 2, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(24, 'en-US', 'English, USA', '', 2, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(25, 'eo-XX', 'Esperanto', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(26, 'et-EE', 'Estonian', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(27, 'fi-FI', 'Finnish', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(28, 'nl-BE', 'Flemish', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(29, 'fr-FR', 'French', '', 1, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(30, 'fr-CA', 'French, Canada', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(26, 'et-EE', 'Estonian', '', 2, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(27, 'fi-FI', 'Finnish', '', 2, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(28, 'nl-BE', 'Flemish', '', 2, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(29, 'fr-FR', 'French', '', 1, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(30, 'fr-CA', 'French, Canada', '', 2, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(31, 'gl-ES', 'Galician', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(32, 'ka-GE', 'Georgian', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(33, 'de-AT', 'German, Austria', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(34, 'de-CH', 'German, Switzerland', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(35, 'de-DE', 'German', '', 1, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(36, 'de-LI', 'German, Liechtenstein', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(37, 'de-LU', 'German, Luxembourg', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(38, 'el-GR', 'Greek', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(32, 'ka-GE', 'Georgian', '', 2, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(33, 'de-AT', 'German, Austria', '', 2, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(34, 'de-CH', 'German, Switzerland', '', 2, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(35, 'de-DE', 'German', '', 1, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(36, 'de-LI', 'German, Liechtenstein', '', 2, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(37, 'de-LU', 'German, Luxembourg', '', 2, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(38, 'el-GR', 'Greek', '', 2, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(39, 'he-IL', 'Hebrew', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(40, 'hi-IN', 'Hindi, India', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(41, 'hu-HU', 'Hungarian', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(41, 'hu-HU', 'Hungarian', '', 2, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(42, 'ga-IE', 'Irish', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(43, 'it-IT', 'Italian', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(44, 'ja-JP', 'Japanese', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(43, 'it-IT', 'Italian', '', 2, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(44, 'ja-JP', 'Japanese', '', 2, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(45, 'km-KH', 'Khmer', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(46, 'ko-KR', 'Korean', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(47, 'lv-LV', 'Latvian', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(46, 'ko-KR', 'Korean', '', 2, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(47, 'lv-LV', 'Latvian', '', 2, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(48, 'mk-MK', 'Macedonian', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(49, 'ms-MY', 'Malay', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(50, 'srp-ME', 'Montenegrin', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(51, 'nb-NO', 'Norwegian Bokmål', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(51, 'nb-NO', 'Norwegian Bokmål', '', 2, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(52, 'nn-NO', 'Norwegian Nynorsk', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(53, 'fa-IR', 'Persian Farsi', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(54, 'pl-PL', 'Polish', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(55, 'pt-PT', 'Portuguese, Portugal', '', 1, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(56, 'pt-BR', 'Portuguese, Brazil', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(57, 'ro-RO', 'Romanian', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(58, 'ru-RU', 'Russian', '', 1, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 6, 0, 1),
(59, 'sr-RS', 'Serbian, Cyrillic', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(60, 'sr-YU', 'Serbian, Latin', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(53, 'fa-IR', 'Persian Farsi', '', 2, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(54, 'pl-PL', 'Polish', '', 2, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(55, 'pt-PT', 'Portuguese, Portugal', '', 1, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(56, 'pt-BR', 'Portuguese, Brazil', '', 2, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(57, 'ro-RO', 'Romanian', '', 2, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(58, 'ru-RU', 'Russian', '', 1, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 7, 0, 1),
(59, 'sr-RS', 'Serbian, Cyrillic', '', 2, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(60, 'sr-YU', 'Serbian, Latin', '', 2, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(61, 'si-LK', 'Sinhala', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(62, 'sk-SK', 'Slovak', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(63, 'sl-SI', 'Slovenian', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(64, 'es-ES', 'Spanish', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(62, 'sk-SK', 'Slovak', '', 2, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(63, 'sl-SI', 'Slovenian', '', 2, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(64, 'es-ES', 'Spanish', '', 2, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(65, 'es-CO', 'Spanish, Colombia', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(66, 'sw-KE', 'Swahili', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(67, 'sv-SE', 'Swedish', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(67, 'sv-SE', 'Swedish', '', 2, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(68, 'sy-IQ', 'Syriac, East', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(69, 'ta-IN', 'Tamil, India', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(70, 'th-TH', 'Thai', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(71, 'tr-TR', 'Turkish', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(69, 'ta-IN', 'Tamil, India', '', 2, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(70, 'th-TH', 'Thai', '', 2, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(71, 'tr-TR', 'Turkish', '', 2, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(72, 'tk-TM', 'Turkmen', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(73, 'uk-UA', 'Ukrainian', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(73, 'uk-UA', 'Ukrainian', '', 2, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(74, 'ug-CN', 'Uyghur', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(75, 'vi-VN', 'Vietnamese', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(76, 'cy-GB', 'Welsh', '', 2, '2018-04-19 13:43:44', '2023-12-26 07:40:20', 5, 0, 1),
(77, 'lt-LT', 'Lithuanian', '', 2, '2018-09-14 10:39:11', '2023-12-26 07:40:20', 4, 0, 2),
(78, 'kk-KZ', 'Kazakh', '', 2, '2020-03-19 13:41:00', '2023-12-26 07:40:20', 3, 0, 3);
(76, 'cy-GB', 'Welsh', '', 2, '2018-04-19 13:43:44', '2025-07-01 20:29:26', 6, 0, 1),
(77, 'lt-LT', 'Lithuanian', '', 2, '2018-09-14 10:39:11', '2025-07-01 20:29:26', 5, 0, 2),
(78, 'kk-KZ', 'Kazakh', '', 2, '2020-03-19 13:41:00', '2025-07-01 20:29:26', 4, 0, 3),
(79, 'ps-AF', 'Pashto Afghanistan', NULL, 2, '2025-07-01 20:29:26', NULL, 1, 0, 4),
(80, 'ur-PK', 'Urdu, Pakistan', NULL, 2, '2025-07-01 20:29:26', NULL, 1, 0, 5);
--
-- Dumping data for table `#__componentbuilder_repository`

View File

@ -1 +0,0 @@

View File

@ -83,6 +83,7 @@ class AjaxController extends BaseController
$this->registerTask('checkRuleName', 'ajax');
$this->registerTask('fieldTypeProperties', 'ajax');
$this->registerTask('getFieldPropertyDesc', 'ajax');
$this->registerTask('exportLanguageTranslations', 'ajax');
$this->registerTask('getCodeGlueOptions', 'ajax');
$this->registerTask('doSearch', 'ajax');
$this->registerTask('replaceAll', 'ajax');
@ -1803,6 +1804,57 @@ class AjaxController extends BaseController
}
}
break;
case 'exportLanguageTranslations':
try
{
$filter_extensionValue = $jinput->get('filter_extension', NULL, 'STRING');
$filter_translatedValue = $jinput->get('filter_translated', NULL, 'STRING');
$filter_not_translatedValue = $jinput->get('filter_not_translated', NULL, 'STRING');
if($user->id != 0)
{
$ajaxModule = $this->getModel('ajax', 'Administrator');
if ($ajaxModule)
{
$result = $ajaxModule->exportLanguageTranslations($filter_extensionValue, $filter_translatedValue, $filter_not_translatedValue);
}
else
{
$result = ['error' => 'There was an error! [149]'];
}
}
else
{
$result = ['error' => 'There was an error! [149]'];
}
if($callback)
{
echo $callback . "(".json_encode($result).");";
}
elseif($returnRaw)
{
echo json_encode($result);
}
else
{
echo "(".json_encode($result).");";
}
}
catch(\Exception $e)
{
if($callback)
{
echo $callback."(".json_encode($e).");";
}
elseif($returnRaw)
{
echo json_encode($e);
}
else
{
echo "(".json_encode($e).");";
}
}
break;
case 'getCodeGlueOptions':
try
{

View File

@ -0,0 +1,78 @@
<?php
/**
* @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
*/
namespace VDM\Component\Componentbuilder\Administrator\Field;
use Joomla\CMS\Factory;
use Joomla\CMS\Form\Field\ListField;
use Joomla\CMS\Language\Text;
use Joomla\CMS\HTML\HTMLHelper as Html;
use Joomla\CMS\Component\ComponentHelper;
use VDM\Component\Componentbuilder\Administrator\Helper\ComponentbuilderHelper;
// No direct access to this file
\defined('_JEXEC') or die;
/**
* Repositoriesfiltertype Form Field class for the Componentbuilder component
*
* @since 1.6
*/
class RepositoriesfiltertypeField extends ListField
{
/**
* The repositoriesfiltertype field type.
*
* @var string
*/
public $type = 'Repositoriesfiltertype';
/**
* Method to get a list of options for a list input.
*
* @return array An array of Html options.
* @since 1.6
*/
protected function getOptions()
{
// Get a db connection.
$db = Factory::getContainer()->get(\Joomla\Database\DatabaseInterface::class);
// Create a new query object.
$query = $db->getQuery(true);
// Select the text.
$query->select($db->quoteName('type'));
$query->from($db->quoteName('#__componentbuilder_repository'));
$query->order($db->quoteName('type') . ' ASC');
// Reset the query using our newly populated query object.
$db->setQuery($query);
$_results = $db->loadColumn();
$_filter = [];
$_filter[] = Html::_('select.option', '', '- ' . Text::_('COM_COMPONENTBUILDER_FILTER_SELECT_TYPE') . ' -');
if ($_results)
{
// get repositoriesmodel
$_model = ComponentbuilderHelper::getModel('repositories');
$_results = array_unique($_results);
foreach ($_results as $type)
{
// Translate the type selection
$_text = $_model->selectionTranslation($type,'type');
// Now add the type and its text to the options array
$_filter[] = Html::_('select.option', $type, Text::_($_text));
}
}
return $_filter;
}
}

View File

@ -420,7 +420,7 @@ abstract class ComponentbuilderEmail
'<title>' . htmlspecialchars($subject) . '</title>',
'<style type="text/css">',
'#outlook a {padding:0;} .ExternalClass {width:100%;} .ExternalClass, .ExternalClass p, .ExternalClass span, .ExternalClass font, .ExternalClass td, .ExternalClass div {line-height:100%;}',
'p {margin: 0; padding: 0; font-size: 0px; line-height: 0px;}',
// 'p {margin: 0; padding: 0; font-size: 0px; line-height: 0px;}',
'table, table td {border-collapse: collapse;}',
'img {display:block; outline:none; text-decoration:none; -ms-interpolation-mode:bicubic;}',
'a img {border:none;} a {text-decoration:none; color:#000001;} a.phone {pointer-events:auto; cursor:default; color:#000001 !important;}',
@ -456,7 +456,7 @@ abstract class ComponentbuilderEmail
'<title>' . htmlspecialchars($subject) . '</title>',
'<style type="text/css">',
'#outlook a {padding:0;} .ExternalClass {width:100%;} .ExternalClass, .ExternalClass p, .ExternalClass span, .ExternalClass font, .ExternalClass td, .ExternalClass div {line-height:100%;}',
'p {margin: 0; padding: 0; font-size: 0px; line-height: 0px;}',
// 'p {margin: 0; padding: 0; font-size: 0px; line-height: 0px;}',
'table, table td {border-collapse: collapse;}',
'img {display:block; outline:none; text-decoration:none; -ms-interpolation-mode:bicubic;}',
'a img {border:none;} a {text-decoration:none; color:#000001;} a.phone {pointer-events:auto; cursor:default; color:#000001 !important;}',

View File

@ -38,14 +38,15 @@ use Joomla\Archive\Archive;
use Joomla\Filesystem\Folder;
use Joomla\Filesystem\Path;
use VDM\Joomla\Openai\Factory as OpenaiFactory;
use VDM\Joomla\Utilities\GuidHelper;
use VDM\Joomla\Utilities\StringHelper as UtilitiesStringHelper;
use VDM\Joomla\Utilities\GetHelper;
use VDM\Joomla\Data\Factory as DataFactory;
use VDM\Joomla\Utilities\ArrayHelper as UtilitiesArrayHelper;
use VDM\Joomla\Utilities\FileHelper;
use VDM\Joomla\Utilities\GuidHelper;
use VDM\Joomla\Utilities\JsonHelper;
use VDM\Joomla\Utilities\StringHelper as UtilitiesStringHelper;
use VDM\Joomla\Utilities\ObjectHelper;
use VDM\Joomla\Utilities\FileHelper;
use VDM\Joomla\Utilities\Component\Helper;
use VDM\Joomla\Utilities\GetHelper;
use VDM\Joomla\Utilities\SessionHelper;
use VDM\Joomla\Componentbuilder\Compiler\Utilities\FieldHelper;
use VDM\Joomla\Componentbuilder\Compiler\Factory as CompilerFactory;
@ -103,12 +104,12 @@ abstract class ComponentbuilderHelper
/**
* Locked Libraries (we can not have these change)
**/
public static $libraryNames = array(1 => 'No Library', 2 => 'Bootstrap v4', 3 => 'Uikit v3', 4 => 'Uikit v2', 5 => 'FooTable v2', 6 => 'FooTable v3');
public static $libraryNames = [1 => 'No Library', 2 => 'Bootstrap v4', 3 => 'Uikit v3', 4 => 'Uikit v2', 5 => 'FooTable v2', 6 => 'FooTable v3'];
/**
* Array of php fields Allowed (16)
**/
public static $phpFieldArray = array('', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'x', 'HEADER');
public static $phpFieldArray = ['', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'x', 'HEADER'];
/**
* The global params
@ -130,30 +131,6 @@ abstract class ComponentbuilderHelper
**/
protected static $exPowers= array();
/**
* The snippet paths
**/
public static $snippetPath = 'https://raw.githubusercontent.com/vdm-io/Joomla-Component-Builder-Snippets/master/';
public static $snippetsPath = 'https://api.github.com/repos/vdm-io/Joomla-Component-Builder-Snippets/git/trees/master';
/**
* The VDM packages paths
**/
public static $vdmGithubPackageUrl = "https://github.com/vdm-io/JCB-Packages/raw/master/";
public static $vdmGithubPackagesUrl = "https://api.github.com/repos/vdm-io/JCB-Packages/git/trees/master";
/**
* The JCB packages paths
**/
public static $jcbGithubPackageUrl = "https://github.com/vdm-io/JCB-Community-Packages/raw/master/";
public static $jcbGithubPackagesUrl = "https://api.github.com/repos/vdm-io/JCB-Community-Packages/git/trees/master";
/**
* The bolerplate paths
**/
public static $bolerplatePath = 'https://raw.githubusercontent.com/vdm-io/boilerplate/jcb/';
public static $bolerplateAPI = 'https://api.github.com/repos/vdm-io/boilerplate/git/trees/jcb';
/**
* The array of constant paths
*
@ -206,527 +183,37 @@ abstract class ComponentbuilderHelper
);
/**
* get the class method or property
* Retrieves an associative array of published language tags as headers.
*
* @input mixed The method/property ID|GUID
* @input string The target type
* The returned array is used to build header labels for language translations.
* Includes a default 'source' => 'source' entry.
*
* @returns string on success
* @since 3.0.0
* @return array<string, string> Associative array of language headers
* @since 5.1.1
*/
public static function getClassCode($target, string $type): ?string
public static function getLanguageTranslationsHeaders(): array
{
if ('property' === $type || 'method' === $type)
{
if (GuidHelper::valid($target))
{
$key = 'guid';
}
elseif (is_numeric($target))
{
$key = 'id';
}
else
{
return null;
}
$values = DataFactory::_('Load')->values(
['langtag'], ['language'], ['published' => 1], ['name' => 'ASC']
);
// Get a db connection.
$db = Factory::getDbo();
// Get user object
$user = Factory::getUser();
// Create a new query object.
$query = $db->getQuery(true);
// get method
if ('method' === $type)
{
$query->select($db->quoteName(['a.comment','a.name','a.visibility','a.arguments','a.code']));
}
// get property
elseif ('property' === $type)
{
$query->select($db->quoteName(['a.comment','a.name','a.visibility','a.default']));
}
$query->from($db->quoteName('#__componentbuilder_class_' . $type, 'a'));
$query->where($db->quoteName('a.' . $key) . ' = ' . $db->quote($target));
// Implement View Level Access
if (!$user->authorise('core.options', 'com_componentbuilder'))
{
$columns = $db->getTableColumns('#__componentbuilder_class_' . $type);
if(isset($columns['access']))
{
$groups = implode(',', $user->getAuthorisedViewLevels());
$query->where('a.access IN (' . $groups . ')');
}
}
$db->setQuery($query);
$db->execute();
if ($db->getNumRows())
{
// get the code
$code = $db->loadObject();
// combine method values
$combinded = [];
// add comment if set
if (UtilitiesStringHelper::check($code->comment))
{
$comment = array_map('trim', (array) explode(PHP_EOL, base64_decode($code->comment)));
$combinded[] = "\t" . implode(PHP_EOL . "\t ", $comment);
}
// build method
if ('method' === $type)
{
// set the method signature
if (UtilitiesStringHelper::check($code->arguments))
{
$combinded[] = "\t" . $code->visibility . ' function ' . $code->name . '(' . base64_decode($code->arguments) . ')';
}
else
{
$combinded[] = "\t" . $code->visibility . ' function ' . $code->name . '()';
}
// set the method code
$combinded[] = "\t" . "{";
// add code if set
if (UtilitiesStringHelper::check(trim($code->code)))
{
$combinded[] = base64_decode($code->code);
}
else
{
$combinded[] = "\t\t// add your code here";
}
$combinded[] = "\t" . "}";
}
else
{
if (UtilitiesStringHelper::check($code->default))
{
$code->default = base64_decode($code->default);
if (is_int($code->default))
{
// set the class property
$combinded[] = "\t" . $code->visibility . ' $' . $code->name . ' = ' . (int) $code->default . ';';
}
elseif (is_float($code->default))
{
// set the class property
$combinded[] = "\t" . $code->visibility . ' $' . $code->name . ' = ' . (float) $code->default . ';';
}
elseif (('false' === $code->default || 'true' === $code->default)
|| (UtilitiesStringHelper::check($code->default) && (strpos($code->default, 'array(') !== false || strpos($code->default, '"') !== false)))
{
// set the class property
$combinded[] = "\t" . $code->visibility . ' $' . $code->name . ' = ' . $code->default . ';';
}
elseif (UtilitiesStringHelper::check($code->default) && strpos($code->default, '"') === false)
{
// set the class property
$combinded[] = "\t" . $code->visibility . ' $' . $code->name . ' = "' . $code->default . '";';
}
else
{
// set the class property
$combinded[] = "\t" . $code->visibility . ' $' . $code->name . ';';
}
}
else
{
// set the class property
$combinded[] = "\t" . $code->visibility . ' $' . $code->name . ';';
}
}
// return the code
return implode(PHP_EOL, $combinded);
}
if (empty($values))
{
// return default array
return ['source' => 'source', 'af-ZA' => 'af-ZA', 'nl-NL' => 'nl-NL', 'fr-FR' => 'fr-FR', 'de-DE' => 'de-DE', 'pt-PT' => 'pt-PT', 'ru-RU' => 'ru-RU'];
}
return null;
}
/**
* extract Boilerplate Class Extends
*
* @input string The class as a string
* @input string The type of class/extension
*
* @returns string on success
* @since 3.0.0
*/
public static function extractBoilerplateClassExtends(&$class, $type)
{
if (($strings = GetHelper::allBetween($class, 'class ', '}')) !== false && UtilitiesArrayHelper::check($strings))
{
foreach ($strings as $string)
{
if (($extends = GetHelper::between($string, 'extends ', '{')) !== false && UtilitiesStringHelper::check($extends))
{
return trim($extends);
}
}
}
return false;
}
$headers = ['source' => 'source'];
/**
* extract Boilerplate Class Header
*
* @input string The class as a string
* @input string The class being extended
* @input string The type of class/extension
*
* @returns string on success
* @since 3.0.0
*/
public static function extractBoilerplateClassHeader(&$class, $extends, $type)
{
if (($string = GetHelper::between($class, "defined('_JEXEC')", 'extends ' . $extends)) !== false && UtilitiesStringHelper::check($string))
foreach ($values as $value)
{
$headArray = explode(PHP_EOL, $string);
if (UtilitiesArrayHelper::check($headArray) && count($headArray) > 3)
if (is_string($value) && trim($value) !== '')
{
// remove first since it still has the [or die;] string in it
array_shift($headArray);
// remove the last since it has the class declaration
array_pop($headArray);
// at this point we have the class comment still in as part of the header, lets remove that
$last = count($headArray);
while ($last > 0)
{
$last--;
if (isset($headArray[$last]) && strpos($headArray[$last], '*') !== false)
{
unset($headArray[$last]);
}
else
{
// moment the comment stops, we break out
$last = 0;
}
}
// make sure we only return if we have values
if (UtilitiesArrayHelper::check($headArray))
{
return implode(PHP_EOL, $headArray);
}
$headers[$value] = $value;
}
}
return false;
}
/**
* extract Boilerplate Class Comment
*
* @input string The class as a string
* @input string The class being extended
* @input string The type of class/extension
*
* @returns string on success
* @since 3.0.0
*/
public static function extractBoilerplateClassComment(&$class, $extends, $type)
{
if (($string = GetHelper::between($class, "defined('_JEXEC')", 'extends ' . $extends)) !== false && UtilitiesStringHelper::check($string))
{
$headArray = explode(PHP_EOL, $string);
if (UtilitiesArrayHelper::check($headArray) && count($headArray) > 3)
{
$comment = array();
// remove the last since it has the class declaration
array_pop($headArray);
// at this point we have the class comment still in as part of the header, lets remove that
$last = count($headArray);
while ($last > 0)
{
$last--;
if (isset($headArray[$last]) && strpos($headArray[$last], '*') !== false)
{
$comment[$last] = $headArray[$last];
}
else
{
// moment the comment stops, we break out
$last = 0;
}
}
// make sure we only return if we have values
if (UtilitiesArrayHelper::check($comment))
{
// set the correct order
ksort($comment);
$replace = array('Foo' => '[[[Plugin_name]]]', '[PACKAGE_NAME]' => '[[[Plugin]]]', '1.0.0' => '[[[plugin.version]]]', '1.0' => '[[[plugin.version]]]');
// now update with JCB placeholders
return str_replace(array_keys($replace), array_values($replace), implode(PHP_EOL, $comment));
}
}
}
return false;
}
/**
* extract Boilerplate Class Properties & Methods
*
* @input string The class as a string
* @input string The class being extended
* @input string The type of class/extension
* @input int The plugin groups
*
* @returns string on success
* @since 3.0.0
*/
public static function extractBoilerplateClassPropertiesMethods(&$class, $extends, $type, $plugin_group = null)
{
$bucket = array('property' => array(), 'method' => array());
// get the class code, and remove the head
$codeArrayTmp = explode('extends ' . $extends, $class);
// make sure we have the correct result
if (UtilitiesArrayHelper::check($codeArrayTmp) && count($codeArrayTmp) == 2)
{
// the triggers
$triggers = array('public' => 1, 'protected' => 2, 'private' => 3);
$codeArray = explode(PHP_EOL, $codeArrayTmp[1]);
unset($codeArrayTmp);
// clean the code
self::cleanBoilerplateCode($codeArray);
// temp bucket
$name = null;
$arg = null;
$target = null;
$visibility = null;
$tmp = array();
$comment = array();
// load method
$loadCode = function (&$bucket, &$target, &$name, &$arg, &$visibility, &$tmp, &$comment) use($type, $plugin_group){
$_tmp = array(
'name' => $name,
'visibility' => $visibility,
'extension_type' => $type
);
// build filter
$filters = array('extension_type' => $type);
// add more data based on target
if ('method' === $target && UtilitiesArrayHelper::check($tmp))
{
// clean the code
self::cleanBoilerplateCode($tmp);
// only load if there are values
if (UtilitiesArrayHelper::check($tmp, true))
{
$_tmp['code'] = implode(PHP_EOL, $tmp);
}
else
{
$_tmp['code'] = '';
}
// load arguments only if set
if (UtilitiesStringHelper::check($arg))
{
$_tmp['arguments'] = $arg;
}
}
elseif ('property' === $target)
{
// load default only if set
if (UtilitiesStringHelper::check($arg))
{
$_tmp['default'] = $arg;
}
}
// load comment only if set
if (UtilitiesArrayHelper::check($comment, true))
{
$_tmp['comment'] = implode(PHP_EOL, $comment);
}
// load the group target
if ($plugin_group)
{
$_tmp['joomla_plugin_group'] = $plugin_group;
$filters['joomla_plugin_group'] = $plugin_group;
}
// load the local values
if (($locals = self::getLocalBoilerplate($name, $target, $type, $filters)) !== false)
{
foreach ($locals as $key => $value)
{
$_tmp[$key] = $value;
}
}
else
{
$_tmp['id'] = 0;
$_tmp['published'] = 1;
$_tmp['version'] = 1;
}
// store the data based on target
$bucket[$target][] = $_tmp;
};
// now we start loading
foreach($codeArray as $line)
{
if ($visibility && $target && $name && strpos($line, '/**') !== false)
{
$loadCode($bucket, $target, $name, $arg, $visibility, $tmp, $comment);
// reset loop buckets
$name = null;
$arg = null;
$target = null;
$visibility = null;
$tmp = array();
$comment = array();
}
// load the comment before method/property
if (!$visibility && !$target && !$name && strpos($line, '*') !== false)
{
$comment[] = rtrim($line);
}
else
{
if (!$visibility && !$target && !$name)
{
// get the line values
$lineArray = array_values(array_map('trim', preg_split('/\s+/', trim($line))));
// check if we are at the main line
if (isset($lineArray[0]) && isset($triggers[$lineArray[0]]))
{
$visibility = $lineArray[0];
if (strpos($line, 'function') !== false)
{
$target = 'method';
// get the name
$name = trim(GetHelper::between($line, 'function ', '('));
// get the arguments
$arg = trim(GetHelper::between($line, ' ' . $name . '(', ')'));
}
else
{
$target = 'property';
if (strpos($line, '=') !== false)
{
// get the name
$name = trim(GetHelper::between($line, '$', '='));
// get the default
$arg = trim(GetHelper::between($line, '=', ';'));
}
else
{
// get the name
$name = trim(GetHelper::between($line, '$', ';'));
}
}
}
}
else
{
$tmp[] = rtrim($line);
}
}
}
// check if a last method is still around
if ($visibility && $target && $name)
{
$loadCode($bucket, $target, $name, $arg, $visibility, $tmp, $comment);
// reset loop buckets
$name = null;
$arg = null;
$target = null;
$visibility = null;
$tmp = array();
$comment = array();
}
return $bucket;
}
return false;
}
protected static function getLocalBoilerplate($name, $table, $extension_type, $filters = array())
{
if ('property' === $table || 'method' === $table)
{
// Get a db connection.
$db = Factory::getDbo();
// Create a new query object.
$query = $db->getQuery(true);
// get method
$query->select($db->quoteName(array('a.id','a.published','a.version')));
$query->from($db->quoteName('#__componentbuilder_class_' . $table,'a'));
$query->where($db->quoteName('a.name') . ' = ' . $db->quote($name));
$query->where($db->quoteName('a.extension_type') . ' = ' . $db->quote($extension_type));
// add more filters
if (UtilitiesArrayHelper::check($filters))
{
foreach($filters as $where => $value)
{
if (is_numeric($value))
{
$query->where($db->quoteName('a.' . $where) . ' = ' . $value);
}
else
{
$query->where($db->quoteName('a.' . $where) . ' = ' . $db->quote($value));
}
}
}
$db->setQuery($query);
$db->execute();
if ($db->getNumRows())
{
// get the code
return $db->loadAssoc();
}
}
return false;
}
protected static function cleanBoilerplateCode(&$code)
{
// remove the first lines until a { is found
$key = 0;
$found = false;
while (!$found)
{
if (isset($code[$key]))
{
if (strpos($code[$key], '{') !== false)
{
unset($code[$key]);
// only remove the first } found
$found = true;
}
// remove empty lines
elseif (!UtilitiesStringHelper::check(trim($code[$key])))
{
unset($code[$key]);
}
}
// check next line
$key++;
// stop loop at line 30 (really this should never happen)
if ($key > 30)
{
$found = true;
}
}
// reset all keys
$code = array_values($code);
// remove last lines until }
$last = count($code);
while ($last > 0)
{
$last--;
if (isset($code[$last]))
{
if (strpos($code[$last], '}') !== false)
{
unset($code[$last]);
// only remove the first } found
$last = 0;
}
// remove empty lines
elseif (!UtilitiesStringHelper::check(trim($code[$last])))
{
unset($code[$last]);
}
}
}
return $headers;
}
/**
@ -797,81 +284,6 @@ abstract class ComponentbuilderHelper
return is_array($items) ? $items : null;
}
/**
* Get the snippet contributor details
*
* @param string $filename The file name
* @param string $type The type of file
*
* @return array On success the contributor details
* @since 3.0.0
*/
public static function getContributorDetails(string $filename, string $type = 'snippet'): ?array
{
// start loading the contributor details
$contributor = [];
// get the path & content
switch ($type)
{
case 'snippet':
$path = self::$snippetPath.$filename;
// get the file if available
$content = FileHelper::getContent($path);
if (JsonHelper::check($content))
{
$content = json_decode($content, true);
}
break;
default:
// only allow types that are being targeted
return null;
break;
}
// see if we have content and all needed details
if (isset($content) && UtilitiesArrayHelper::check($content)
&& isset($content['contributor_company'])
&& isset($content['contributor_name'])
&& isset($content['contributor_email'])
&& isset($content['contributor_website']))
{
// got the details from file
return [
'contributor_company' => $content['contributor_company'] ,
'contributor_name' => $content['contributor_name'],
'contributor_email' => $content['contributor_email'],
'contributor_website' => $content['contributor_website'],
'origin' => 'file'
];
}
// get the global settings
if (!ObjectHelper::check(self::$params))
{
self::$params = ComponentHelper::getParams('com_componentbuilder');
}
// get the global company details
if (!UtilitiesArrayHelper::check(self::$localCompany))
{
// Set the person sharing information (default VDM ;)
self::$localCompany['company'] = self::$params->get('export_company', 'Vast Development Method');
self::$localCompany['owner'] = self::$params->get('export_owner', 'Llewellyn van der Merwe');
self::$localCompany['email'] = self::$params->get('export_email', 'joomla@vdm.io');
self::$localCompany['website'] = self::$params->get('export_website', 'https://www.vdm.io/');
}
// default global
return [
'contributor_company' => self::$localCompany['company'],
'contributor_name' => self::$localCompany['owner'],
'contributor_email' => self::$localCompany['email'],
'contributor_website' => self::$localCompany['website'],
'origin' => 'global'
];
}
/**
* Get the library files
*

View File

@ -42,6 +42,8 @@ use VDM\Joomla\Utilities\Base64Helper;
use VDM\Joomla\Componentbuilder\Table\Search;
use VDM\Joomla\Componentbuilder\Compiler\Utilities\FieldHelper;
use VDM\Joomla\Utilities\FormHelper;
use VDM\Joomla\Componentbuilder\Utilities\FilterHelper;
use VDM\Joomla\Data\Factory as DataFactory;
use VDM\Joomla\Componentbuilder\Package\Factory as PackageFactory;
use VDM\Joomla\Componentbuilder\Fieldtype\Factory as FieldtypeFactory;
use VDM\Joomla\Componentbuilder\JoomlaPower\Factory as JoomlaPowerFactory;
@ -4872,6 +4874,237 @@ class AjaxModel extends ListModel
return $xml;
}
// Used in language_translation
/**
* Export language translation data by filtering records based on extension, translated, and untranslated tags.
*
* This method loads translation records from the database and structures them into an array
* with language-tagged headers (e.g., `en-GB`, `de-DE`). It supports filtering for a specific
* extension, already translated languages, and missing translations. All matching records are
* padded with empty values for missing languages, and returned with a size count or errors.
*
* @param string $extension The extension identifier in format "type__name" (e.g., "com_example__field").
* @param string $translated Comma-separated list of language tags that must have translations.
* @param string $notTranslated Comma-separated list of language tags that must not yet have translations.
*
* @return array<string, mixed> Returns an array with:
* - 'data' (array<int, array<string, string>>): The exportable translation rows.
* - 'size' (int): Number of rows (if successful).
* - 'errors' (string): Error message (if an error occurred).
*
* @throws \Throwable If any unexpected exception occurs during data fetching or parsing.
* @since 5.1.1
*/
public function exportLanguageTranslations(string $extension, string $translated, string $notTranslated): array
{
try {
$ids = $this->resolveLanguageTranslationFilterIds($extension, $translated, $notTranslated);
$where = $this->buildLanguageTranslationWhereClause($ids);
$records = $this->loadLanguageTranslationRows($where);
$headers = $this->getLanguageTranslationHeaders();
if (empty($records))
{
return $this->errorLanguageTranslationResponse(Text::_('COM_COMPONENTBUILDER_NO_LANGUAGE_STRINGS_FOUND'));
}
$data = $this->normalizeLanguageTranslationData($records, $headers);
return [
'data' => $data,
'size' => count($data),
];
} catch (\Throwable $e) {
return $this->errorLanguageTranslationResponse($e->getMessage());
}
}
/**
* Resolve all relevant record IDs from the given filters.
*
* @param string $extension Extension string in format "type__name".
* @param string $translated Comma-separated list of translated language tags.
* @param string $notTranslated Comma-separated list of untranslated language tags.
*
* @return array<int>|null Array of record IDs, or empty array to load all or null to force a skip all.
* @since 5.1.1
*/
protected function resolveLanguageTranslationFilterIds(string $extension, string $translated, string $notTranslated): ?array
{
$ids = [];
$forceEmpty = false;
// Extension IDs
if (!empty($extension) && strpos($extension, '__') !== false)
{
[$type, $name] = explode('__', $extension, 2);
$extIds = FilterHelper::translation($name, $type);
if (!empty($extIds))
{
$ids = array_merge($ids, $extIds);
}
else
{
$forceEmpty = true;
}
}
// Translated IDs
if (!empty($translated))
{
$trIds = FilterHelper::translations($translated);
if (!empty($trIds))
{
$ids = array_merge($ids, $trIds);
}
else
{
$forceEmpty = true;
}
}
// Not translated IDs
if (!empty($notTranslated))
{
$untrIds = FilterHelper::translations($notTranslated, false);
if (!empty($untrIds))
{
$ids = array_merge($ids, $untrIds);
}
else
{
$forceEmpty = true;
}
}
if ($ids === [] && !$forceEmpty)
{
return [];
}
return $forceEmpty ? null : array_unique($ids);
}
/**
* Build a SQL WHERE clause using resolved IDs.
*
* @param array<int>|null $ids The record IDs to include in the query.
*
* @return array<string, array<string, mixed>> A structured WHERE clause.
* @since 5.1.1
*/
protected function buildLanguageTranslationWhereClause(?array $ids): array
{
if ($ids === [])
{
// return all published
return ['published' => ['value' => 1, 'operator' => '=', 'quote' => false]];
}
elseif ($ids === null)
{
// return none
return ['id' => ['value' => 0, 'operator' => '=', 'quote' => false]];
}
// return selected and published
return [
'id' => ['value' => $ids, 'operator' => 'IN', 'quote' => false],
'published' => ['value' => 1, 'operator' => '=', 'quote' => false]
];
}
/**
* Load translation rows from the database based on the given WHERE clause.
*
* @param array<string, array<string, mixed>>|null $where Optional WHERE clause.
*
* @return array<int, array<string, mixed>> Loaded records with 'source' and 'translation' keys.
* @since 5.1.1
*/
protected function loadLanguageTranslationRows(?array $where): array
{
return DataFactory::_('Load')->rows(
['source', 'translation'],
['language_translation'],
$where,
['source' => 'ASC']
);
}
/**
* Get the list of available language headers (e.g., ['en-GB' => 'en-GB']).
*
* This includes a default 'source' => 'source' entry.
*
* @return array<string, string> Associative list of language tags.
* @since 5.1.1
*/
protected function getLanguageTranslationHeaders(): array
{
return ComponentbuilderHelper::getLanguageTranslationsHeaders() ?? ['source' => 'source'];
}
/**
* Normalize translation records by mapping language keys and padding missing headers.
*
* @param array<int, array<string, mixed>> $rows Raw translation rows from the database.
* @param array<string, string> $headers Valid language header list.
*
* @return array<int, array<string, string>> Structured translation data ready for export.
* @since 5.1.1
*/
protected function normalizeLanguageTranslationData(array $rows, array $headers): array
{
$normalized = [];
foreach ($rows as $row)
{
$translations = json_decode($row['translation'] ?? '[]', true) ?: [];
unset($row['translation']);
// Pad all expected language headers
foreach ($headers as $lang => $_)
{
if ($lang === 'source')
{
continue;
}
$row[$lang] = '';
}
foreach ($translations as $entry)
{
$lang = $entry['language'] ?? '';
$text = trim(($entry['translation'] ?? ''));
if (isset($headers[$lang]) && trim($text) !== '')
{
$row[$lang] = $text;
}
}
$normalized[] = $row;
}
return $normalized;
}
/**
* Build a standardized error response with message.
*
* @param string $message Error message to return.
*
* @return array<string, mixed> Error response with 'data' as empty array and 'errors' as message.
* @since 5.1.1
*/
protected function errorLanguageTranslationResponse(string $message): array
{
return [
'data' => [],
'errors' => $message,
];
}
// Used in admin_fields_relations
public function getCodeGlueOptions($listfield, $joinfields, $type, $area)
{

View File

@ -29,10 +29,7 @@ use Joomla\Utilities\ArrayHelper;
use Joomla\Input\Input;
use VDM\Component\Componentbuilder\Administrator\Helper\ComponentbuilderHelper;
use Joomla\CMS\Helper\TagsHelper;
use VDM\Joomla\Utilities\SessionHelper;
use VDM\Joomla\Utilities\StringHelper as UtilitiesStringHelper;
use VDM\Joomla\Utilities\ObjectHelper;
use VDM\Joomla\Utilities\GuidHelper;
use VDM\Joomla\Utilities\ArrayHelper as UtilitiesArrayHelper;
// No direct access to this file
@ -118,62 +115,6 @@ class Language_translationModel extends AdminModel
return parent::getTable($type, $prefix, $config);
}
/**
* Retrieves or generates a Vast Development Method (VDM) key for the current item.
*
* This function performs the following operations:
* 1. Checks if the VDM key is already set. If not, it proceeds to generate or retrieve one.
* 2. Determines the item ID based on the presence of a specific argument.
* 3. Attempts to retrieve an existing VDM key from a helper method using the item ID.
* 4. If a VDM key is not found, it generates a new random VDM key.
* 5. Stores the VDM key and associates it with the item ID in a helper method.
* 6. Optionally, stores return and GUID values if available.
* 7. Returns the VDM key.
*
* @return string The VDM key for the current item.
*/
public function getVDM()
{
if (!isset($this->vastDevMod))
{
$_id = 0; // new item probably (since it was not set in the getItem method)
if (empty($_id))
{
$id = 0;
}
else
{
$id = $_id;
}
// set the id and view name to session
if (($vdm = SessionHelper::get('language_translation__'.$id)) !== null)
{
$this->vastDevMod = $vdm;
}
else
{
// set the vast development method key
$this->vastDevMod = UtilitiesStringHelper::random(50);
SessionHelper::set($this->vastDevMod, 'language_translation__'.$id);
SessionHelper::set('language_translation__'.$id, $this->vastDevMod);
// set a return value if found
$jinput = Factory::getApplication()->input;
$return = $jinput->get('return', null, 'base64');
SessionHelper::set($this->vastDevMod . '__return', $return);
// set a GUID value if found
if (isset($item) && ObjectHelper::check($item) && isset($item->guid)
&& GuidHelper::valid($item->guid))
{
SessionHelper::set($this->vastDevMod . '__guid', $item->guid);
}
}
}
return $this->vastDevMod;
}
/**
* Method to get a single record.
*
@ -233,38 +174,6 @@ class Language_translationModel extends AdminModel
$translation->loadString($item->translation);
$item->translation = $translation->toArray();
}
if (empty($item->id))
{
$id = 0;
}
else
{
$id = $item->id;
}
// set the id and view name to session
if (($vdm = SessionHelper::get('language_translation__'.$id)) !== null)
{
$this->vastDevMod = $vdm;
}
else
{
// set the vast development method key
$this->vastDevMod = UtilitiesStringHelper::random(50);
SessionHelper::set($this->vastDevMod, 'language_translation__'.$id);
SessionHelper::set('language_translation__'.$id, $this->vastDevMod);
// set a return value if found
$jinput = Factory::getApplication()->input;
$return = $jinput->get('return', null, 'base64');
SessionHelper::set($this->vastDevMod . '__return', $return);
// set a GUID value if found
if (isset($item) && ObjectHelper::check($item) && isset($item->guid)
&& GuidHelper::valid($item->guid))
{
SessionHelper::set($this->vastDevMod . '__guid', $item->guid);
}
}
}
return $item;

View File

@ -28,7 +28,6 @@ use VDM\Joomla\Componentbuilder\Utilities\FilterHelper as JCBFilterHelper;
use VDM\Joomla\Utilities\ArrayHelper as UtilitiesArrayHelper;
use VDM\Joomla\Utilities\ObjectHelper;
use VDM\Joomla\Utilities\StringHelper;
use VDM\Joomla\Utilities\JsonHelper;
use Joomla\CMS\Form\Form;
// No direct access to this file
@ -282,55 +281,6 @@ class Language_translationsModel extends ListModel
}
}
}
// prep the lang strings for export
if (isset($_export) && $_export && UtilitiesArrayHelper::check($items))
{
// insure we have the same order in the languages
$languages = ComponentbuilderHelper::getVars('language', 1, 'published', 'langtag');
foreach ($items as $nr => &$item)
{
// remove some values completely
unset($item->components);
unset($item->modules);
unset($item->plugins);
unset($item->params);
unset($item->published);
unset($item->created_by);
unset($item->modified_by);
unset($item->created);
unset($item->modified);
unset($item->version);
unset($item->hits);
unset($item->access);
unset($item->ordering);
// set the lang order
if ($nr != 0)
{
foreach ($languages as $lanTag)
{
$item->{$lanTag} = '';
}
// now adapt the source
if (isset($item->translation) && JsonHelper::check($item->translation))
{
$translations = json_decode($item->translation, true);
if (UtilitiesArrayHelper::check($translations))
{
foreach ($translations as $language)
{
if (isset($language['translation']) && StringHelper::check($language['translation'])
&& isset($language['language']) && StringHelper::check($language['language']))
{
$item->{$language['language']} = $language['translation'];
}
}
}
}
}
// remove translation
unset($item->translation);
}
}
// return items
return $items;
@ -606,56 +556,6 @@ class Language_translationsModel extends ListModel
{
array_unshift($items,$headers);
}
// prep the lang strings for export
if (isset($_export) && $_export && UtilitiesArrayHelper::check($items))
{
// insure we have the same order in the languages
$languages = ComponentbuilderHelper::getVars('language', 1, 'published', 'langtag');
foreach ($items as $nr => &$item)
{
// remove some values completely
unset($item->components);
unset($item->modules);
unset($item->plugins);
unset($item->params);
unset($item->published);
unset($item->created_by);
unset($item->modified_by);
unset($item->created);
unset($item->modified);
unset($item->version);
unset($item->hits);
unset($item->access);
unset($item->ordering);
// set the lang order
if ($nr != 0)
{
foreach ($languages as $lanTag)
{
$item->{$lanTag} = '';
}
// now adapt the source
if (isset($item->translation) && JsonHelper::check($item->translation))
{
$translations = json_decode($item->translation, true);
if (UtilitiesArrayHelper::check($translations))
{
foreach ($translations as $language)
{
if (isset($language['translation']) && StringHelper::check($language['translation'])
&& isset($language['language']) && StringHelper::check($language['language']))
{
$item->{$language['language']} = $language['translation'];
}
}
}
}
}
// remove translation
unset($item->translation);
}
}
return $items;
}
}

View File

@ -23,9 +23,11 @@ use Joomla\Utilities\ArrayHelper;
use Joomla\Input\Input;
use VDM\Component\Componentbuilder\Administrator\Helper\ComponentbuilderHelper;
use Joomla\CMS\Helper\TagsHelper;
use VDM\Joomla\Utilities\FormHelper;
use VDM\Joomla\Utilities\ArrayHelper as UtilitiesArrayHelper;
use VDM\Joomla\Utilities\ObjectHelper;
use VDM\Joomla\Utilities\StringHelper;
use Joomla\CMS\Form\Form;
// No direct access to this file
\defined('_JEXEC') or die;
@ -89,8 +91,8 @@ class RepositoriesModel extends ListModel
'a.organisation','organisation',
'a.repository','repository',
'a.target','target',
'a.base','base',
'a.type','type'
'a.type','type',
'a.base','base'
);
}
@ -99,6 +101,48 @@ class RepositoriesModel extends ListModel
$this->app ??= Factory::getApplication();
}
/**
* Get the filter form - Override the parent method
*
* @param array $data data
* @param boolean $loadData load current data
*
* @return Form|boolean The Form object or false on error
*
* @since JCB 2.12.5
*/
public function getFilterForm($data = array(), $loadData = true)
{
// load form from the parent class
$form = parent::getFilterForm($data, $loadData);
// Create the "read write branch" filter
$attributes = array(
'name' => 'branch',
'type' => 'list',
'onchange' => 'this.form.submit();',
);
$options = [
'' => '- ' . Text::_('COM_COMPONENTBUILDER_SELECT_BRANCH_STATE') . ' -',
'write' => Text::_('COM_COMPONENTBUILDER_WRITE_BRANCH_SET'),
'no_write' => Text::_('COM_COMPONENTBUILDER_NO_WRITE_BRANCH_SET'),
'read' => Text::_('COM_COMPONENTBUILDER_READ_BRANCH_SET'),
'no_read' => Text::_('COM_COMPONENTBUILDER_NO_READ_BRANCH_SET'),
'both' => Text::_('COM_COMPONENTBUILDER_BOTH_BRANCHES_SET'),
'none' => Text::_('COM_COMPONENTBUILDER_NO_BRANCHES_SET')
];
$form->setField(FormHelper::xml($attributes, $options),'filter');
$form->setValue(
'branch',
'filter',
$this->state->get("filter.branch")
);
array_push($this->filter_fields, 'branch');
return $form;
}
/**
* Method to auto-populate the model state.
*
@ -166,13 +210,6 @@ class RepositoriesModel extends ListModel
$this->setState('filter.target', $target);
}
$base = $this->getUserStateFromRequest($this->context . '.filter.base', 'filter_base');
if ($formSubmited)
{
$base = $app->input->post->get('base');
$this->setState('filter.base', $base);
}
$type = $this->getUserStateFromRequest($this->context . '.filter.type', 'filter_type');
if ($formSubmited)
{
@ -180,6 +217,13 @@ class RepositoriesModel extends ListModel
$this->setState('filter.type', $type);
}
$base = $this->getUserStateFromRequest($this->context . '.filter.base', 'filter_base');
if ($formSubmited)
{
$base = $app->input->post->get('base');
$this->setState('filter.base', $base);
}
// List state information.
parent::populateState($ordering, $direction);
}
@ -298,6 +342,51 @@ class RepositoriesModel extends ListModel
// From the componentbuilder_item table
$query->from($db->quoteName('#__componentbuilder_repository', 'a'));
// Filtering by "branch"
$filterBranch = $this->state->get('filter.branch');
// Ensure the filter value is a string and not empty
if (is_string($filterBranch) && $filterBranch !== '')
{
$readBranch = 'CHAR_LENGTH(TRIM(' . $db->quoteName('a.read_branch') . '))';
$writeBranch = 'CHAR_LENGTH(TRIM(' . $db->quoteName('a.write_branch') . '))';
switch ($filterBranch)
{
case 'both':
// Both read_branch and write_branch must be set
$query->where("{$readBranch} > 1");
$query->where("{$writeBranch} > 1");
break;
case 'none':
// Neither read_branch nor write_branch must be set
$query->where("{$readBranch} = 0");
$query->where("{$writeBranch} = 0");
break;
case 'read':
// Only read_branch must be set
$query->where("{$readBranch} > 1");
break;
case 'write':
// Only write_branch must be set
$query->where("{$writeBranch} > 1");
break;
case 'no_read':
// Only read_branch must NOT be set
$query->where("{$readBranch} = 0");
break;
case 'no_write':
// Only write_branch must NOT be set
$query->where("{$writeBranch} = 0");
break;
}
}
// Filter by published state
$published = $this->getState('filter.published');
if (is_numeric($published))
@ -397,6 +486,23 @@ class RepositoriesModel extends ListModel
{
$query->where('a.target = ' . $db->quote($db->escape($_target)));
}
// Filter by Type.
$_type = $this->getState('filter.type');
if (is_numeric($_type))
{
if (is_float($_type))
{
$query->where('a.type = ' . (float) $_type);
}
else
{
$query->where('a.type = ' . (int) $_type);
}
}
elseif (StringHelper::check($_type))
{
$query->where('a.type = ' . $db->quote($db->escape($_type)));
}
// Filter by Base.
$_base = $this->getState('filter.base');
if (is_numeric($_base))
@ -458,8 +564,8 @@ class RepositoriesModel extends ListModel
$id .= ':' . $this->getState('filter.organisation');
$id .= ':' . $this->getState('filter.repository');
$id .= ':' . $this->getState('filter.target');
$id .= ':' . $this->getState('filter.base');
$id .= ':' . $this->getState('filter.type');
$id .= ':' . $this->getState('filter.base');
return parent::getStoreId($id);
}

View File

@ -403,14 +403,6 @@ class HtmlView extends BaseHtmlView
{
Html::_('script', $script, ['version' => 'auto']);
}
// add JavaScripts
Html::_('script', 'media/com_componentbuilder/uikit-v2/js/uikit.min.js', ['version' => 'auto']);
Html::_('script', 'media/com_componentbuilder/uikit-v2/js/components/lightbox.min.js', ['version' => 'auto']);
Html::_('script', 'media/com_componentbuilder/uikit-v2/js/components/notify.min.js', ['version' => 'auto']);
// add the style sheets
Html::_('stylesheet', 'media/com_componentbuilder/uikit-v2/css/uikit.gradient.min.css', ['version' => 'auto']);
Html::_('stylesheet', 'media/com_componentbuilder/uikit-v2/css/components/notify.gradient.min.css', ['version' => 'auto']);
// add var key
$this->getDocument()->addScriptDeclaration("var vastDevMod = '".$this->get('VDM')."';");
// LayoutHelper::render('exportlanguagetranslations', []); // to ensure that the layout gets loaded
}
}

View File

@ -15,7 +15,6 @@ use Joomla\CMS\HTML\HTMLHelper as Html;
use Joomla\CMS\Layout\LayoutHelper;
use Joomla\CMS\Router\Route;
use VDM\Component\Componentbuilder\Administrator\Helper\ComponentbuilderHelper;
use Joomla\CMS\Uri\Uri;
/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = $this->getDocument()->getWebAssetManager();
@ -102,26 +101,3 @@ $tmpl = $tmpl ? '&tmpl=' . $tmpl : '';
</div>
</form>
</div>
<script type="text/javascript">
<?php
$app = Factory::getApplication();
?>
function JRouter(link) {
<?php
if ($app->isClient('site'))
{
echo 'var url = "'. Uri::root() . '";';
}
else
{
echo 'var url = "";';
}
?>
return url+link;
}
</script>

View File

@ -49,3 +49,30 @@ if ($this->saveOrder)
<input type="hidden" name="task" value="" />
<?php echo Html::_('form.token'); ?>
</form>
<script type="text/javascript">
// language_translations footer script
// get page body
var outerBodyDiv = document.querySelector('body');
// start loading spinner
var loadingDiv = document.createElement('div');
loadingDiv.id = 'loading';
// Set CSS properties individually
loadingDiv.style.background = "rgba(255, 255, 255, .8) url('components/com_componentbuilder/assets/images/ajax.gif') 50% 35% no-repeat";
loadingDiv.style.top = (outerBodyDiv.getBoundingClientRect().top + window.pageYOffset) + "px";
loadingDiv.style.left = (outerBodyDiv.getBoundingClientRect().left + window.pageXOffset) + "px";
loadingDiv.style.width = outerBodyDiv.offsetWidth + "px";
loadingDiv.style.height = outerBodyDiv.offsetHeight + "px";
loadingDiv.style.position = 'fixed';
loadingDiv.style.opacity = '0.80';
loadingDiv.style.msFilter = "progid:DXImageTransform.Microsoft.Alpha(Opacity=80)";
loadingDiv.style.filter = "alpha(opacity=80)";
loadingDiv.style.display = 'none';
// add to page body
outerBodyDiv.appendChild(loadingDiv);
</script>
<?php echo LayoutHelper::render('exportlanguagetranslations', []); ?>
</script>

View File

@ -61,3 +61,30 @@ $wa->useScript('core')
<input type="hidden" name="task" value="" />
<?php echo Html::_('form.token'); ?>
</form>
<script type="text/javascript">
// language_translations footer script
// get page body
var outerBodyDiv = document.querySelector('body');
// start loading spinner
var loadingDiv = document.createElement('div');
loadingDiv.id = 'loading';
// Set CSS properties individually
loadingDiv.style.background = "rgba(255, 255, 255, .8) url('components/com_componentbuilder/assets/images/ajax.gif') 50% 35% no-repeat";
loadingDiv.style.top = (outerBodyDiv.getBoundingClientRect().top + window.pageYOffset) + "px";
loadingDiv.style.left = (outerBodyDiv.getBoundingClientRect().left + window.pageXOffset) + "px";
loadingDiv.style.width = outerBodyDiv.offsetWidth + "px";
loadingDiv.style.height = outerBodyDiv.offsetHeight + "px";
loadingDiv.style.position = 'fixed';
loadingDiv.style.opacity = '0.80';
loadingDiv.style.msFilter = "progid:DXImageTransform.Microsoft.Alpha(Opacity=80)";
loadingDiv.style.filter = "alpha(opacity=80)";
loadingDiv.style.display = 'none';
// add to page body
outerBodyDiv.appendChild(loadingDiv);
</script>
<?php echo LayoutHelper::render('exportlanguagetranslations', []); ?>
</script>

View File

@ -97,7 +97,15 @@ $edit = "index.php?option=com_componentbuilder&view=repositories&task=repository
<?php echo $this->escape($item->organisation); ?>
</td>
<td class="hidden-phone">
<?php echo $this->escape($item->repository); ?>
<div>
<?php echo $this->escape($item->repository); ?>
<?php if (!empty($item->read_branch)): ?>
<br><small><?php echo Text::_('COM_COMPONENTBUILDER_READ_BRANCH'); ?>: <?php echo $item->read_branch; ?></small>
<?php endif; ?>
<?php if (!empty($item->write_branch)): ?>
<br><small><?php echo Text::_('COM_COMPONENTBUILDER_WRITE_BRANCH'); ?>: <?php echo $item->write_branch; ?></small>
<?php endif; ?>
</div>
</td>
<td class="hidden-phone">
<?php echo Text::_($item->target); ?>

View File

@ -1,15 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<extension type="component" version="5.0" method="upgrade">
<name>COM_COMPONENTBUILDER</name>
<creationDate>1st July, 2025</creationDate>
<creationDate>2nd July, 2025</creationDate>
<author>Llewellyn van der Merwe</author>
<authorEmail>joomla@vdm.io</authorEmail>
<authorUrl>https://dev.vdm.io</authorUrl>
<copyright>Copyright (C) 2015 Vast Development Method. All rights reserved.</copyright>
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
<version>5.1.1-beta6</version>
<version>5.1.1-rc1</version>
<description><![CDATA[
<h1>Component Builder (v.5.1.1-beta6)</h1>
<h1>Component Builder (v.5.1.1-rc1)</h1>
<div style="clear: both;"></div>
<p>The Component Builder for [Joomla](https://extensions.joomla.org/extension/component-builder/) is highly advanced tool that is truly able to build extremely complex components in a fraction of the time.

View File

@ -131,31 +131,13 @@
<element>pkg_component_builder</element>
<type>package</type>
<client>site</client>
<version>5.1.1-beta</version>
<version>5.1.1-rc1</version>
<infourl title="Component Builder!">https://dev.vdm.io</infourl>
<downloads>
<downloadurl type="full" format="zip">https://github.com/joomengine/pkg-component-builder/archive/refs/tags/v5.1.1-beta6.zip</downloadurl>
<downloadurl type="full" format="zip">https://github.com/joomengine/pkg-component-builder/archive/refs/tags/v5.1.1-rc1.zip</downloadurl>
</downloads>
<tags>
<tag>beta</tag>
</tags>
<maintainer>Llewellyn van der Merwe</maintainer>
<maintainerurl>https://dev.vdm.io</maintainerurl>
<targetplatform name="joomla" version="5\.[0123]"/>
</update>
<update>
<name>Component Builder</name>
<description>Builds Complex Joomla Components</description>
<element>pkg_component_builder</element>
<type>package</type>
<client>site</client>
<version>5.1.1-beta6</version>
<infourl title="Component Builder!">https://dev.vdm.io</infourl>
<downloads>
<downloadurl type="full" format="zip">https://github.com/joomengine/pkg-component-builder/archive/refs/tags/v5.1.1-beta6.zip</downloadurl>
</downloads>
<tags>
<tag>beta</tag>
<tag>release_candidate</tag>
</tags>
<maintainer>Llewellyn van der Merwe</maintainer>
<maintainerurl>https://dev.vdm.io</maintainerurl>

View File

@ -9,40 +9,3 @@
*/
jQuery(document).ready(function($)
{
// set button to add more languages
addButton('language','entranslation');
});
function addData(result,where){
jQuery(result).insertAfter(jQuery(where).closest('.control-group'));
}
function addButton_server(type, size){
var getUrl = JRouter("index.php?option=com_componentbuilder&task=ajax.getButton&format=json&raw=true&vdm="+vastDevMod);
if(token.length > 0 && type.length > 0){
var request = token+'=1&type='+type+'&size='+size;
}
return jQuery.ajax({
type: 'GET',
url: getUrl,
dataType: 'json',
data: request,
jsonp: false
});
}
function addButton(type, where, size){
// just to insure that default behaviour still works
size = typeof size !== 'undefined' ? size : 1;
addButton_server(type, size).done(function(result) {
if(result){
if (2 == size) {
jQuery('#'+where).html(result);
} else {
addData(result, '#jform_'+where);
}
}
})
}