From 96b3c51eee0b9e89db30c6bf125435099db7ceeb Mon Sep 17 00:00:00 2001
From: aB0t
Date: Thu, 21 May 2026 20:46:02 +0000
Subject: [PATCH] Release of v5.1.6
---
CHANGELOG.md | 173 +--
ComponentbuilderInstallerScript.php | 4 +-
README.md | 6 +-
admin/README.txt | 6 +-
admin/access.xml | 1 +
admin/config.xml | 8 +
admin/forms/admin_fields.xml | 2 +-
admin/forms/admin_fields_conditions.xml | 2 +-
admin/forms/admin_view.xml | 1 -
admin/forms/component_admin_views.xml | 2 +-
admin/forms/component_updates.xml | 2 +-
admin/forms/custom_admin_view.xml | 1 -
admin/forms/dynamic_get.xml | 10 +-
admin/forms/fieldtype.xml | 2 +-
admin/forms/library.xml | 2 +-
admin/forms/site_view.xml | 1 -
.../en-GB/en-GB.com_componentbuilder.ini | 13 +-
.../en-GB/en-GB.com_componentbuilder.sys.ini | 2 +
.../{filedisplay.php => filedisplayjfive.php} | 0
...ody.php => reposelectioncardbodyjfive.php} | 0
...ranslationimportercolumnsdisplayjfive.php} | 2 +-
...> translationimportereasymappingjfive.php} | 0
admin/sql/install.mysql.utf8.sql | 23 +-
.../updates/mysql/{4_1_3.sql => 4_1_5.sql} | 0
.../updates/mysql/{6_1_4.sql => 5_1_5.sql} | 0
admin/sql/updates/mysql/6_1_6.sql | 1 +
admin/src/Controller/Admin_viewController.php | 3 +-
admin/src/Controller/CompilerController.php | 43 +
admin/src/Helper/ComponentbuilderHelper.php | 481 ++++---
admin/src/Model/Admin_viewModel.php | 8 +-
admin/src/Model/Admin_viewsModel.php | 8 +-
admin/src/Model/AjaxModel.php | 1123 ++++++++++++-----
admin/src/Model/FieldModel.php | 1 +
admin/src/Model/FieldsModel.php | 19 +-
admin/src/Model/LibraryModel.php | 5 +-
admin/src/Model/Site_viewModel.php | 40 +-
admin/src/Model/Site_viewsModel.php | 2 +
admin/src/View/Compiler/HtmlView.php | 5 +
.../src/View/Import_translations/HtmlView.php | 4 +-
admin/tmpl/admin_custom_tabs/default.php | 38 +-
admin/tmpl/admin_fields/default.php | 38 +-
.../tmpl/admin_fields_conditions/default.php | 38 +-
admin/tmpl/admin_fields_relations/default.php | 38 +-
admin/tmpl/admin_view/default.php | 93 +-
admin/tmpl/admin_views/default.php | 24 +-
admin/tmpl/admin_views/modal.php | 24 +-
admin/tmpl/class_extends/default.php | 38 +-
admin/tmpl/class_method/default.php | 38 +-
admin/tmpl/class_property/default.php | 38 +-
admin/tmpl/compiler/default.php | 24 +-
admin/tmpl/component_admin_views/default.php | 38 +-
admin/tmpl/component_config/default.php | 38 +-
.../component_custom_admin_menus/default.php | 38 +-
.../component_custom_admin_views/default.php | 38 +-
admin/tmpl/component_dashboard/default.php | 38 +-
.../tmpl/component_files_folders/default.php | 38 +-
admin/tmpl/component_modules/default.php | 38 +-
admin/tmpl/component_mysql_tweaks/default.php | 38 +-
admin/tmpl/component_placeholders/default.php | 38 +-
admin/tmpl/component_plugins/default.php | 38 +-
admin/tmpl/component_router/default.php | 38 +-
admin/tmpl/component_site_views/default.php | 38 +-
admin/tmpl/component_updates/default.php | 38 +-
admin/tmpl/custom_admin_view/default.php | 38 +-
admin/tmpl/custom_admin_views/default.php | 24 +-
admin/tmpl/custom_admin_views/modal.php | 24 +-
admin/tmpl/custom_code/default.php | 38 +-
admin/tmpl/custom_codes/default.php | 24 +-
admin/tmpl/custom_codes/modal.php | 24 +-
admin/tmpl/dynamic_get/default.php | 38 +-
admin/tmpl/dynamic_gets/default.php | 24 +-
admin/tmpl/dynamic_gets/modal.php | 24 +-
admin/tmpl/field/default.php | 95 +-
admin/tmpl/fields/default.php | 25 +-
admin/tmpl/fields/modal.php | 25 +-
admin/tmpl/fieldtype/default.php | 38 +-
admin/tmpl/help_document/default.php | 38 +-
admin/tmpl/import_translations/default.php | 2 +-
... default_translationimporterareajfive.php} | 0
.../tmpl/initialization_selection/default.php | 2 +-
...rea.php => default_selectionareajfive.php} | 26 +-
admin/tmpl/joomla_component/default.php | 38 +-
admin/tmpl/joomla_components/default.php | 24 +-
admin/tmpl/joomla_components/modal.php | 24 +-
admin/tmpl/joomla_module/default.php | 38 +-
.../default.php | 38 +-
admin/tmpl/joomla_module_updates/default.php | 38 +-
admin/tmpl/joomla_modules/default.php | 24 +-
admin/tmpl/joomla_modules/modal.php | 24 +-
admin/tmpl/joomla_plugin/default.php | 38 +-
.../default.php | 38 +-
admin/tmpl/joomla_plugin_group/default.php | 38 +-
admin/tmpl/joomla_plugin_updates/default.php | 38 +-
admin/tmpl/joomla_plugins/default.php | 24 +-
admin/tmpl/joomla_plugins/modal.php | 24 +-
admin/tmpl/joomla_power/default.php | 38 +-
admin/tmpl/language/default.php | 38 +-
admin/tmpl/language_translation/default.php | 38 +-
admin/tmpl/language_translations/default.php | 24 +-
admin/tmpl/language_translations/modal.php | 24 +-
admin/tmpl/languages/default.php | 24 +-
admin/tmpl/languages/modal.php | 24 +-
admin/tmpl/layout/default.php | 38 +-
admin/tmpl/layouts/default.php | 24 +-
admin/tmpl/layouts/modal.php | 24 +-
admin/tmpl/libraries/default.php | 24 +-
admin/tmpl/libraries/modal.php | 24 +-
admin/tmpl/library/default.php | 38 +-
admin/tmpl/library_config/default.php | 38 +-
.../library_files_folders_urls/default.php | 38 +-
admin/tmpl/placeholder/default.php | 38 +-
admin/tmpl/power/default.php | 38 +-
admin/tmpl/powers/default.php | 24 +-
admin/tmpl/powers/modal.php | 24 +-
admin/tmpl/pull_selection/default.php | 2 +-
...php => default_pullselectionareajfive.php} | 26 +-
admin/tmpl/repository/default.php | 38 +-
admin/tmpl/server/default.php | 38 +-
admin/tmpl/site_view/default.php | 38 +-
admin/tmpl/site_views/default.php | 24 +-
admin/tmpl/site_views/modal.php | 24 +-
admin/tmpl/snippet_type/default.php | 38 +-
admin/tmpl/snippets/default.php | 24 +-
admin/tmpl/snippets/modal.php | 24 +-
admin/tmpl/template/default.php | 38 +-
admin/tmpl/templates/default.php | 24 +-
admin/tmpl/templates/modal.php | 24 +-
admin/tmpl/validation_rule/default.php | 38 +-
changelog.xml | 193 +--
componentbuilder.xml | 6 +-
componentbuilder_update_server.xml | 67 +-
.../Compiler/Component/JoomlaSix/Settings.php | 159 ---
.../Compiler/Component/Placeholder.php | 283 +++--
.../Compiler/Helper/Interpretation.php | 62 +-
.../Compiler/Library/Data.php | 65 +-
.../Compiler/Model/JoomlaFive/Customtabs.php | 6 +-
.../Compiler/Model/JoomlaSix/Customtabs.php | 6 +-
.../Compiler/Model/Libraries.php | 157 ++-
.../Componentbuilder/User/IdentityTrait.php | 12 +-
media/js/admin_view.js | 292 +++--
media/js/custom_code.js | 1 -
media/js/placeholder.js | 1 -
142 files changed, 3777 insertions(+), 2403 deletions(-)
rename admin/layouts/{filedisplay.php => filedisplayjfive.php} (100%)
rename admin/layouts/{reposelectioncardbody.php => reposelectioncardbodyjfive.php} (100%)
rename admin/layouts/{translationimportercolumnsdisplay.php => translationimportercolumnsdisplayjfive.php} (96%)
rename admin/layouts/{translationimportereasymapping.php => translationimportereasymappingjfive.php} (100%)
rename admin/sql/updates/mysql/{4_1_3.sql => 4_1_5.sql} (100%)
rename admin/sql/updates/mysql/{6_1_4.sql => 5_1_5.sql} (100%)
create mode 100644 admin/sql/updates/mysql/6_1_6.sql
rename admin/tmpl/import_translations/{default_translationimporterarea.php => default_translationimporterareajfive.php} (100%)
rename admin/tmpl/initialization_selection/{default_selectionarea.php => default_selectionareajfive.php} (87%)
rename admin/tmpl/pull_selection/{default_pullselectionarea.php => default_pullselectionareajfive.php} (86%)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 065f2354b..276e5bec1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,71 +1,36 @@
-# v6.1.4
+# v6.1.6
-- Fix the linked [new] button to use the correct init_default params.
-- Fix the Components list view to better display long website urls.
-- Fix language text area css. #1264
-- Refactor the JS selText method.
-- Add fix for empty currentFullPath in the Structuresingle class.
-- Fix the buildSelectPart method to not access an empty select value in the Queries class.
-- Fix that ensure that we always load and int into loadUserById.
-- Fix the subform permission behaviour to use removeField.
-- Add version 6 to the version 5 update server, for easier upgrade path.
-- Fixed linked admin views with New and New & Close buttons to correctly initialize fields using global unique IDs and default conventions.
-- Resolved missing language strings when linked admin views are also configured as site edit views.
-- Corrected misconfiguration of the isModal property for site-linked views to default to false since modals are not yet supported on the site area.
-- Enhanced security in edit views by moving access validation into the model, ensuring protection even when Joomla bypasses the controller layer.
-- Added user access verification logic in the model to prevent unauthorized direct item loading.
-- Refactored the view class to introduce a class constructor that initializes key dependencies more reliably.
-- Began aligning the view class structure with Joomla 6's updated MVC architecture for future compatibility.
-- Improved initialization handling in the view to reduce dependency errors during rendering.
-- Fixed a bug caused by missing option settings in the model configuration.
-- Updated and refined the site view class for greater consistency with backend view logic.
-- Continued progressive improvements across view classes to ensure compatibility with Joomla's evolving MVC patterns.
-- Fix permission issue in the list view. #1266
-- Refactor the list view buttons.
-- Refactor the getActions helper method.
-- Rename Custom Buttons tab to Toolbar for clearer grouping of toolbar functions. #1268
-- Add options for overriding default toolbars in all Admin, Site, and Custom Admin Views. #1268
-- Refactor toolbar into version specific classes to resolve getToolbar errors. #1267
-- Add method to unify getDatabase handling across Joomla versions and prevent undefined method calls. #1270
-- Fix php8.3 deprecation warnings in compiler templates. #1265
-- Refactor and enhance the JCB Email Helper class for a more robust and maintainable design.
-- Fix an issue where the Email Helper send() function was executed twice.
-- Fix a issue where layouts added to the PHP Ajax Model were not loaded during compilation.
-- Add new functionality to the toolbar override system, enabling the inclusion of custom buttons via placeholders.
-- Fix the Compiler success message to display both build seconds and project weeks.
-- Add Development Valuation Model to calculate JCB component pricing.
-- Add functionality to push JCB packages to Self hosted Git system that uses a self signed SSL.
-- Add functionality that will include Validation Rules linked to fields in JCB packages. #1273
-- Refactor and Improve Router Helper Class to new standard Site Router practice.
-- Refactor and Improve JCB Search engine for better searching of JCB areas.
-- Fix bug in the search editor in JCB Search Engine.
-- Fix issue where Custom Gets linked to a Site view did not ship correctly with JCB packages. #1272
-- Fix issue where the Icon linked to a Custom Admin Menu did not ship with JCB packages. #1271
-- Fix and Improve Normalize Class to better handle different files Linked to JCB packages. #1274
-- Remove batch feature from JCB compiler Templates due to lack of use and outdated code.
-- Refactor Utilities/Response classes for Joomla 6+ compatibility and improved version-agnostic behavior.
-- Refactor the File Upload Manager to achieve improved maintainability, greater customization flexibility, and easier extensibility.
-- Refactor the Item Importer to improve data management, structural clarity, and long-term maintainability of the import process.
-- Add a Pull button to all main JCB entities, allowing a complete reset of a selected entity together with all entities linked to it, from a selected remote repository.
-- Add functionality to the compiler to automatically pull missing entities from a remote repository when they are referenced by a component but not present locally in JCB.
-- Refactor the Packaging Engine into separate set and get classes to improve separation of concerns, readability, and overall maintainability.
-- Refactor and restructure the demo console item import plugin to improve usability, maintainability, and reusability across other areas of JCB.
-- Refactor the Compiler to remove deprecated code paths and legacy classes that are no longer maintained.
-- Add new Compiler Classes to modernise ancient tech previously embedded in the compiler and replace it with a clean, modern architecture.
-- Add a dedicated CLI plugin to the JCB core, enabling direct interaction with JCB through the Joomla CLI console.
-- Add structured CLI commands and options to Init, Reset, Push, and Pull JCB entities via the Joomla CLI.
-- Add full support for compiling Components directly via the CLI, enabling automated and headless build workflows.
-- Add functionality to place Super Power classes into the component src folder for both Admin and Site areas, using the correct namespaced structure.
-- Add functionality to the CLI compiler to directly install compiled components and apply compiler options used during compilation.
-- Add functionality to the compiler to automatically pull a missing component from a remote repository when a compilation is triggered and the component is not locally available.
-- Add functionality to add and replace classes in module and plugin src folders that are linked to a component, when using correct namespace resolution.
-- Add a Documentation tab to the Joomla Component Builder dashboard.
-- Fix an issue with the JCB version notice on Joomla 5.
-- Fix an issue where components without a component image failed to compile successfully. #1282
-- Add functionality for importing translated language strings directly into the Language Translations area of JCB.
-- Fix issue where the Language Translations export button was not displayed due to recent toolbar changes.
-- Fix a bug that caused language filters to malfunction because of a broken field configuration. #1278
-- Fix issue where components without a layout added to the Component failed to compile correctly. #1132
+- Add Health Check buttons to the JCB Compiler and Component list view to verify system PHP configuration.
+- Add the Toolbar Custom Buttons subform to the Global Subform settings for improved usability and presentation.
+- Fix the Field edit view to ensure full compliance with Joomla 6 standards and updated UI/UX conventions.
+- Begin migration from UIkit to Bootstrap 5 to align JCB with Joomla 6+ standards.
+- Complete Bootstrap 5 refactor of all primary views and core UI.
+- Continue phased removal of UIkit with full transition planned for upcoming releases.
+- Fix deprecated JText usage in Admin Custom Tabs area for Joomla 5+ compatibility. #1302
+- Fix snippet loading for Joomla 6, improving reliability and removing legacy UIkit dependencies during migration.
+- Introduced strict version-aware compilation to ensure libraries are only included when explicitly defined for Joomla 6.
+- Disabled automatic UIkit injection for Joomla 6, preventing auto-loading across custom admin views, site views, templates, and layouts.
+- Migrate Search view for J6 to use Bootstrap and Joomla-native HTML and JavaScript.
+- Remove all manually linked Bootstrap v4 assets from the component.
+- Improve subform ordering fields for clearer display and better readability.
+- Refactor the JavaScript waiting spinner to use solid, native JavaScript with improved page-loading handling.
+
+# v5.1.6
+
+- Add Health Check buttons to the JCB Compiler and Component list view to verify system PHP configuration.
+- Add the Toolbar Custom Buttons subform to the Global Subform settings for improved usability and presentation.
+- Fix the Field edit view to ensure full compliance with Joomla 6 standards and updated UI/UX conventions.
+- Begin migration from UIkit to Bootstrap 5 to align JCB with Joomla 6+ standards.
+- Complete Bootstrap 5 refactor of all primary views and core UI.
+- Continue phased removal of UIkit with full transition planned for upcoming releases.
+- Fix deprecated JText usage in Admin Custom Tabs area for Joomla 5+ compatibility. #1302
+- Fix snippet loading for Joomla 6, improving reliability and removing legacy UIkit dependencies during migration.
+- Introduced strict version-aware compilation to ensure libraries are only included when explicitly defined for Joomla 6.
+- Disabled automatic UIkit injection for Joomla 6, preventing auto-loading across custom admin views, site views, templates, and layouts.
+- Migrate Search view for J6 to use Bootstrap and Joomla-native HTML and JavaScript.
+- Remove all manually linked Bootstrap v4 assets from the component.
+- Improve subform ordering fields for clearer display and better readability.
+- Refactor the JavaScript waiting spinner to use solid, native JavaScript with improved page-loading handling.
# v5.1.5
@@ -364,66 +329,22 @@
- Add the BaseDatabaseModel use statement to custom site view controller. #1119
- Fix the customfolderlist field. #1120
-# v4.1.3
+# v4.1.5
-- Fix the linked [new] button to use the correct init_default params.
-- Fix the Components list view to better display long website urls.
-- Fix language text area css. #1264
-- Refactor the JS selText method.
-- Add fix for empty currentFullPath in the Structuresingle class.
-- Fix the buildSelectPart method to not access an empty select value in the Queries class.
-- Fix that ensure that we always load and int into loadUserById.
-- Fix the subform permission behaviour to use removeField.
-- Add version 5 to the version 4 update server, for easier upgrade path.
-- Fixed linked admin views with New and New & Close buttons to correctly initialize fields using global unique IDs and default conventions.
-- Resolved missing language strings when linked admin views are also configured as site edit views.
-- Corrected misconfiguration of the isModal property for site-linked views to default to false since modals are not yet supported on the site area.
-- Enhanced security in edit views by moving access validation into the model, ensuring protection even when Joomla bypasses the controller layer.
-- Added user access verification logic in the model to prevent unauthorized direct item loading.
-- Refactored the view class to introduce a class constructor that initializes key dependencies more reliably.
-- Began aligning the view class structure with Joomla 6's updated MVC architecture for future compatibility.
-- Improved initialization handling in the view to reduce dependency errors during rendering.
-- Fixed a bug caused by missing option settings in the model configuration.
-- Updated and refined the site view class for greater consistency with backend view logic.
-- Continued progressive improvements across view classes to ensure compatibility with Joomla's evolving MVC patterns.
-- Fix permission issue in the list view. #1266
-- Refactor the list view buttons
-- Refactor the getActions helper method
-- Refactor toolbar into version-specific classes to resolve getToolbar errors. #1267
-- Add method to unify getDatabase handling across Joomla versions and prevent undefined method calls. #1270
-- Refactor and enhance the JCB Email Helper class for a more robust and maintainable design.
-- Fix an issue where the Email Helper send() function was executed twice.
-- Fix a issue where layouts added to the PHP Ajax Model were not loaded during compilation.
-- Fix the Compiler success message to display both build seconds and project weeks.
-- Add Development Valuation Model to calculate JCB component pricing.
-- Add functionality to push JCB packages to Self hosted Git system that uses a self signed SSL.
-- Add functionality that will include Validation Rules linked to fields in JCB packages. #1273
-- Refactor and Improve Router Helper Class to new standard Site Router practice.
-- Refactor and Improve JCB Search engine for better searching of JCB areas.
-- Fix bug in the search editor in JCB Search Engine.
-- Fix issue where Custom Gets linked to a Site view did not ship correctly with JCB packages. #1272
-- Fix issue where the Icon linked to a Custom Admin Menu did not ship with JCB packages. #1271
-- Fix and Improve Normalize Class to better handle different files Linked to JCB packages. #1274
-- Remove batch feature from JCB compiler Templates due to lack of use and outdated code.
-- Refactor Utilities/Response classes for Joomla 6+ compatibility and improved version-agnostic behavior.
-- Refactor the File Upload Manager to achieve improved maintainability, greater customization flexibility, and easier extensibility.
-- Refactor the Item Importer to improve data management, structural clarity, and long-term maintainability of the import process.
-- Add a Pull button to all main JCB entities, allowing a complete reset of a selected entity together with all entities linked to it.
-- Add functionality to the Compiler to automatically pull missing entities from a remote repository when they are referenced by a component but not present locally in JCB.
-- Refactor the Packaging Engine into separate set and get classes to improve separation of concerns, readability, and overall maintainability.
-- Refactor and restructure the demo console item import plugin to improve usability, maintainability, and reusability across other areas of JCB.
-- Refactor the Compiler to remove deprecated code paths and legacy classes that are no longer maintained.
-- Add new Compiler Classes to modernise ancient tech previously embedded in the compiler and replace it with a clean, modern architecture.
-- Add a dedicated CLI plugin to the JCB core, enabling direct interaction with JCB through the Joomla CLI console.
-- Add structured CLI commands and options to Init, Reset, Push, and Pull JCB entities via the Joomla CLI.
-- Add full support for compiling Components directly via the CLI, enabling automated and headless build workflows.
-- Add functionality to place Super Power classes into the component src folder for both Admin and Site areas, using the correct namespaced structure.
-- Add functionality to the CLI compiler to directly install compiled components and apply compiler options used during compilation.
-- Add functionality to the compiler to automatically pull a missing component from a remote repository when a compilation is triggered and the component is not locally available.
-- Add functionality to add and replace classes in module and plugin src folders that are linked to a component, when using correct namespace resolution.
-- Add a Documentation tab to the Joomla Component Builder dashboard.
-- Fix an issue with the JCB version notice on Joomla 5.
-- Fix an issue where components without a component image failed to compile successfully. #1282
+- Add Health Check buttons to the JCB Compiler and Component list view to verify system PHP configuration.
+- Add the Toolbar Custom Buttons subform to the Global Subform settings for improved usability and presentation.
+- Fix the Field edit view to ensure full compliance with Joomla 6 standards and updated UI/UX conventions.
+- Begin migration from UIkit to Bootstrap 5 to align JCB with Joomla 6+ standards.
+- Complete Bootstrap 5 refactor of all primary views and core UI.
+- Continue phased removal of UIkit with full transition planned for upcoming releases.
+- Fix deprecated JText usage in Admin Custom Tabs area for Joomla 5+ compatibility. #1302
+- Fix snippet loading for Joomla 6, improving reliability and removing legacy UIkit dependencies during migration.
+- Introduced strict version-aware compilation to ensure libraries are only included when explicitly defined for Joomla 6.
+- Disabled automatic UIkit injection for Joomla 6, preventing auto-loading across custom admin views, site views, templates, and layouts.
+- Migrate Search view for J6 to use Bootstrap and Joomla-native HTML and JavaScript.
+- Remove all manually linked Bootstrap v4 assets from the component.
+- Improve subform ordering fields for clearer display and better readability.
+- Refactor the JavaScript waiting spinner to use solid, native JavaScript with improved page-loading handling.
# v3.2.5
diff --git a/ComponentbuilderInstallerScript.php b/ComponentbuilderInstallerScript.php
index f5858d790..7c0ca30ee 100644
--- a/ComponentbuilderInstallerScript.php
+++ b/ComponentbuilderInstallerScript.php
@@ -1517,7 +1517,7 @@ class Com_ComponentbuilderInstallerScript implements InstallerScriptInterface
// Fix the assets table rules column size.
- $this->setDatabaseAssetsRulesFix(106880, "MEDIUMTEXT");
+ $this->setDatabaseAssetsRulesFix(107040, "MEDIUMTEXT");
// Install the global extension params.
$this->setExtensionsParams(
'{"autorName":"Llewellyn van der Merwe","autorEmail":"joomla@vdm.io","subform_layouts":{"subform_layouts0":{"view_field":"default","layout":"repeatablejcbjfive"}},"editor":"none","allow_selfsigned_certificates":"0","set_browser_storage":"1","storage_time_to_live":"global","builder_gif_size":"480-272","compiler_plugin":["componentbuilderactionlogcompiler","componentbuilderfieldorderingcompiler","componentbuilderheaderscompiler","componentbuilderpowersautoloadercompiler","componentbuilderprivacycompiler"],"add_menu_prefix":"1","menu_prefix":"»","namespace_prefix":"JCB","minify":"0","language":"en-GB","percentagelanguageadd":"30","assets_table_fix":"2","compiler_field_builder_type":"2","field_name_builder":"2","type_name_builder":"2","avg_seconds_per_folder":"3.0","avg_seconds_per_file":"5.0","avg_seconds_per_line":"9.5","avg_lines_per_page":"56","avg_hourly_rate_usd":"75.0","debugging_factor":"0.25","planning_factor":"0.14","mapping_factor":"0.10","office_factor":"0.16","license_seat_annual_cost":"3000.0","assumed_developer_count":"1","workdays_per_year":"240","assumed_subscription_per_month":"100","market_multiplier_low":"0.9","market_multiplier_medium":"1.2","market_multiplier_high":"2.0","market_multiplier_revolutionary":"4.0","profit_margin_factor":"0.6","blueprint_replication_potential":"50","blueprint_risk_factor":"0.2","export_company":"Vast Development Method","export_owner":"Llewellyn van der Merwe","export_email":"joomla@vdm.io","export_website":"https://dev.vdm.io/","export_license":"GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html","export_copyright":"Copyright (C) 2015. All Rights Reserved","check_in":"-1 day","save_history":"1","history_limit":"10","add_jquery_framework":"1","uikit_load":"1","uikit_min":"","uikit_style":""}'
@@ -3293,7 +3293,7 @@ class Com_ComponentbuilderInstallerScript implements InstallerScriptInterface
echo '
-
Upgrade to Version 5.1.5 Was Successful! Let us know if anything is not working as expected.
';
+
Upgrade to Version 5.1.6 Was Successful! Let us know if anything is not working as expected.
';
// Add/Update component in the action logs extensions table.
$this->setActionLogsExtensions();
diff --git a/README.md b/README.md
index fd2303eff..fb6ce6322 100644
--- a/README.md
+++ b/README.md
@@ -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.5**) 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.6**) 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.
@@ -196,9 +196,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:** 24th March, 2026 · **Version:** 5.1.5
+* **Created:** 30th April, 2015 · **Last Build:** 21st May, 2026 · **Version:** 5.1.6
* **License:** GNU General Public License version 2 or later; see LICENSE.txt · **Copyright:** Copyright (C) 2015 Vast Development Method. All rights reserved.
-* **Lines:** 1347955 · **Fields:** 2129 · **Files:** 8837 · **Folders:** 810
+* **Lines:** 1349538 · **Fields:** 2129 · **Files:** 8838 · **Folders:** 810
> Generated with [JCB](https://www.joomlacomponentbuilder.com) — The Smartest Way to Build Joomla Extensions.
diff --git a/admin/README.txt b/admin/README.txt
index fd2303eff..fb6ce6322 100644
--- a/admin/README.txt
+++ b/admin/README.txt
@@ -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.5**) 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.6**) 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.
@@ -196,9 +196,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:** 24th March, 2026 · **Version:** 5.1.5
+* **Created:** 30th April, 2015 · **Last Build:** 21st May, 2026 · **Version:** 5.1.6
* **License:** GNU General Public License version 2 or later; see LICENSE.txt · **Copyright:** Copyright (C) 2015 Vast Development Method. All rights reserved.
-* **Lines:** 1347955 · **Fields:** 2129 · **Files:** 8837 · **Folders:** 810
+* **Lines:** 1349538 · **Fields:** 2129 · **Files:** 8838 · **Folders:** 810
> Generated with [JCB](https://www.joomlacomponentbuilder.com) — The Smartest Way to Build Joomla Extensions.
diff --git a/admin/access.xml b/admin/access.xml
index dd8eff7fc..6f6694ee9 100644
--- a/admin/access.xml
+++ b/admin/access.xml
@@ -115,6 +115,7 @@
+
diff --git a/admin/config.xml b/admin/config.xml
index fbc8aec25..65aadbcd9 100644
--- a/admin/config.xml
+++ b/admin/config.xml
@@ -73,6 +73,8 @@
+
+
+
@@ -127,6 +133,8 @@
COM_COMPONENTBUILDER_CONFIG_REPEATABLE_JOOMLA
+
diff --git a/admin/forms/admin_fields.xml b/admin/forms/admin_fields.xml
index 82e86cd8b..181e5b0a0 100644
--- a/admin/forms/admin_fields.xml
+++ b/admin/forms/admin_fields.xml
@@ -293,7 +293,7 @@
label="COM_COMPONENTBUILDER_ADMIN_FIELDS_ORDER_EDIT_LABEL"
default="0"
description="COM_COMPONENTBUILDER_ADMIN_FIELDS_ORDER_EDIT_DESCRIPTION"
- class="fieldSmall"
+ class="text_area"
first="0"
last="100"
step="1"
diff --git a/admin/forms/admin_fields_conditions.xml b/admin/forms/admin_fields_conditions.xml
index 1bffeb5b7..321f38e7d 100644
--- a/admin/forms/admin_fields_conditions.xml
+++ b/admin/forms/admin_fields_conditions.xml
@@ -115,7 +115,7 @@
type="subform"
name="addconditions"
label="COM_COMPONENTBUILDER_ADMIN_FIELDS_CONDITIONS_ADDCONDITIONS_LABEL"
- layout="repeatablejcb"
+ layout="joomla.form.field.subform.repeatable-table"
multiple="true"
description="COM_COMPONENTBUILDER_ADMIN_FIELDS_CONDITIONS_ADDCONDITIONS_DESCRIPTION"
default=""
diff --git a/admin/forms/admin_view.xml b/admin/forms/admin_view.xml
index b61acf5a8..847ae79dc 100644
--- a/admin/forms/admin_view.xml
+++ b/admin/forms/admin_view.xml
@@ -212,7 +212,6 @@
label="COM_COMPONENTBUILDER_ADMIN_VIEW_AJAX_INPUT_LABEL"
layout="joomla.form.field.subform.repeatable-table"
multiple="true"
- default=""
icon="list">
';
}
-
+
/**
- * Get Linked to Items
- *
- * @param string $view View that is being searched for
- * @param int $id ID
- * @param string|null $guid GUID
+ * Get linked to items.
*
- * @return array|null Found items
+ * @param string $view View that is being searched for.
+ * @param int $id ID.
+ * @param string|null $guid GUID.
+ *
+ * @return array|null Found items.
* @since 3.0.0
*/
protected function getLinkedTo(string $view, int $id, ?string $guid): ?array
{
- // reset bucket
+ if (
+ !isset($this->linkedKeys[$view])
+ || !UtilitiesArrayHelper::check($this->linkedKeys[$view])
+ )
+ {
+ return null;
+ }
+
$linked = [];
+ $db = $this->getDatabase();
- // start search
foreach ($this->linkedKeys[$view] as $search)
{
- // Create a new query object.
- $query = $this->_db->getQuery(true);
+ $items = $this->getLinkedSearchItems($db, $search);
- // get all history values
- $selection = array_keys($search['fields']);
- $selection[] = 'id';
- $query->select($selection);
- $query->from('#__componentbuilder_' . $search['table']);
- $this->_db->setQuery($query);
- $this->_db->execute();
- if ($this->_db->getNumRows())
+ if ($items === [])
{
- // load all items
- $items = $this->_db->loadObjectList();
+ continue;
+ }
- // search the items
- foreach ($items as $item)
+ foreach ($items as $item)
+ {
+ $match = $this->findLinkedItemMatch($item, $search, $id, $guid);
+
+ if ($match['found'])
{
- $found = false;
- $type_name = null;
- foreach ($search['fields'] as $key => $target)
- {
- if ('NAME' === $target)
- {
- $linked_name = $item->{$key};
- $linked_nameTable = $key;
- continue;
- }
- elseif ('TYPE' === $target)
- {
- $type_name = $item->{$key};
- $type_nameTable = $key;
- continue;
- }
- elseif (!$found)
- {
- if ('INT' === $target)
- {
- // check if ID match
- if ($item->{$key} == $id)
- {
- $found = true;
- }
- }
- elseif ('GUID' === $target)
- {
- // check if GUID match
- if ($this->linkedGuid($guid, $item->{$key}))
- {
- $found = true;
- }
- }
- else
- {
- // check if we have a json
- if (JsonHelper::check($item->{$key}))
- {
- $item->{$key} = json_decode($item->{$key}, true);
- }
- // if array
- if (UtilitiesArrayHelper::check($item->{$key}))
- {
- if ('ARRAY' === $target)
- {
- // check if ID match
- foreach ($item->{$key} as $_id)
- {
- if ($_id == $id || $this->linkedGuid($guid, $_id))
- {
- $found = true;
- }
- }
- }
- else
- {
- // check if this is a sub sub form target
- if (strpos($target, '.') !== false)
- {
- $_target = (array) explode('.', $target);
- // check that we have an array and get the size
- if (($_size = UtilitiesArrayHelper::check($_target)) !== false)
- {
- foreach ($item->{$key} as $row)
- {
- if ($_size == 2)
- {
- if (isset($row[$_target[0]]) && isset($row[$_target[0]][$_target[1]]) && ($row[$_target[0]][$_target[1]] == $id || $this->linkedGuid($guid, $row[$_target[0]][$_target[1]])))
- {
- $found = true;
- }
- }
- elseif ($_size == 3 && isset($row[$_target[0]]) && UtilitiesArrayHelper::check($row[$_target[0]]))
- {
- foreach ($row[$_target[0]] as $_row)
- {
- if (!$found && isset($_row[$_target[2]]) && ($_row[$_target[2]] == $id || $this->linkedGuid($guid, $_row[$_target[2]])))
- {
- $found = true;
- }
- }
- }
- }
- }
- }
- elseif (strpos($target, ':') !== false)
- {
- $_target = (array) explode(':', $target);
- // check that we have an array and get the size
- if (($_size = UtilitiesArrayHelper::check($_target)) == 2)
- {
- foreach ($item->{$key} as $field_name => $row)
- {
- if (!$found && $field_name === $_target[0])
- {
- foreach ($row as $_key => $_ids)
- {
- if (!$found && strpos($_key, $_target[1]) !== false && (in_array($id, $_ids) || $this->linkedGuid($guid, $_ids)))
- {
- $found = true;
- }
- }
- }
- }
- }
- // check that we have an array and get the size
- if (($_size = UtilitiesArrayHelper::check($_target)) == 3)
- {
- foreach ($item->{$key} as $field_name => $row)
- {
- if (!$found && $field_name === $_target[0])
- {
- foreach ($row as $_key => $_items)
- {
- if (!$found && strpos($_key, $_target[1]) !== false && is_array($_items) && count($_items) > 0)
- {
- foreach ($_items as $_item)
- {
- if (!$found && isset($_item[$_target[2]]) && ($id == $_item[$_target[2]] || $this->linkedGuid($guid, $_item[$_target[2]])))
- {
- $found = true;
- }
- }
- }
- }
- }
- }
- }
- }
- else
- {
- foreach ($item->{$key} as $row)
- {
- if (!$found && isset($row[$target]) && ($row[$target] == $id || $this->linkedGuid($guid, $row[$target])))
- {
- $found = true;
- }
- }
- }
- }
- }
- // if string (fields)
- if (!$found && 'xml' === $key && StringHelper::check($item->{$key})
- && strpos($item->{$key}, $target.'="') !== false)
- {
- // now get the fields between
- $_fields = GetHelper::between($item->{$key}, $target.'="', '"');
- // check the result
- if (StringHelper::check($_fields))
- {
- // get the ids of all the fields linked here
- $_fields = array_map('trim', (array) explode(',', $_fields));
- // check the result
- if (UtilitiesArrayHelper::check($_fields))
- {
- foreach ($_fields as $_field)
- {
- if ($_field == $id || $this->linkedGuid($guid, $_field))
- {
- $found = true;
- }
- }
- }
- }
- }
- }
- }
- }
- // check if found
- if ($found)
- {
- // build the name
- $edit = true;
- if ((is_numeric($linked_name) || GuidHelper::valid($linked_name)) && isset($search['linked_name']))
- {
- $key_field = GuidHelper::valid($linked_name) ? 'guid':'id';
- if (!$linked_name = GetHelper::var($linked_nameTable, $linked_name, $key_field, $search['linked_name']))
- {
- $linked_name = Text::_('COM_COMPONENTBUILDER_NO_FOUND');
- $edit = false;
- }
- }
-
- // build the local type
- if ((is_numeric($type_name) || GuidHelper::valid($type_name)) && isset($search['type_name']))
- {
- $key_field = GuidHelper::valid($type_name) ? 'guid':'id';
- if (!$type_name = GetHelper::var($type_nameTable, $type_name, $key_field, $search['type_name']))
- {
- $type_name = '';
- }
- else
- {
- $type_name = ' (' . $type_name . ') ';
- }
- }
- elseif (StringHelper::check($type_name) || is_numeric($type_name))
- {
- $type_name = ' (' . $type_name . ') ';
- }
-
- // set edit link
- $link = ($edit) ? ComponentbuilderHelper::getEditButton($item->id, $search['table'], $search['tables'], $this->ref) : '';
- // build the linked
- $linked[] = Text::_($search['linked']) . $type_name . ' - ' . $linked_name . ' ' . $link;
- }
+ $linked[] = $this->buildLinkedItemDisplay($item, $search, $match);
}
}
}
- // check if we found any
+
if (UtilitiesArrayHelper::check($linked))
{
return $linked;
}
+
return null;
}
/**
- * Check if we have a GUID match
- *
- * @param string|null $guid The active power guid
- * @param string|array $setGuid The linked power guid
+ * Load candidate items for a linked search definition.
*
- * @return bool true if match is found
- * @since 3.0.0
+ * @param object $db The database object.
+ * @param array $search The linked search configuration.
+ *
+ * @return array
+ * @since 6.1.6
*/
- protected function linkedGuid(?string $guid, $setGuid): bool
+ protected function getLinkedSearchItems($db, array $search): array
{
- // check if GUID is valid
- if ($guid !== null && GuidHelper::valid($guid))
+ $query = $db->getQuery(true);
+
+ $selection = array_keys($search['fields']);
+ $selection[] = 'id';
+
+ $query->select($selection);
+ $query->from('#__componentbuilder_' . $search['table']);
+
+ $db->setQuery($query);
+
+ $items = $db->loadObjectList();
+
+ if (!UtilitiesArrayHelper::check($items))
{
- if (is_string($setGuid) && GuidHelper::valid($setGuid) && $guid === $setGuid)
+ return [];
+ }
+
+ return $items;
+ }
+
+ /**
+ * Find a match for the current linked item.
+ *
+ * @param object $item The database row item.
+ * @param array $search The linked search configuration.
+ * @param int $id The target ID.
+ * @param string|null $guid The target GUID.
+ *
+ * @return array Match result with keys: found, linked_name, linked_name_table, type_name, type_name_table.
+ * @since 6.1.6
+ */
+ protected function findLinkedItemMatch(object $item, array $search, int $id, ?string $guid): array
+ {
+ $match = [
+ 'found' => false,
+ 'linked_name' => null,
+ 'linked_name_table' => null,
+ 'type_name' => null,
+ 'type_name_table' => null,
+ ];
+
+ foreach ($search['fields'] as $key => $target)
+ {
+ if ('NAME' === $target)
{
- return true;
+ $match['linked_name'] = property_exists($item, $key) ? $item->{$key} : null;
+ $match['linked_name_table'] = $key;
+ continue;
}
- elseif (is_array($setGuid) && in_array($guid, $setGuid))
+
+ if ('TYPE' === $target)
+ {
+ $match['type_name'] = property_exists($item, $key) ? $item->{$key} : null;
+ $match['type_name_table'] = $key;
+ continue;
+ }
+
+ if ($match['found'])
+ {
+ continue;
+ }
+
+ $value = property_exists($item, $key) ? $item->{$key} : null;
+
+ if ($value !== null && $this->fieldContainsLinkedReference($key, $target, $value, $id, $guid))
+ {
+ $match['found'] = true;
+ }
+ }
+
+ return $match;
+ }
+
+ /**
+ * Determine whether a field contains the linked reference.
+ *
+ * @param string $key The field name.
+ * @param string $target The field target definition.
+ * @param mixed $value The field value.
+ * @param int $id The target ID.
+ * @param string|null $guid The target GUID.
+ *
+ * @return bool
+ * @since 6.1.6
+ */
+ protected function fieldContainsLinkedReference(
+ string $key,
+ string $target,
+ $value,
+ int $id,
+ ?string $guid
+ ): bool
+ {
+ if ('INT' === $target)
+ {
+ return ($value == $id);
+ }
+
+ if ('GUID' === $target)
+ {
+ return $this->linkedGuid($guid, $value);
+ }
+
+ // Attempt JSON decode for complex field types.
+ $decodedValue = $this->decodeLinkedFieldValue($value);
+
+ // Check array-based structures first.
+ if (UtilitiesArrayHelper::check($decodedValue))
+ {
+ return $this->arrayFieldContainsLinkedReference($decodedValue, $target, $id, $guid);
+ }
+
+ // Fall through to XML check using the raw value (not decoded),
+ // since only non-array string values can be valid XML fields.
+ if ('xml' === $key)
+ {
+ return $this->xmlFieldContainsLinkedReference($value, $target, $id, $guid);
+ }
+
+ return false;
+ }
+
+ /**
+ * Decode a linked field value from JSON when possible.
+ *
+ * @param mixed $value The raw field value.
+ *
+ * @return mixed The decoded value, or the original value if decoding fails or is not applicable.
+ * @since 6.1.6
+ */
+ protected function decodeLinkedFieldValue($value)
+ {
+ if (JsonHelper::check($value))
+ {
+ $decoded = json_decode($value, true);
+
+ if (json_last_error() === JSON_ERROR_NONE)
+ {
+ return $decoded;
+ }
+ }
+
+ return $value;
+ }
+
+ /**
+ * Determine whether an array-based field contains the linked reference.
+ *
+ * @param array $value The decoded array value.
+ * @param string $target The field target definition.
+ * @param int $id The target ID.
+ * @param string|null $guid The target GUID.
+ *
+ * @return bool
+ * @since 6.1.6
+ */
+ protected function arrayFieldContainsLinkedReference(array $value, string $target, int $id, ?string $guid): bool
+ {
+ if ('ARRAY' === $target)
+ {
+ return $this->simpleArrayContainsLinkedReference($value, $id, $guid);
+ }
+
+ if (strpos($target, '.') !== false)
+ {
+ return $this->dotTargetContainsLinkedReference($value, $target, $id, $guid);
+ }
+
+ if (strpos($target, ':') !== false)
+ {
+ return $this->colonTargetContainsLinkedReference($value, $target, $id, $guid);
+ }
+
+ return $this->rowFieldContainsLinkedReference($value, $target, $id, $guid);
+ }
+
+ /**
+ * Check a simple array for a linked reference.
+ *
+ * @param array $values The array values.
+ * @param int $id The target ID.
+ * @param string|null $guid The target GUID.
+ *
+ * @return bool
+ * @since 6.1.6
+ */
+ protected function simpleArrayContainsLinkedReference(array $values, int $id, ?string $guid): bool
+ {
+ foreach ($values as $_id)
+ {
+ if ($_id == $id || $this->linkedGuid($guid, $_id))
{
return true;
}
}
+
return false;
}
+
+ /**
+ * Check a dot-notation target for a linked reference.
+ *
+ * Supports two-level (e.g. "subfield.key") and three-level (e.g. "subfield.ignored.key")
+ * dot-separated targets within subform row structures.
+ *
+ * @param array $value The array value.
+ * @param string $target The dot notation target.
+ * @param int $id The target ID.
+ * @param string|null $guid The target GUID.
+ *
+ * @return bool
+ * @since 6.1.6
+ */
+ protected function dotTargetContainsLinkedReference(array $value, string $target, int $id, ?string $guid): bool
+ {
+ $_target = explode('.', $target);
+ $_size = UtilitiesArrayHelper::check($_target);
+
+ if ($_size === false)
+ {
+ return false;
+ }
+
+ foreach ($value as $row)
+ {
+ if (!is_array($row))
+ {
+ continue;
+ }
+
+ if ($_size == 2 && $this->dotTwoLevelMatch($row, $_target, $id, $guid))
+ {
+ return true;
+ }
+
+ if ($_size == 3 && $this->dotThreeLevelMatch($row, $_target, $id, $guid))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Check a two-level dot target match within a single row.
+ *
+ * @param array $row The row data.
+ * @param array $_target The exploded target parts.
+ * @param int $id The target ID.
+ * @param string|null $guid The target GUID.
+ *
+ * @return bool
+ * @since 6.1.6
+ */
+ protected function dotTwoLevelMatch(array $row, array $_target, int $id, ?string $guid): bool
+ {
+ return (
+ isset($row[$_target[0]][$_target[1]])
+ && (
+ $row[$_target[0]][$_target[1]] == $id
+ || $this->linkedGuid($guid, $row[$_target[0]][$_target[1]])
+ )
+ );
+ }
+
+ /**
+ * Check a three-level dot target match within a single row.
+ *
+ * @param array $row The row data.
+ * @param array $_target The exploded target parts.
+ * @param int $id The target ID.
+ * @param string|null $guid The target GUID.
+ *
+ * @return bool
+ * @since 6.1.6
+ */
+ protected function dotThreeLevelMatch(array $row, array $_target, int $id, ?string $guid): bool
+ {
+ if (
+ !isset($row[$_target[0]])
+ || !UtilitiesArrayHelper::check($row[$_target[0]])
+ )
+ {
+ return false;
+ }
+
+ foreach ($row[$_target[0]] as $_row)
+ {
+ if (
+ is_array($_row)
+ && isset($_row[$_target[2]])
+ && (
+ $_row[$_target[2]] == $id
+ || $this->linkedGuid($guid, $_row[$_target[2]])
+ )
+ )
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Check a colon-notation target for a linked reference.
+ *
+ * @param array $value The array value.
+ * @param string $target The colon notation target.
+ * @param int $id The target ID.
+ * @param string|null $guid The target GUID.
+ *
+ * @return bool
+ * @since 6.1.6
+ */
+ protected function colonTargetContainsLinkedReference(array $value, string $target, int $id, ?string $guid): bool
+ {
+ $_target = explode(':', $target);
+ $_size = UtilitiesArrayHelper::check($_target);
+
+ if ($_size == 2)
+ {
+ return $this->colonTwoPartMatch($value, $_target, $id, $guid);
+ }
+
+ if ($_size == 3)
+ {
+ return $this->colonThreePartMatch($value, $_target, $id, $guid);
+ }
+
+ return false;
+ }
+
+ /**
+ * Check a two-part colon target for a linked reference.
+ *
+ * @param array $value The array value.
+ * @param array $_target The exploded target parts.
+ * @param int $id The target ID.
+ * @param string|null $guid The target GUID.
+ *
+ * @return bool
+ * @since 6.1.6
+ */
+ protected function colonTwoPartMatch(array $value, array $_target, int $id, ?string $guid): bool
+ {
+ foreach ($value as $field_name => $row)
+ {
+ if ($field_name !== $_target[0] || !is_array($row))
+ {
+ continue;
+ }
+
+ foreach ($row as $_key => $_ids)
+ {
+ if (
+ strpos($_key, $_target[1]) !== false
+ && is_array($_ids)
+ && (
+ in_array($id, $_ids)
+ || $this->linkedGuid($guid, $_ids)
+ )
+ )
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Check a three-part colon target for a linked reference.
+ *
+ * @param array $value The array value.
+ * @param array $_target The exploded target parts.
+ * @param int $id The target ID.
+ * @param string|null $guid The target GUID.
+ *
+ * @return bool
+ * @since 6.1.6
+ */
+ protected function colonThreePartMatch(array $value, array $_target, int $id, ?string $guid): bool
+ {
+ foreach ($value as $field_name => $row)
+ {
+ if ($field_name !== $_target[0] || !is_array($row))
+ {
+ continue;
+ }
+
+ foreach ($row as $_key => $_items)
+ {
+ if (
+ strpos($_key, $_target[1]) === false
+ || !is_array($_items)
+ || count($_items) === 0
+ )
+ {
+ continue;
+ }
+
+ foreach ($_items as $_item)
+ {
+ if (
+ is_array($_item)
+ && isset($_item[$_target[2]])
+ && (
+ $id == $_item[$_target[2]]
+ || $this->linkedGuid($guid, $_item[$_target[2]])
+ )
+ )
+ {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Check row arrays for a linked reference using a direct field name.
+ *
+ * @param array $value The array value.
+ * @param string $target The target field name.
+ * @param int $id The target ID.
+ * @param string|null $guid The target GUID.
+ *
+ * @return bool
+ * @since 6.1.6
+ */
+ protected function rowFieldContainsLinkedReference(array $value, string $target, int $id, ?string $guid): bool
+ {
+ foreach ($value as $row)
+ {
+ if (
+ is_array($row)
+ && isset($row[$target])
+ && (
+ $row[$target] == $id
+ || $this->linkedGuid($guid, $row[$target])
+ )
+ )
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Check whether an XML field contains the linked reference.
+ *
+ * @param mixed $value The raw field value (must be a string for XML parsing).
+ * @param string $target The target XML attribute.
+ * @param int $id The target ID.
+ * @param string|null $guid The target GUID.
+ *
+ * @return bool
+ * @since 6.1.6
+ */
+ protected function xmlFieldContainsLinkedReference($value, string $target, int $id, ?string $guid): bool
+ {
+ if (
+ !StringHelper::check($value)
+ || strpos($value, $target . '="') === false
+ )
+ {
+ return false;
+ }
+
+ $_fields = GetHelper::between($value, $target . '="', '"');
+
+ if (!StringHelper::check($_fields))
+ {
+ return false;
+ }
+
+ $_fields = array_map('trim', (array) explode(',', $_fields));
+
+ if (!UtilitiesArrayHelper::check($_fields))
+ {
+ return false;
+ }
+
+ foreach ($_fields as $_field)
+ {
+ if ($_field == $id || $this->linkedGuid($guid, $_field))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Build the display value for a found linked item.
+ *
+ * @param object $item The database row item.
+ * @param array $search The linked search configuration.
+ * @param array $match The match details.
+ *
+ * @return string
+ * @since 6.1.6
+ */
+ protected function buildLinkedItemDisplay(object $item, array $search, array $match): string
+ {
+ $nameResult = $this->resolveLinkedName($match, $search);
+ $typeName = $this->resolveTypeName($match, $search);
+ $link = $this->buildLinkedEditButton($item, $search, $nameResult['edit']);
+
+ return Text::_($search['linked']) . $typeName . ' - ' . $nameResult['name'] . ' ' . $link;
+ }
+
+ /**
+ * Resolve the linked item name.
+ *
+ * @param array $match The match details.
+ * @param array $search The linked search configuration.
+ *
+ * @return array With keys 'name' (string) and 'edit' (bool).
+ * @since 6.1.6
+ */
+ protected function resolveLinkedName(array $match, array $search): array
+ {
+ $linkedName = $match['linked_name'];
+ $linkedNameTable = $match['linked_name_table'];
+ $edit = true;
+
+ if (
+ (is_numeric($linkedName) || GuidHelper::valid($linkedName))
+ && isset($search['linked_name'])
+ )
+ {
+ $key_field = GuidHelper::valid($linkedName) ? 'guid' : 'id';
+
+ $resolved = GetHelper::var(
+ $linkedNameTable,
+ $linkedName,
+ $key_field,
+ $search['linked_name']
+ );
+
+ if (!$resolved)
+ {
+ return ['name' => Text::_('COM_COMPONENTBUILDER_NO_FOUND'), 'edit' => false];
+ }
+
+ $linkedName = $resolved;
+ }
+
+ return ['name' => (string) $linkedName, 'edit' => $edit];
+ }
+
+ /**
+ * Resolve the linked item type display.
+ *
+ * @param array $match The match details.
+ * @param array $search The linked search configuration.
+ *
+ * @return string The formatted type string (empty if none).
+ * @since 6.1.6
+ */
+ protected function resolveTypeName(array $match, array $search): string
+ {
+ $typeName = $match['type_name'];
+ $typeNameTable = $match['type_name_table'];
+
+ if (
+ (is_numeric($typeName) || GuidHelper::valid($typeName))
+ && isset($search['type_name'])
+ )
+ {
+ $key_field = GuidHelper::valid($typeName) ? 'guid' : 'id';
+
+ $resolved = GetHelper::var(
+ $typeNameTable,
+ $typeName,
+ $key_field,
+ $search['type_name']
+ );
+
+ if (!$resolved)
+ {
+ return '';
+ }
+
+ return ' (' . $resolved . ') ';
+ }
+
+ if (StringHelper::check($typeName) || is_numeric($typeName))
+ {
+ return ' (' . $typeName . ') ';
+ }
+
+ return '';
+ }
+
+ /**
+ * Build the edit button for a linked item.
+ *
+ * @param object $item The database row item.
+ * @param array $search The linked search configuration.
+ * @param bool $edit Whether edit access is enabled.
+ *
+ * @return string
+ * @since 6.1.6
+ */
+ protected function buildLinkedEditButton(object $item, array $search, bool $edit): string
+ {
+ if (!$edit)
+ {
+ return '';
+ }
+
+ return ComponentbuilderHelper::getEditButton(
+ $item->id,
+ $search['table'],
+ $search['tables'],
+ $this->ref
+ );
+ }
+
+ /**
+ * Check if we have a GUID match.
+ *
+ * @param string|null $guid The active power GUID.
+ * @param string|array $setGuid The linked power GUID.
+ *
+ * @return bool True if match is found.
+ * @since 3.0.0
+ */
+ protected function linkedGuid(?string $guid, $setGuid): bool
+ {
+ if ($guid === null || !GuidHelper::valid($guid))
+ {
+ return false;
+ }
+
+ if (
+ is_string($setGuid)
+ && GuidHelper::valid($setGuid)
+ && $guid === $setGuid
+ )
+ {
+ return true;
+ }
+
+ if (is_array($setGuid) && in_array($guid, $setGuid))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Converts Add Api Type values into human-readable strings.
+ *
+ * This function translates numeric values into add API types.
+ *
+ * @param string $header The header name (not used in this function but kept for consistency).
+ * @param mixed $value The numeric value representing the Api Type.
+ *
+ * @return string The human-readable label for the add API Type. Returns "not set" if the value is invalid.
+ * @since 6.1.6
+ */
+ protected function setAddApiType($header, $value)
+ {
+ switch ($value)
+ {
+ case 0:
+ return Text::_('COM_COMPONENTBUILDER_NONE');
+ break;
+ case 1:
+ return Text::_('COM_COMPONENTBUILDER_LIST');
+ break;
+ case 2:
+ return Text::_('COM_COMPONENTBUILDER_BOTH');
+ break;
+ case 3:
+ return Text::_('COM_COMPONENTBUILDER_ITEM');
+ break;
+ }
+ return Text::_('COM_COMPONENTBUILDER_NOT_SET');
+ }
/**
* The view persistence details
@@ -4375,9 +4854,9 @@ class AjaxModel extends ListModel
return false;
}
- $db = Factory::getDbo();
+ $db = $this->getDatabase();
$query = $db->getQuery(true)
- ->select($db->quoteName('a.id'))
+ ->select($db->quoteName('a.guid'))
->from($db->quoteName('#__componentbuilder_snippet', 'a'))
->where($db->quoteName('a.published') . ' = 1')
->where($db->quoteName('a.library') . ' IN ("' . implode('","', $validatedLibraries) . '")');
@@ -4457,7 +4936,7 @@ class AjaxModel extends ListModel
return false;
}
- $db = Factory::getDbo();
+ $db = $this->getDatabase();
$query = $db->getQuery(true);
$query
@@ -4651,6 +5130,7 @@ class AjaxModel extends ListModel
}
// Used in field
+
/**
* The current extras available
*
@@ -4718,7 +5198,12 @@ class AjaxModel extends ListModel
});
// get subform field object
- $extras = $this->buildFieldOptionsSubform($extraValues, $extraNameListOption, 'extraproperties', 'COM_COMPONENTBUILDER_EXTRA_PROPERTIES_LIKE_LISTCLASS_ESCAPE_DISPLAY_VALIDATEBR_SMALLHERE_YOU_CAN_SET_THE_EXTRA_PROPERTIES_FOR_THIS_FIELDSMALL');
+ $extras = $this->buildFieldOptionsSubform(
+ $extraValues,
+ $extraNameListOption,
+ 'extraproperties',
+ 'COM_COMPONENTBUILDER_EXTRA_PROPERTIES_LIKE_LISTCLASS_ESCAPE_DISPLAY_VALIDATEBR_SMALLHERE_YOU_CAN_SET_THE_EXTRA_PROPERTIES_FOR_THIS_FIELDSMALL'
+ );
// load the html
$field['subform'] = '
'. $properties->label . '
' . $properties->input . '
';
@@ -4727,7 +5212,7 @@ class AjaxModel extends ListModel
// check if we have PHP values
if (UtilitiesArrayHelper::check($field['php']))
{
- $field['textarea'] = array();
+ $field['textarea'] = [];
foreach($field['php'] as $name => $values)
{
$value = implode(PHP_EOL, $values['value']);
@@ -4791,7 +5276,7 @@ class AjaxModel extends ListModel
}
}
- // return only if extras founb
+ // return only if extras found
if (UtilitiesArrayHelper::check($values))
{
return $values;
@@ -4827,15 +5312,16 @@ class AjaxModel extends ListModel
// start building the name field XML
$textareaXML = new \SimpleXMLElement('');
// textarea attributes
- $textareaAttribute = array(
+ $textareaAttribute = [
'type' => 'textarea',
- 'name' => 'property_'.$name,
+ 'name' => 'property_' . $name,
'label' => $desc,
'rows' => (int) ($rows >= 3) ? $rows : $rows + 2,
'cols' => '15',
'class' => 'text_area span12',
'filter' => 'RAW',
- 'hint' => 'COM_COMPONENTBUILDER__ADD_YOUR_PHP_SCRIPT_HERE');
+ 'hint' => 'COM_COMPONENTBUILDER__ADD_YOUR_PHP_SCRIPT_HERE'
+ ];
// load the textarea attributes
FormHelper::attributes($textareaXML, $textareaAttribute);
@@ -4883,7 +5369,8 @@ class AjaxModel extends ListModel
'layout' => 'joomla.form.field.subform.repeatable-table',
'multiple' => 'true',
'icon' => 'list',
- 'max' => (UtilitiesArrayHelper::check($nameListOptions)) ? (int) count($nameListOptions) : 4];
+ 'max' => (UtilitiesArrayHelper::check($nameListOptions)) ? (int) count($nameListOptions) : 4
+ ];
// load the subform attributes
FormHelper::attributes($subformXML, $subformAttribute);
// now add the subform child form
@@ -4892,7 +5379,8 @@ class AjaxModel extends ListModel
$childFormAttribute = [
'hidden' => 'true',
'name' => 'list_properties',
- 'repeat' => 'true'];
+ 'repeat' => 'true'
+ ];
// load the child form attributes
FormHelper::attributes($childForm, $childFormAttribute);
@@ -4913,7 +5401,6 @@ class AjaxModel extends ListModel
$nameAttribute['description'] = 'COM_COMPONENTBUILDER_SELECTION';
$nameAttribute['multiple'] = 'false';
$nameAttribute['onchange'] = "getFieldPropertyDesc(this, '".$name."')";
- $nameAttribute['layout'] = 'joomla.form.field.list-fancy-select';
}
else
{
@@ -4943,7 +5430,8 @@ class AjaxModel extends ListModel
'cols' => '15',
'class' => 'text_area full-column-in-subform',
'filter' => 'STRING',
- 'hint' => 'COM_COMPONENTBUILDER_PROPERTY_VALUE'];
+ 'hint' => 'COM_COMPONENTBUILDER_PROPERTY_VALUE'
+ ];
// load the subform attributes
FormHelper::attributes($valueXML, $valueAttribute);
// now add the fields to the child form
@@ -4961,7 +5449,8 @@ class AjaxModel extends ListModel
'readonly' => 'true',
'class' => 'text_area full-column-in-subform',
'filter' => 'WORD',
- 'hint' => 'COM_COMPONENTBUILDER_SELECT_A_PROPERTY'];
+ 'hint' => 'COM_COMPONENTBUILDER_SELECT_A_PROPERTY'
+ ];
// load the desc attributes
FormHelper::attributes($descXML, $descAttribute);
// now add the fields to the child form
@@ -5007,12 +5496,12 @@ class AjaxModel extends ListModel
return false;
}
- // Get a db connection.
- $db = Factory::getDbo();
+ // Get a db connection.
+ $db = Factory::getContainer()->get(DatabaseInterface::class);
// Create a new query object.
$query = $db->getQuery(true);
- $query->select($db->quoteName(array('properties', 'short_description', 'description')));
+ $query->select($db->quoteName(['properties', 'short_description', 'description']));
$query->from($db->quoteName('#__componentbuilder_fieldtype'));
$query->where($db->quoteName($key) . ' = '. $db->quote($fieldtype));
@@ -5922,12 +6411,12 @@ class AjaxModel extends ListModel
{
$displayData = ['data' => [(object) $fileDefinition], 'entity' => $entity, 'target' => $target];
// change this to the layout of your custom importer columns display
- $display = LayoutHelper::render('translationimportercolumnsdisplay', $displayData);
+ $display = LayoutHelper::render('translationimportercolumnsdisplayjfive', $displayData);
}
else
{
// change this to the layout of your custom importer easy mapping
- return ['data' => LayoutHelper::render('translationimportereasymapping', []), 'state' => 0];
+ return ['data' => LayoutHelper::render('translationimportereasymappingjfive', []), 'state' => 0];
}
}
catch (\Exception $error)
diff --git a/admin/src/Model/FieldModel.php b/admin/src/Model/FieldModel.php
index 946095b96..3b8263625 100644
--- a/admin/src/Model/FieldModel.php
+++ b/admin/src/Model/FieldModel.php
@@ -835,6 +835,7 @@ class FieldModel extends AdminModel
$data['metadata'] = (string) $metadata;
}
+
// get the properties
$properties = $input->get('properties', null, 'ARRAY');
// get the extra properties
diff --git a/admin/src/Model/FieldsModel.php b/admin/src/Model/FieldsModel.php
index 8e436da38..bd667dd06 100644
--- a/admin/src/Model/FieldsModel.php
+++ b/admin/src/Model/FieldsModel.php
@@ -106,6 +106,7 @@ class FieldsModel extends ListModel
$this->app ??= Factory::getApplication();
}
+
/**
* Get the filter form - Override the parent method
*
@@ -133,25 +134,25 @@ class FieldsModel extends ListModel
array_push($this->filter_fields, 'extension');
// Create the "admin_view" filter
- $attributes = array(
+ $attributes = [
'name' => 'admin_view',
'type' => 'list',
'onchange' => 'this.form.submit();',
- );
- $options = array(
+ ];
+ $options = [
'' => '- ' . Text::_('COM_COMPONENTBUILDER_NO_ADMIN_VIEWS_FOUND') . ' -'
- );
+ ];
// check if we have admin views (and limit to an extension if it is set)
if (($admin_views = JCBFilterHelper::names('admin_view', $this->state->get("filter.extension"))) !== null)
{
- $options = array(
+ $options = [
'' => '- ' . Text::_('COM_COMPONENTBUILDER_SELECT_ADMIN_VIEW') . ' -'
- );
+ ];
// make sure we do not lose the key values in normal merge
$options = $options + $admin_views;
}
- $form->setField(JCBFormHelper::xml($attributes, $options),'filter');
+ $form->setField(JCBFormHelper::xml($attributes, $options), 'filter');
$form->setValue(
'admin_view',
'filter',
@@ -160,8 +161,7 @@ class FieldsModel extends ListModel
array_push($this->filter_fields, 'admin_view');
return $form;
- }
-
+ }
/**
* Method to auto-populate the model state.
@@ -431,6 +431,7 @@ class FieldsModel extends ListModel
$query->from($db->quoteName('#__componentbuilder_field', 'a'));
$query->join('LEFT', $db->quoteName('#__categories', 'c') . ' ON (' . $db->quoteName('a.catid') . ' = ' . $db->quoteName('c.id') . ')');
+
// Filtering "extension"
$filter_extension = $this->state->get("filter.extension");
$field_guids = [];
diff --git a/admin/src/Model/LibraryModel.php b/admin/src/Model/LibraryModel.php
index f253e6ceb..af8792de9 100644
--- a/admin/src/Model/LibraryModel.php
+++ b/admin/src/Model/LibraryModel.php
@@ -437,7 +437,10 @@ class LibraryModel extends AdminModel
{
$form->setValue('guid', null, GuidHelper::get());
}
-
+
+
+ // update the addconditions (sub form) layout
+ $form->setFieldAttribute('addconditions', 'layout', ComponentbuilderHelper::getSubformLayout('library', 'addconditions'));
return $form;
}
diff --git a/admin/src/Model/Site_viewModel.php b/admin/src/Model/Site_viewModel.php
index e391f7ce9..2c769690a 100644
--- a/admin/src/Model/Site_viewModel.php
+++ b/admin/src/Model/Site_viewModel.php
@@ -424,45 +424,6 @@ class Site_viewModel extends AdminModel
{
SessionHelper::set($this->vastDevMod . '__guid', $item->guid);
}
- }
-
- // update the fields
- $objectUpdate = new \stdClass();
- $objectUpdate->id = (int) $item->id;
- // check what type of custom_button array we have here (should be subform... but just incase)
- // This could happen due to huge data sets
- if (isset($item->custom_button) && isset($item->custom_button['name']))
- {
- $bucket = array();
- foreach($item->custom_button as $option => $values)
- {
- foreach($values as $nr => $value)
- {
- $bucket['custom_button'.$nr][$option] = $value;
- }
- }
- $item->custom_button = $bucket;
- $objectUpdate->custom_button = json_encode($bucket);
- }
- // check what type of ajax_input array we have here (should be subform... but just incase)
- // This could happen due to huge data sets
- if (isset($item->ajax_input) && isset($item->ajax_input['value_name']))
- {
- $bucket = array();
- foreach($item->ajax_input as $option => $values)
- {
- foreach($values as $nr => $value)
- {
- $bucket['ajax_input'.$nr][$option] = $value;
- }
- }
- $item->ajax_input = $bucket;
- $objectUpdate->ajax_input = json_encode($bucket);
- }
- // be sure to update the table if we found repeatable fields that are still not converted
- if (count((array) $objectUpdate) > 1)
- {
- $this->_db->updateObject('#__componentbuilder_site_view', $objectUpdate, 'id');
}
}
@@ -916,6 +877,7 @@ class Site_viewModel extends AdminModel
$data['metadata'] = (string) $metadata;
}
+
// always reset the snippets
$data['snippet'] = 0;
// if system name is empty create from name
diff --git a/admin/src/Model/Site_viewsModel.php b/admin/src/Model/Site_viewsModel.php
index 6344aaa14..1fa139b25 100644
--- a/admin/src/Model/Site_viewsModel.php
+++ b/admin/src/Model/Site_viewsModel.php
@@ -104,6 +104,7 @@ class Site_viewsModel extends ListModel
$this->app ??= Factory::getApplication();
}
+
/**
* Get the filter form - Override the parent method
*
@@ -357,6 +358,7 @@ class Site_viewsModel extends ListModel
// From the componentbuilder_item table
$query->from($db->quoteName('#__componentbuilder_site_view', 'a'));
+
// Filtering "joomla components"
$filter_joomla_component = $this->state->get("filter.joomla_component");
if ($filter_joomla_component !== null && !empty($filter_joomla_component))
diff --git a/admin/src/View/Compiler/HtmlView.php b/admin/src/View/Compiler/HtmlView.php
index edede29bd..0ab3bd899 100644
--- a/admin/src/View/Compiler/HtmlView.php
+++ b/admin/src/View/Compiler/HtmlView.php
@@ -688,6 +688,11 @@ class HtmlView extends BaseHtmlView
// add Clear tmp button.
ToolbarHelper::custom('compiler.clearTmp', 'purge custom-button-cleartmp', '', 'COM_COMPONENTBUILDER_CLEAR_TMP', false);
}
+ if ($this->canDo->get('compiler.health_check'))
+ {
+ // add Health Check button.
+ ToolbarHelper::custom('compiler.healthCheck', 'health custom-button-healthcheck', '', 'COM_COMPONENTBUILDER_HEALTH_CHECK', false);
+ }
// set help url for this view if found
$this->help_url = ComponentbuilderHelper::getHelpUrl('compiler');
if (StringHelper::check($this->help_url))
diff --git a/admin/src/View/Import_translations/HtmlView.php b/admin/src/View/Import_translations/HtmlView.php
index 057fa94cc..a12fc0b1a 100644
--- a/admin/src/View/Import_translations/HtmlView.php
+++ b/admin/src/View/Import_translations/HtmlView.php
@@ -245,10 +245,10 @@ class HtmlView extends BaseHtmlView
// these comments are here so the layouts will be linked and loaded for the ajax (don't remove it)
// change this to the layout of your custom importer columns display
- // LayoutHelper::render('translationimportercolumnsdisplay', [?]);
+ // LayoutHelper::render('translationimportercolumnsdisplayjfive', [?]);
// change this to the layout of your custom importer easy mapping
- // LayoutHelper::render('translationimportereasymapping', [?]);
+ // LayoutHelper::render('translationimportereasymappingjfive', [?]);
// add the libs for subform (since not adding it via xml but ajax)
$this->getDocument()
diff --git a/admin/tmpl/admin_custom_tabs/default.php b/admin/tmpl/admin_custom_tabs/default.php
index d7e3b822b..26fef0c0c 100644
--- a/admin/tmpl/admin_custom_tabs/default.php
+++ b/admin/tmpl/admin_custom_tabs/default.php
@@ -29,19 +29,31 @@ $tmpl = $this->input->get('tmpl');
$tmpl = $tmpl ? '&tmpl=' . $tmpl : '';
?>