Release of v5.0.3-alpha1

Add push option to powers area.
This commit is contained in:
Robot 2024-08-20 22:34:47 +02:00
parent 2fab51f611
commit 29fef2cbea
Signed by: Robot
GPG Key ID: 14DECD44E7E1BB95
29 changed files with 1480 additions and 106 deletions

View File

@ -1,3 +1,7 @@
# v5.0.3-alpha1
- Add push option to powers area
# v5.0.2
- Fix site view form missing classes in J4+

View File

@ -1514,7 +1514,7 @@ class Com_ComponentbuilderInstallerScript implements InstallerScriptInterface
// Fix the assets table rules column size.
$this->setDatabaseAssetsRulesFix(95040, "MEDIUMTEXT");
$this->setDatabaseAssetsRulesFix(95200, "MEDIUMTEXT");
// Install the global extension params.
$this->setExtensionsParams(
'{"autorName":"Llewellyn van der Merwe","autorEmail":"joomla@vdm.io","subform_layouts":"default","editor":"none","manage_jcb_package_directories":"2","set_browser_storage":"1","storage_time_to_live":"global","super_powers_documentation":"0","powers_repository":"0","super_powers_repositories":"0","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":"1","type_name_builder":"1","import_guid_only":"1","export_language_strings":"1","cronjob_backup_type":"1","cronjob_backup_server":"0","backup_package_name":"JCB_Backup_[YEAR]_[MONTH]_[DAY]","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":""}'
@ -3270,7 +3270,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.0.2 Was Successful! Let us know if anything is not working as expected.</h3></div>';
<h3>Upgrade to Version 5.0.3-alpha1 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

@ -3,13 +3,13 @@
This is a [Joomla 5.x](https://extensions.joomla.org/extension/component-builder/) component. [__See In Action__](https://www.youtube.com/watch?v=IQfsLYIeblk&list=PLQRGFI8XZ_wtGvPQZWBfDzzlERLQgpMRE&index=45)
![Component Builder image](https://git.vdm.dev/joomla/Component-Builder/raw/branch/master/admin/assets/images/jcb-banner.jpg "The Component Builder")
![Component Builder image](https://git.vdm.dev/joomla/Component-Builder/raw/branch/5.x/admin/assets/images/jcb-banner.jpg "The Component Builder")
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.
Whether you're a seasoned [Joomla](https://extensions.joomla.org/extension/component-builder/) developer, or have just started, Component Builder will save you lots of time and money. A real must have!
You can install it quite easily and with no limitations. On [gitea](https://git.vdm.dev/joomla/Component-Builder/tags) is the latest release (5.0.2) with **ALL** its features and **ALL** concepts totally open-source and free!
You can install it quite easily and with no limitations. On [gitea](https://git.vdm.dev/joomla/Component-Builder/tags) is the latest release (5.0.3-alpha1) with **ALL** its features and **ALL** concepts totally open-source and free!
> Watch Quick Build of a Hello World component in [JCB on Youtube](https://www.youtube.com/watch?v=IQfsLYIeblk&list=PLQRGFI8XZ_wtGvPQZWBfDzzlERLQgpMRE&index=45)
@ -144,13 +144,13 @@ TODO
+ *Author*: [Llewellyn van der Merwe](mailto:joomla@vdm.io)
+ *Name*: [Component Builder](https://git.vdm.dev/joomla/Component-Builder)
+ *First Build*: 30th April, 2015
+ *Last Build*: 16th August, 2024
+ *Version*: 5.0.2
+ *Last Build*: 20th August, 2024
+ *Version*: 5.0.3-alpha1
+ *Copyright*: Copyright (C) 2015 Vast Development Method. All rights reserved.
+ *License*: GNU General Public License version 2 or later; see LICENSE.txt
+ *Line count*: **826768**
+ *Field count*: **2100**
+ *File count*: **5753**
+ *Line count*: **830565**
+ *Field count*: **2104**
+ *File count*: **5760**
+ *Folder count*: **618**
> This **component** was build with a [Joomla](https://extensions.joomla.org/extension/component-builder/) [Automated Component Builder](https://www.joomlacomponentbuilder.com).

View File

@ -3,13 +3,13 @@
This is a [Joomla 5.x](https://extensions.joomla.org/extension/component-builder/) component. [__See In Action__](https://www.youtube.com/watch?v=IQfsLYIeblk&list=PLQRGFI8XZ_wtGvPQZWBfDzzlERLQgpMRE&index=45)
![Component Builder image](https://git.vdm.dev/joomla/Component-Builder/raw/branch/master/admin/assets/images/jcb-banner.jpg "The Component Builder")
![Component Builder image](https://git.vdm.dev/joomla/Component-Builder/raw/branch/5.x/admin/assets/images/jcb-banner.jpg "The Component Builder")
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.
Whether you're a seasoned [Joomla](https://extensions.joomla.org/extension/component-builder/) developer, or have just started, Component Builder will save you lots of time and money. A real must have!
You can install it quite easily and with no limitations. On [gitea](https://git.vdm.dev/joomla/Component-Builder/tags) is the latest release (5.0.2) with **ALL** its features and **ALL** concepts totally open-source and free!
You can install it quite easily and with no limitations. On [gitea](https://git.vdm.dev/joomla/Component-Builder/tags) is the latest release (5.0.3-alpha1) with **ALL** its features and **ALL** concepts totally open-source and free!
> Watch Quick Build of a Hello World component in [JCB on Youtube](https://www.youtube.com/watch?v=IQfsLYIeblk&list=PLQRGFI8XZ_wtGvPQZWBfDzzlERLQgpMRE&index=45)
@ -144,13 +144,13 @@ TODO
+ *Author*: [Llewellyn van der Merwe](mailto:joomla@vdm.io)
+ *Name*: [Component Builder](https://git.vdm.dev/joomla/Component-Builder)
+ *First Build*: 30th April, 2015
+ *Last Build*: 16th August, 2024
+ *Version*: 5.0.2
+ *Last Build*: 20th August, 2024
+ *Version*: 5.0.3-alpha1
+ *Copyright*: Copyright (C) 2015 Vast Development Method. All rights reserved.
+ *License*: GNU General Public License version 2 or later; see LICENSE.txt
+ *Line count*: **826768**
+ *Field count*: **2100**
+ *File count*: **5753**
+ *Line count*: **830565**
+ *Field count*: **2104**
+ *File count*: **5760**
+ *Folder count*: **618**
> This **component** was build with a [Joomla](https://extensions.joomla.org/extension/component-builder/) [Automated Component Builder](https://www.joomlacomponentbuilder.com).

View File

@ -465,6 +465,7 @@
<action name="placeholder.version" title="COM_COMPONENTBUILDER_PLACEHOLDERS_EDIT_VERSION" description="COM_COMPONENTBUILDER_PLACEHOLDERS_EDIT_VERSION_DESC" />
<action name="placeholder.submenu" title="COM_COMPONENTBUILDER_PLACEHOLDERS_SUBMENU" description="COM_COMPONENTBUILDER_PLACEHOLDERS_SUBMENU_DESC" />
<action name="power.init" title="COM_COMPONENTBUILDER_POWER_INIT_BUTTON_ACCESS" description="COM_COMPONENTBUILDER_POWER_INIT_BUTTON_ACCESS_DESC" />
<action name="power.push" title="COM_COMPONENTBUILDER_POWER_PUSH_BUTTON_ACCESS" description="COM_COMPONENTBUILDER_POWER_PUSH_BUTTON_ACCESS_DESC" />
<action name="power.reset" title="COM_COMPONENTBUILDER_POWER_RESET_BUTTON_ACCESS" description="COM_COMPONENTBUILDER_POWER_RESET_BUTTON_ACCESS_DESC" />
<action name="power.access" title="COM_COMPONENTBUILDER_POWERS_ACCESS" description="COM_COMPONENTBUILDER_POWERS_ACCESS_DESC" />
<action name="power.batch" title="COM_COMPONENTBUILDER_POWERS_BATCH_USE" description="COM_COMPONENTBUILDER_POWERS_BATCH_USE_DESC" />

View File

@ -426,7 +426,7 @@ class ###Component###Model###View### extends AdminModel
*
* @since 3.0
*/
protected function generateUnique($field,$value)
protected function generateUnique($field, $value)
{
// set field value unique
$table = $this->getTable();

View File

@ -210,6 +210,51 @@
validate="guid"
hint="COM_COMPONENTBUILDER_REPOSITORY_GUID_HINT"
/>
<!-- Addplaceholders Field. Type: Subform. (joomla) -->
<field
type="subform"
name="addplaceholders"
label="COM_COMPONENTBUILDER_REPOSITORY_ADDPLACEHOLDERS_LABEL"
layout="joomla.form.field.subform.repeatable-table"
multiple="true"
description="COM_COMPONENTBUILDER_REPOSITORY_ADDPLACEHOLDERS_DESCRIPTION"
default=""
icon="list">
<form hidden="true" name="list_addplaceholders_modal" repeat="true">
<!-- Target Field. Type: Text. (joomla) -->
<field
type="text"
name="target"
label="COM_COMPONENTBUILDER_REPOSITORY_TARGET_LABEL"
size="50"
maxlength="150"
description="COM_COMPONENTBUILDER_REPOSITORY_TARGET_DESCRIPTION"
class="text_area"
required="true"
filter="STRING"
message="COM_COMPONENTBUILDER_REPOSITORY_TARGET_MESSAGE"
hint="COM_COMPONENTBUILDER_REPOSITORY_TARGET_HINT"
autocomplete="off"
/>
<!-- Value Field. Type: Textarea. (joomla) -->
<field
type="textarea"
name="value"
label="COM_COMPONENTBUILDER_REPOSITORY_VALUE_LABEL"
rows="1"
cols="5"
default="default text"
description="COM_COMPONENTBUILDER_REPOSITORY_VALUE_DESCRIPTION"
message="COM_COMPONENTBUILDER_REPOSITORY_VALUE_MESSAGE"
class="text_area span12"
filter="RAW"
hint="COM_COMPONENTBUILDER_REPOSITORY_VALUE_HINT"
required="true"
/>
</form>
</field>
<!-- Placeholder_note Field. Type: Note. A None Database Field. (joomla) -->
<field type="note" name="placeholder_note" label="COM_COMPONENTBUILDER_REPOSITORY_PLACEHOLDER_NOTE_LABEL" description="COM_COMPONENTBUILDER_REPOSITORY_PLACEHOLDER_NOTE_DESCRIPTION" heading="h4" class="alert alert-info placeholder_note" />
<!-- Access_repo Field. Type: Radio. (joomla) -->
<field
type="radio"

View File

@ -7875,6 +7875,8 @@ COM_COMPONENTBUILDER_POWER_PROPERTY_LABEL="Properties"
COM_COMPONENTBUILDER_POWER_PROPERTY_SELECTION_DESCRIPTION="Select properties you want to use in your power."
COM_COMPONENTBUILDER_POWER_PROPERTY_SELECTION_LABEL="Property Selection"
COM_COMPONENTBUILDER_POWER_PUBLISHING="Publishing"
COM_COMPONENTBUILDER_POWER_PUSH_BUTTON_ACCESS="Power Push Button Access"
COM_COMPONENTBUILDER_POWER_PUSH_BUTTON_ACCESS_DESC="Allows the users in this group to access the push button."
COM_COMPONENTBUILDER_POWER_RESET_BUTTON_ACCESS="Power Reset Button Access"
COM_COMPONENTBUILDER_POWER_RESET_BUTTON_ACCESS_DESC="Allows the users in this group to access the reset button."
COM_COMPONENTBUILDER_POWER_SAVE_WARNING="Alias already existed so a number was added at the end. You can re-edit the Power to customise the alias."
@ -7973,6 +7975,8 @@ COM_COMPONENTBUILDER_REPOSITORIES_SUBMENU_DESC="Allows the users in this group t
COM_COMPONENTBUILDER_REPOSITORY="Repository"
COM_COMPONENTBUILDER_REPOSITORY_ACCESS_REPO_DESCRIPTION="Set the access options to this repository"
COM_COMPONENTBUILDER_REPOSITORY_ACCESS_REPO_LABEL="Access"
COM_COMPONENTBUILDER_REPOSITORY_ADDPLACEHOLDERS_DESCRIPTION="Set dnamic placeholders for this component."
COM_COMPONENTBUILDER_REPOSITORY_ADDPLACEHOLDERS_LABEL="Placeholders"
COM_COMPONENTBUILDER_REPOSITORY_BASE_DESCRIPTION="Enter Base URL"
COM_COMPONENTBUILDER_REPOSITORY_BASE_HINT="https://git.vdm.dev"
COM_COMPONENTBUILDER_REPOSITORY_BASE_LABEL="Base URL"
@ -8004,6 +8008,9 @@ COM_COMPONENTBUILDER_REPOSITORY_ORGANISATION_LABEL="Organisation"
COM_COMPONENTBUILDER_REPOSITORY_ORGANISATION_MESSAGE="Error! Please add organisation here."
COM_COMPONENTBUILDER_REPOSITORY_OVERRIDE="Override"
COM_COMPONENTBUILDER_REPOSITORY_PERMISSION="Permissions"
COM_COMPONENTBUILDER_REPOSITORY_PLACEHOLDERS="Placeholders"
COM_COMPONENTBUILDER_REPOSITORY_PLACEHOLDER_NOTE_DESCRIPTION="<p>When powers are connected to components, we utilize global placeholders. These placeholders are linked to the specific component, allowing for dynamic updates to any placeholders found within the powers. However, when we directly <b>push</b> powers from the powers area to this repository, we require <b>placeholders</b> that are specific to this repository.</p><p>You can add these repository-specific placeholders here. Please note that these placeholders are only utilized during direct pushes from the powers area and are not considered during the compilation of any component. Although you can use the same placeholders to ensure consistent outcomes, they technically have no direct relationship with the components.</p>"
COM_COMPONENTBUILDER_REPOSITORY_PLACEHOLDER_NOTE_LABEL="The Repository Placeholders"
COM_COMPONENTBUILDER_REPOSITORY_PUBLISHING="Publishing"
COM_COMPONENTBUILDER_REPOSITORY_READ_BRANCH_DESCRIPTION="The read branch to use in the API calls."
COM_COMPONENTBUILDER_REPOSITORY_READ_BRANCH_HINT="[master]"
@ -8020,7 +8027,9 @@ COM_COMPONENTBUILDER_REPOSITORY_SYSTEM_NAME_HINT="The System Name Here"
COM_COMPONENTBUILDER_REPOSITORY_SYSTEM_NAME_LABEL="System Name"
COM_COMPONENTBUILDER_REPOSITORY_SYSTEM_NAME_MESSAGE="Error! Please add some text here."
COM_COMPONENTBUILDER_REPOSITORY_TARGET_DESCRIPTION="The target content this repository holds."
COM_COMPONENTBUILDER_REPOSITORY_TARGET_HINT="coral"
COM_COMPONENTBUILDER_REPOSITORY_TARGET_LABEL="Target Content"
COM_COMPONENTBUILDER_REPOSITORY_TARGET_MESSAGE="Error! That target placeholder text already exist. Please add an unique placeholder target."
COM_COMPONENTBUILDER_REPOSITORY_TOKEN_DESCRIPTION="This token is used to access the api."
COM_COMPONENTBUILDER_REPOSITORY_TOKEN_HINT="Access Tokens"
COM_COMPONENTBUILDER_REPOSITORY_TOKEN_LABEL="Access Token"
@ -8030,6 +8039,10 @@ COM_COMPONENTBUILDER_REPOSITORY_USERNAME_DESCRIPTION="The username to use in the
COM_COMPONENTBUILDER_REPOSITORY_USERNAME_HINT="Username"
COM_COMPONENTBUILDER_REPOSITORY_USERNAME_LABEL="Username"
COM_COMPONENTBUILDER_REPOSITORY_USERNAME_MESSAGE="Error! Please add username here."
COM_COMPONENTBUILDER_REPOSITORY_VALUE_DESCRIPTION="Set the text you would like to set as the replacement value for the targeted placeholder."
COM_COMPONENTBUILDER_REPOSITORY_VALUE_HINT="membersmanager"
COM_COMPONENTBUILDER_REPOSITORY_VALUE_LABEL="Set String Value"
COM_COMPONENTBUILDER_REPOSITORY_VALUE_MESSAGE="Error! Please add some set target value here."
COM_COMPONENTBUILDER_REPOSITORY_VERSION_DESC="A count of the number of times this Repository has been revised."
COM_COMPONENTBUILDER_REPOSITORY_VERSION_LABEL="Version"
COM_COMPONENTBUILDER_REPOSITORY_WRITE_BRANCH_DESCRIPTION="The write branch to use in the API calls. This can be the same as the read branch."
@ -9039,6 +9052,7 @@ COM_COMPONENTBUILDER_THESE_JOOMLA_POWERS_HAVE_SUCCESSFULLY_BEEN_PUSHED="These Jo
COM_COMPONENTBUILDER_THESE_JOOMLA_POWERS_HAVE_SUCCESSFULLY_BEEN_RESET="These Joomla powers have successfully been reset."
COM_COMPONENTBUILDER_THESE_OPTIONS_ARE_NOT_AVAILABLE_TO_THE_FIELD_IF_BNONE_DBB_OPTION_IS_SELECTED="These options are not available to the field if <b>None DB</b> option is selected."
COM_COMPONENTBUILDER_THESE_OPTIONS_ARE_ONLY_AVAILABLE_TO_THE_FIELD_IF_BSHOW_IN_LIST_VIEWB_OPTION_IS_SELECTED="These options are only available to the field if <b>Show in list view</b> option is selected."
COM_COMPONENTBUILDER_THESE_POWERS_HAVE_SUCCESSFULLY_BEEN_PUSHED="These powers have successfully been pushed."
COM_COMPONENTBUILDER_THESE_POWERS_HAVE_SUCCESSFULLY_BEEN_RESET="These powers have successfully been reset."
COM_COMPONENTBUILDER_THE_ADMIN_CUSTOM_TABS="The admin custom tabs"
COM_COMPONENTBUILDER_THE_ADMIN_FIELDS="The admin fields"
@ -9103,12 +9117,15 @@ COM_COMPONENTBUILDER_THE_PACKAGE_KEY_IS_CODESCODE="The package key is: <code>%s<
COM_COMPONENTBUILDER_THE_PACKAGE_KEY_IS_S="The package key is: %s"
COM_COMPONENTBUILDER_THE_PLUGIN_FILES_FOLDERS="The plugin files & folders"
COM_COMPONENTBUILDER_THE_PLUGIN_UPDATES="The plugin updates"
COM_COMPONENTBUILDER_THE_POWER_HAS_SUCCESSFULLY_BEEN_PUSHED="The power has successfully been pushed."
COM_COMPONENTBUILDER_THE_POWER_HAS_SUCCESSFULLY_BEEN_RESET="The power has successfully been reset."
COM_COMPONENTBUILDER_THE_PRIVATE_KEY_FIELD_COULD_NOT_BE_LOADED_FOR_BSB_SERVER="The private key field could not be loaded for <b>%s</b> server!"
COM_COMPONENTBUILDER_THE_PRIVATE_KEY_FILE_COULD_NOT_BE_LOADEDFOUND_FOR_BSB_SERVER="The private key file could not be loaded/found for <b>%s</b> server!"
COM_COMPONENTBUILDER_THE_PRO_BOARD_IS_LOADING="The pro board is loading"
COM_COMPONENTBUILDER_THE_PUSH_OF_THESE_JOOMLA_POWERS_HAS_FAILED="The push of these Joomla powers has failed."
COM_COMPONENTBUILDER_THE_PUSH_OF_THESE_POWERS_HAS_FAILED="The push of these powers has failed."
COM_COMPONENTBUILDER_THE_PUSH_OF_THIS_JOOMLA_POWER_HAS_FAILED="The push of this Joomla power has failed."
COM_COMPONENTBUILDER_THE_PUSH_OF_THIS_POWER_HAS_FAILED="The push of this power has failed."
COM_COMPONENTBUILDER_THE_README_IS_LOADING="The readme is loading"
COM_COMPONENTBUILDER_THE_REPLACE_PROCESS_HAD_AN_ERROR_WITH_TABLE="The replace process had an error with table"
COM_COMPONENTBUILDER_THE_RESET_OF_THESE_JOOMLA_POWERS_HAS_FAILED="The reset of these Joomla powers has failed."
@ -9365,6 +9382,7 @@ COM_COMPONENTBUILDER_YOU_DO_NOT_HAVE_PERMISSION_TO_INSTALL_THE_COMPONENT="You do
COM_COMPONENTBUILDER_YOU_DO_NOT_HAVE_PERMISSION_TO_INSTALL_THE_MODULE="You do not have permission to install the module!"
COM_COMPONENTBUILDER_YOU_DO_NOT_HAVE_PERMISSION_TO_INSTALL_THE_PLUGIN="You do not have permission to install the plugin!"
COM_COMPONENTBUILDER_YOU_DO_NOT_HAVE_PERMISSION_TO_PUSH_THIS_JOOMLA_POWER="You do not have permission to push this Joomla power"
COM_COMPONENTBUILDER_YOU_DO_NOT_HAVE_PERMISSION_TO_PUSH_THIS_POWER="You do not have permission to push this power"
COM_COMPONENTBUILDER_YOU_DO_NOT_HAVE_PERMISSION_TO_RESET_THIS_JOOMLA_POWER="You do not have permission to reset this Joomla power"
COM_COMPONENTBUILDER_YOU_DO_NOT_HAVE_PERMISSION_TO_RESET_THIS_POWER="You do not have permission to reset this power"
COM_COMPONENTBUILDER_YOU_DO_NOT_HAVE_PERMISSION_TO_RUN_THE_GET_BOILERPLATE_MODULE="You do not have permission to run the get boilerplate module!"

View File

@ -981,6 +981,8 @@ COM_COMPONENTBUILDER_POWERS_SUBMENU="Powers Submenu"
COM_COMPONENTBUILDER_POWERS_SUBMENU_DESC="Allows the users in this group to submenu of power"
COM_COMPONENTBUILDER_POWER_INIT_BUTTON_ACCESS="Power Init Button Access"
COM_COMPONENTBUILDER_POWER_INIT_BUTTON_ACCESS_DESC="Allows the users in this group to access the init button."
COM_COMPONENTBUILDER_POWER_PUSH_BUTTON_ACCESS="Power Push Button Access"
COM_COMPONENTBUILDER_POWER_PUSH_BUTTON_ACCESS_DESC="Allows the users in this group to access the push button."
COM_COMPONENTBUILDER_POWER_RESET_BUTTON_ACCESS="Power Reset Button Access"
COM_COMPONENTBUILDER_POWER_RESET_BUTTON_ACCESS_DESC="Allows the users in this group to access the reset button."
COM_COMPONENTBUILDER_REPOSITORIES_ACCESS="Repositories Access"

View File

@ -0,0 +1,45 @@
<?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
*/
// No direct access to this file
defined('_JEXEC') or die;
// get the form
$form = $displayData->getForm();
// get the layout fields override method name (from layout path/ID)
$layout_path_array = explode('.', $this->getLayoutId());
// Since we cannot pass the layout and tab names as parameters to the model method
// this name combination of tab and layout in the method name is the only work around
// seeing that JCB uses those two values (tab_name & layout_name) as the layout file name.
// example of layout name: details_left.php
// example of method name: getFields_details_left()
$fields_tab_layout = 'fields_' . $layout_path_array[1];
// get the fields
$fields = $displayData->get($fields_tab_layout) ?: array(
'placeholder_note',
'addplaceholders'
);
$hiddenFields = $displayData->get('hidden_fields') ?: [];
?>
<?php if ($fields && count((array) $fields)) :?>
<div class="form-vertical">
<?php foreach($fields as $field): ?>
<?php if (in_array($field, $hiddenFields)) : ?>
<?php $form->setFieldAttribute($field, 'type', 'hidden'); ?>
<?php endif; ?>
<?php echo $form->renderField($field, null, null, array('class' => 'control-wrapper-' . $field)); ?>
<?php endforeach; ?>
</div>
<?php endif; ?>

View File

@ -1302,6 +1302,7 @@ CREATE TABLE IF NOT EXISTS `#__componentbuilder_repository` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`asset_id` INT(10) unsigned NOT NULL DEFAULT 0 COMMENT 'FK to the #__assets table.',
`access_repo` TINYINT(1) NOT NULL DEFAULT 0,
`addplaceholders` TEXT NOT NULL,
`base` VARCHAR(255) NOT NULL DEFAULT '',
`guid` VARCHAR(36) NOT NULL DEFAULT '',
`organisation` VARCHAR(255) NOT NULL DEFAULT '',
@ -2517,8 +2518,8 @@ INSERT INTO `#__componentbuilder_language` (`id`, `langtag`, `name`, `params`, `
INSERT INTO `#__componentbuilder_repository` (`id`, `system_name`, `organisation`, `repository`, `target`, `type`, `base`, `guid`, `read_branch`, `access`, `ordering`, `published`, `modified`, `created`, `params`) VALUES
(1, 'Super Powers', 'joomla', 'super-powers', 1, 1, 'https://git.vdm.dev', '1a1b6f3d-f16c-431a-b270-7a8a80cc7f56', 'master', 1, 1, 1, '2024-06-10 11:13:46', '2024-06-10 10:59:35', ''),
(2, 'JCB Compiler', 'joomla', 'jcb-compiler', 1, 1, 'https://git.vdm.dev', 'd12c7672-7b75-4a59-a5f3-c33f1a2f929b', 'master', 1, 2, 1, '2024-06-15 17:43:09', '2024-06-10 11:01:06', ''),
(3, 'JCB Packager', 'joomla', 'jcb-packager', 1, 1, 'https://git.vdm.dev', 'd8155014-5734-4fb5-9ccb-6bb33367ef78', 'master', 1, 3, 1, '2024-06-15 17:43:09', '2024-06-10 11:01:35', ''),
(2, 'JCB Compiler', 'joomla', 'jcb-compiler', 1, 1, 'https://git.vdm.dev', 'd12c7672-7b75-4a59-a5f3-c33f1a2f929b', 'master', 1, 2, 1, '2024-08-20 09:34:58', '2024-06-10 11:01:06', ''),
(3, 'JCB Packager', 'joomla', 'jcb-packager', 1, 1, 'https://git.vdm.dev', 'd8155014-5734-4fb5-9ccb-6bb33367ef78', 'master', 1, 3, 1, '2024-08-20 08:33:21', '2024-06-10 11:01:35', ''),
(4, 'PHP Seclib', 'joomla', 'phpseclib', 1, 1, 'https://git.vdm.dev', 'e63d916c-81c1-4a56-a73f-584174b36c87', 'master', 1, 4, 1, '2024-06-15 17:43:09', '2024-06-10 11:02:03', ''),
(5, 'JCB Search', 'joomla', 'search', 1, 1, 'https://git.vdm.dev', '6e57dc23-a17d-4937-bdbf-639ae1b8409f', 'master', 1, 5, 1, '2024-06-15 17:43:09', '2024-06-10 11:02:43', ''),
(6, 'Gitea', 'joomla', 'gitea', 1, 1, 'https://git.vdm.dev', 'a0cbfb8a-b427-48eb-b493-a02b34854019', 'master', 1, 6, 1, '2024-06-15 17:43:09', '2024-06-10 11:03:09', ''),

View File

@ -0,0 +1 @@

View File

@ -105,7 +105,7 @@ class Joomla_powerController extends FormController
$message = '<h1>' . Text::_('COM_COMPONENTBUILDER_NOT_SAVED') . '</h1>';
$message .= '<p>' . Text::_('COM_COMPONENTBUILDER_YOU_MUST_FIRST_SAVE_THE_JOOMLA_POWER_BEFORE_YOU_CAN_USE_THIS_FEATURE') . '</p>';
}
elseif($user->authorise('power.reset', 'com_componentbuilder'))
elseif($user->authorise('joomla_power.reset', 'com_componentbuilder'))
{
try {
if (JoomlaPowerFactory::_('Joomla.Power.Remote.Get')->reset([$guid]))
@ -180,7 +180,7 @@ class Joomla_powerController extends FormController
$message = '<h1>' . Text::_('COM_COMPONENTBUILDER_NOT_SAVED') . '</h1>';
$message .= '<p>' . Text::_('COM_COMPONENTBUILDER_YOU_MUST_FIRST_SAVE_THE_JOOMLA_POWER_BEFORE_YOU_CAN_USE_THIS_FEATURE') . '</p>';
}
elseif($user->authorise('power.push', 'com_componentbuilder'))
elseif($user->authorise('joomla_power.push', 'com_componentbuilder'))
{
try {
if (JoomlaPowerFactory::_('Joomla.Power.Remote.Set')->items([$guid]))

View File

@ -80,7 +80,7 @@ class Joomla_powersController extends AdminController
$status = 'error';
$success = false;
if($user->authorise('power.init', 'com_componentbuilder'))
if($user->authorise('joomla_power.init', 'com_componentbuilder'))
{
try {
if (JoomlaPowerFactory::_('Joomla.Power.Remote.Get')->init())
@ -153,7 +153,7 @@ class Joomla_powersController extends AdminController
// check if user has the right
$user = Factory::getUser();
if($user->authorise('power.reset', 'com_componentbuilder'))
if($user->authorise('joomla_power.reset', 'com_componentbuilder'))
{
$guids = GetHelper::vars('joomla_power', $pks, 'id', 'guid');
@ -232,7 +232,7 @@ class Joomla_powersController extends AdminController
// check if user has the right
$user = Factory::getUser();
if($user->authorise('power.push', 'com_componentbuilder'))
if($user->authorise('joomla_power.push', 'com_componentbuilder'))
{
$guids = GetHelper::vars('joomla_power', $pks, 'id', 'guid');

View File

@ -130,6 +130,81 @@ class PowerController extends FormController
$this->setRedirect($redirect_url, $message, $status);
return $success;
}
/**
* Pushes the specified power.
*
* This function performs several checks and operations:
* 1. It verifies the authenticity of the request to prevent request forgery.
* 2. It retrieves the item data posted by the user.
* 3. It checks whether the current user has the necessary permissions to push the power.
* 4. It validates the presence of the necessary item identifiers (ID and GUID).
* 5. If the user is authorized and the identifiers are valid, it attempts to push the specified power.
* 6. Depending on the result of the push operation, it sets the appropriate success or error message.
* 7. It redirects the user to a specified URL with the result message and status.
*
* @return bool True on successful push, false on failure.
*/
public function pushPowers()
{
// Check for request forgeries
Session::checkToken() or die(Text::_('JINVALID_TOKEN'));
// get Item posted
$item = $this->input->post->get('jform', array(), 'array');
// check if user has the right
$user = Factory::getUser();
// set default error message
$message = '<h1>' . Text::_('COM_COMPONENTBUILDER_PERMISSION_DENIED') . '</h1>';
$message .= '<p>' . Text::_('COM_COMPONENTBUILDER_YOU_DO_NOT_HAVE_PERMISSION_TO_PUSH_THIS_POWER') . '</p>';
$status = 'error';
$success = false;
// load the ID
$id = $item['id'] ?? null;
$guid = $item['guid'] ?? null;
// check if there is any selections
if ($id === null || $guid === null)
{
// set error message
$message = '<h1>' . Text::_('COM_COMPONENTBUILDER_NOT_SAVED') . '</h1>';
$message .= '<p>' . Text::_('COM_COMPONENTBUILDER_YOU_MUST_FIRST_SAVE_THE_POWER_BEFORE_YOU_CAN_USE_THIS_FEATURE') . '</p>';
}
elseif($user->authorise('power.push', 'com_componentbuilder'))
{
try {
if (PowerFactory::_('Power.Remote.Set')->items([$guid]))
{
// set success message
$message = '<h1>'.Text::_('COM_COMPONENTBUILDER_SUCCESS').'</h1>';
$message .= '<p>'.Text::_('COM_COMPONENTBUILDER_THE_POWER_HAS_SUCCESSFULLY_BEEN_PUSHED').'</p>';
$status = 'success';
$success = true;
}
else
{
$message = '<h1>' . Text::_('COM_COMPONENTBUILDER_PUSH_FAILED') . '</h1>';
$message .= '<p>' . Text::_('COM_COMPONENTBUILDER_THE_PUSH_OF_THIS_POWER_HAS_FAILED') . '</p>';
}
} catch (\Exception $e) {
$message = '<h1>' . Text::_('COM_COMPONENTBUILDER_PUSH_FAILED') . '</h1>';
$message .= '<p>' . \htmlspecialchars($e->getMessage()) . '</p>';
}
}
// set redirect
$redirect_url = Route::_(
'index.php?option=com_componentbuilder&view=power'
. $this->getRedirectToItemAppend($id), $success
);
$this->setRedirect($redirect_url, $message, $status);
return $success;
}

View File

@ -173,6 +173,85 @@ class PowersController extends AdminController
return $success;
}
// set redirect
$redirect_url = Route::_('index.php?option=com_componentbuilder&view=powers', false);
$this->setRedirect($redirect_url);
return $success;
}
/**
* Pushes the selected powers.
*
* This function performs several checks and operations:
* 1. It verifies the authenticity of the request to prevent request forgery.
* 2. It retrieves the IDs of the selected powers from the user input.
* 3. It sanitizes the input by converting the IDs to integers.
* 4. It checks whether any powers have been selected.
* 5. It checks whether the current user has the necessary permissions to push the selected powers.
* 6. If the user is authorized and powers are selected, it attempts to push the selected powers.
* 7. Depending on the result of the push operation, it sets the appropriate success or error message.
* 8. It redirects the user to a specified URL with the result message and status.
*
* @return bool True on successful push, false on failure.
*/
public function pushPowers()
{
// Check for request forgeries
Session::checkToken() or die(Text::_('JINVALID_TOKEN'));
// get IDS of the selected powers
$pks = $this->input->post->get('cid', [], 'array');
// Sanitize the input
ArrayHelper::toInteger($pks);
// check if there is any selections
if ($pks === [])
{
// set error message
$message = '<h1>'.Text::_('COM_COMPONENTBUILDER_NO_SELECTION_DETECTED').'</h1>';
$message .= '<p>'.Text::_('COM_COMPONENTBUILDER_PLEASE_FIRST_MAKE_A_SELECTION_FROM_THE_LIST').'</p>';
// set redirect
$redirect_url = Route::_('index.php?option=com_componentbuilder&view=powers', false);
$this->setRedirect($redirect_url, $message, 'error');
return false;
}
$status = 'error';
$success = false;
// check if user has the right
$user = Factory::getUser();
if($user->authorise('power.push', 'com_componentbuilder'))
{
$guids = GetHelper::vars('power', $pks, 'id', 'guid');
try {
if (PowerFactory::_('Power.Remote.Set')->items($guids))
{
// set success message
$message = '<h1>'.Text::_('COM_COMPONENTBUILDER_SUCCESS').'</h1>';
$message .= '<p>'.Text::_('COM_COMPONENTBUILDER_THESE_POWERS_HAVE_SUCCESSFULLY_BEEN_PUSHED').'</p>';
$status = 'success';
$success = true;
}
else
{
$message = '<h1>' . Text::_('COM_COMPONENTBUILDER_PUSH_FAILED') . '</h1>';
$message .= '<p>' . Text::_('COM_COMPONENTBUILDER_THE_PUSH_OF_THESE_POWERS_HAS_FAILED') . '</p>';
}
} catch (\Exception $e) {
$message = '<h1>' . Text::_('COM_COMPONENTBUILDER_PUSH_FAILED') . '</h1>';
$message .= '<p>' . \htmlspecialchars($e->getMessage()) . '</p>';
}
// set redirect
$redirect_url = Route::_('index.php?option=com_componentbuilder&view=powers', $success);
$this->setRedirect($redirect_url, $message, $status);
return $success;
}
// set redirect
$redirect_url = Route::_('index.php?option=com_componentbuilder&view=powers', false);
$this->setRedirect($redirect_url);

View File

@ -71,6 +71,12 @@ class RepositoryModel extends AdminModel
'target',
'access_repo'
)
),
'placeholders' => array(
'fullwidth' => array(
'placeholder_note',
'addplaceholders'
)
)
);
@ -154,6 +160,14 @@ class RepositoryModel extends AdminModel
$registry->loadString($item->metadata);
$item->metadata = $registry->toArray();
}
if (!empty($item->addplaceholders))
{
// Convert the addplaceholders field to an array.
$addplaceholders = new Registry;
$addplaceholders->loadString($item->addplaceholders);
$item->addplaceholders = $addplaceholders->toArray();
}
}
return $item;
@ -893,6 +907,19 @@ class RepositoryModel extends AdminModel
$data['guid'] = (string) GuidHelper::get();
}
// Set the addplaceholders items to data.
if (isset($data['addplaceholders']) && is_array($data['addplaceholders']))
{
$addplaceholders = new Registry;
$addplaceholders->loadArray($data['addplaceholders']);
$data['addplaceholders'] = (string) $addplaceholders;
}
elseif (!isset($data['addplaceholders']))
{
// Set the empty addplaceholders to data
$data['addplaceholders'] = '';
}
// Set the Params Items to data
if (isset($data['params']) && is_array($data['params']))
{

View File

@ -258,6 +258,11 @@ class HtmlView extends BaseHtmlView
// add Reset button.
ToolbarHelper::custom('power.resetPowers', 'joomla custom-button-resetpowers', '', 'COM_COMPONENTBUILDER_RESET', false);
}
if ($this->canDo->get('power.push'))
{
// add Push button.
ToolbarHelper::custom('power.pushPowers', 'share custom-button-pushpowers', '', 'COM_COMPONENTBUILDER_PUSH', false);
}
ToolbarHelper::cancel('power.cancel', 'JTOOLBAR_CLOSE');
}
}

View File

@ -208,6 +208,11 @@ class HtmlView extends BaseHtmlView
// add Reset button.
ToolbarHelper::custom('powers.resetPowers', 'joomla custom-button-resetpowers', '', 'COM_COMPONENTBUILDER_RESET', false);
}
if ($this->user->authorise('power.push', 'com_componentbuilder'))
{
// add Push button.
ToolbarHelper::custom('powers.pushPowers', 'share custom-button-pushpowers', '', 'COM_COMPONENTBUILDER_PUSH', false);
}
// set help url for this view if found
$this->help_url = ComponentbuilderHelper::getHelpUrl('powers');

View File

@ -59,6 +59,16 @@ defined('_JEXEC') or die;
</div>
<?php echo Html::_('uitab.endTab'); ?>
<?php echo Html::_('uitab.addTab', 'repositoryTab', 'placeholders', Text::_('COM_COMPONENTBUILDER_REPOSITORY_PLACEHOLDERS', true)); ?>
<div class="row">
</div>
<div class="row">
<div class="col-md-12">
<?php echo LayoutHelper::render('repository.placeholders_fullwidth', $this); ?>
</div>
</div>
<?php echo Html::_('uitab.endTab'); ?>
<?php $this->ignore_fieldsets = array('details','metadata','vdmmetadata','accesscontrol'); ?>
<?php $this->tab_name = 'repositoryTab'; ?>
<?php echo LayoutHelper::render('joomla.edit.params', $this); ?>

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>16th August, 2024</creationDate>
<creationDate>20th August, 2024</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.0.2</version>
<version>5.0.3-alpha1</version>
<description><![CDATA[
<h1>Component Builder (v.5.0.2)</h1>
<h1>Component Builder (v.5.0.3-alpha1)</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

@ -75,7 +75,7 @@
<name>Component Builder</name>
<description>Builds Complex Joomla Components</description>
<element>pkg_component_builder</element>
<type>component</type>
<type>package</type>
<client>site</client>
<version>5.0.2</version>
<infourl title="Component Builder!">https://dev.vdm.io</infourl>
@ -89,4 +89,22 @@
<maintainerurl>https://dev.vdm.io</maintainerurl>
<targetplatform name="joomla" version="5\.[012]"/>
</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.0.3-alpha1</version>
<infourl title="Component Builder!">https://dev.vdm.io</infourl>
<downloads>
<downloadurl type="full" format="zip">https://git.vdm.dev/api/v1/repos/joomla/pkg-component-builder/archive/v5.0.3-alpha1.zip</downloadurl>
</downloads>
<tags>
<tag>alpha</tag>
</tags>
<maintainer>Llewellyn van der Merwe</maintainer>
<maintainerurl>https://dev.vdm.io</maintainerurl>
<targetplatform name="joomla" version="5\.[012]"/>
</update>
</updates>

View File

@ -17,6 +17,7 @@ use VDM\Joomla\Interfaces\Data\ItemsInterface as Items;
use VDM\Joomla\Interfaces\Readme\ItemInterface as ItemReadme;
use VDM\Joomla\Interfaces\Readme\MainInterface as MainReadme;
use VDM\Joomla\Interfaces\Git\Repository\ContentsInterface as Git;
use VDM\Joomla\Utilities\ObjectHelper;
use VDM\Joomla\Interfaces\Remote\SetInterface;
@ -147,6 +148,27 @@ abstract class Set implements SetInterface
*/
protected string $index_settings_path = 'index.json';
/**
* Core Placeholders
*
* @var array
* @since 5.0.3
*/
protected array $placeholders = [
'[['.'[NamespacePrefix]]]' => 'VDM',
'[['.'[ComponentNamespace]]]' => 'Componentbuilder',
'[['.'[Component]]]' => 'Componentbuilder',
'[['.'[component]]]' => 'componentbuilder'
];
/**
* Repo Placeholders
*
* @var array
* @since 5.0.3
*/
protected array $repoPlaceholders = [];
/**
* Constructor.
*
@ -499,55 +521,10 @@ abstract class Set implements SetInterface
* @since 3.2.2
*/
protected function getLocalItems(array $guids): ?array
{
$items = $this->fetchLocalItems($guids);
if ($items === null)
{
return null;
}
return $this->mapItems($items);
}
/**
* Fetch items from the database
*
* @param array $guids The global unique ids of the items
*
* @return array|null
* @since 3.2.2
*/
protected function fetchLocalItems(array $guids): ?array
{
return $this->items->table($this->getTable())->get($guids);
}
/**
* Map items to their properties
*
* @param array $items The items fetched from the database
*
* @return array
* @since 3.2.2
*/
protected function mapItems(array $items): array
{
$bucket = [];
foreach ($items as $item)
{
if (!isset($item->guid))
{
continue;
}
$bucket[$item->guid] = $this->mapItem($item);
}
return $bucket;
}
/**
* Map a single item to its properties
*
@ -562,7 +539,15 @@ abstract class Set implements SetInterface
foreach ($this->map as $key => $map)
{
$power[$key] = $item->{$map} ?? null;
$methodName = "mapItemValue_{$key}";
if (method_exists($this, $methodName))
{
$this->{$methodName}($item, $power);
}
else
{
$power[$key] = $item->{$map} ?? null;
}
}
return (object) $power;
@ -586,11 +571,15 @@ abstract class Set implements SetInterface
$index_item = null;
foreach ($this->repos as $key => $repo)
{
if (empty($repo->write_branch) || $repo->write_branch === 'default')
if (empty($repo->write_branch) || $repo->write_branch === 'default' || !$this->targetRepo($item, $repo))
{
continue;
}
$item = $this->mapItem($item);
$this->setRepoPlaceholders($repo);
$this->git->load_($repo->base ?? null, $repo->token ?? null);
if (($existing = $this->grep->get($item->guid, ['remote'], $repo)) !== null)
@ -622,6 +611,43 @@ abstract class Set implements SetInterface
}
}
/**
* Set the Repo Placeholders
*
* @param object $repo The repo
*
* @return void
* @since 5.0.3
*/
protected function setRepoPlaceholders(object $repo): void
{
$this->repoPlaceholders = $this->placeholders;
if (!empty($repo->placeholders) && is_array($repo->placeholders))
{
foreach ($repo->placeholders as $key => $value)
{
$this->repoPlaceholders[$key] = $value;
}
}
}
/**
* Update Placeholders in String
*
* @param string $string The value to update
*
* @return string
* @since 5.0.3
*/
protected function updatePlaceholders(string $string): string
{
return str_replace(
array_keys($this->repoPlaceholders),
array_values($this->repoPlaceholders),
$string
);
}
/**
* Get index values
*
@ -669,25 +695,36 @@ abstract class Set implements SetInterface
}
/**
* Checks if two objects are equal by comparing their JSON representations.
* check that we have a target repo of this item
*
* This method converts both input objects to JSON strings and compares these strings.
* If the JSON strings are identical, the objects are considered equal.
* @param object $item The item
* @param object $repo The current repo
*
* @param object $obj1 The first object to compare.
* @param object $obj2 The second object to compare.
*
* @return bool True if the objects are equal, false otherwise.
* @since 3.2.2
* @return bool
* @since 5.0.3
*/
protected function areObjectsEqual(object $obj1, object $obj2): bool
protected function targetRepo(object $item, object $repo): bool
{
// Convert both objects to JSON strings
$json1 = json_encode($obj1);
$json2 = json_encode($obj2);
return true; // for more control in children classes
}
// Compare the JSON strings
return $json1 === $json2;
/**
* Checks if two objects are equal by comparing their properties and values.
*
* This method converts both input objects to associative arrays, sorts the arrays by keys,
* and compares these sorted arrays.
*
* If the arrays are identical, the objects are considered equal.
*
* @param object|null $obj1 The first object to compare.
* @param object|null $obj2 The second object to compare.
*
* @return bool True if the objects are equal, false otherwise.
* @since 5.0.2
*/
protected function areObjectsEqual(?object $obj1, ?object $obj2): bool
{
return ObjectHelper::equal($obj1, $obj2); // basic comparison
}
/**

View File

@ -143,6 +143,60 @@ final class Grep extends ExtendingGrep implements GrepInterface
// set the git details in params
$power->main_class_code = $code;
}
// set the git details in params
$path_guid = $path->guid ?? null;
if ($path_guid !== null)
{
// get the Settings meta
if (($meta = $this->contents->metadata($path->organisation, $path->repository, $path->index->{$guid}->settings, $branch)) !== null &&
isset($meta->sha))
{
if (isset($power->params) && is_object($power->params) &&
isset($power->params->source) && is_array($power->params->source))
{
$power->params->source[$path_guid . '-settings'] = $meta->sha;
}
else
{
$power->params = (object) [
'source' => [$path_guid . '-settings' => $meta->sha]
];
}
}
// get the power meta
if (($meta = $this->contents->metadata($path->organisation, $path->repository, $path->index->{$guid}->power, $branch)) !== null &&
isset($meta->sha))
{
if (isset($power->params) && is_object($power->params) &&
isset($power->params->source) && is_array($power->params->source))
{
$power->params->source[$path_guid . '-power'] = $meta->sha;
}
else
{
$power->params = (object) [
'source' => [$path_guid . '-power' => $meta->sha]
];
}
}
// get the README meta
if (($meta = $this->contents->metadata($path->organisation, $path->repository, $path->index->{$guid}->path . '/README.md', $branch)) !== null &&
isset($meta->sha))
{
if (isset($power->params) && is_object($power->params) &&
isset($power->params->source) && is_array($power->params->source))
{
$power->params->source[$path_guid . '-readme'] = $meta->sha;
}
else
{
$power->params = (object) [
'source' => [$path_guid . '-readme' => $meta->sha]
];
}
}
}
}
// reset back to the global base and token

View File

@ -0,0 +1,794 @@
<?php
/**
* @package Joomla.Component.Builder
*
* @created 4th September, 2022
* @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\Joomla\Componentbuilder\Power\Remote;
use VDM\Joomla\Interfaces\GrepInterface as Grep;
use VDM\Joomla\Interfaces\Data\ItemsInterface as Items;
use VDM\Joomla\Interfaces\Readme\ItemInterface as ItemReadme;
use VDM\Joomla\Interfaces\Readme\MainInterface as MainReadme;
use VDM\Joomla\Interfaces\Git\Repository\ContentsInterface as Git;
use VDM\Joomla\Componentbuilder\Power\Parser;
use VDM\Joomla\Utilities\String\NamespaceHelper;
use VDM\Joomla\Utilities\ArrayHelper;
use VDM\Joomla\Utilities\StringHelper;
use VDM\Joomla\Utilities\String\ClassfunctionHelper;
use VDM\Joomla\Utilities\GuidHelper;
use VDM\Joomla\Interfaces\Remote\SetInterface;
use VDM\Joomla\Abstraction\Remote\Set as ExtendingSet;
/**
* Set Power based on global unique ids to remote repository
*
* @since 5.0.2
*/
final class Set extends ExtendingSet implements SetInterface
{
/**
* Table Name
*
* @var string
* @since 5.0.3
*/
protected string $table = 'power';
/**
* Area Name
*
* @var string
* @since 5.0.3
*/
protected string $area = 'Super Power';
/**
* Prefix Key
*
* @var string
* @since 5.0.3
*/
protected string $prefix_key = 'Super---';
/**
* The item map
*
* @var array
* @since 5.0.3
*/
protected array $map = [
'add_head' => 'add_head',
'description' => 'description',
'extends' => 'extends',
'extendsinterfaces' => 'extendsinterfaces',
'guid' => 'guid',
'head' => 'head',
'use_selection' => 'use_selection',
'implements' => 'implements',
'load_selection' => 'load_selection',
'name' => 'name',
'power_version' => 'power_version',
'system_name' => 'system_name',
'type' => 'type',
'namespace' => 'namespace',
'composer' => 'composer',
'add_licensing_template' => 'add_licensing_template',
'licensing_template' => 'licensing_template',
'main_class_code' => 'main_class_code'
];
/**
* The index map
*
* @var array
* @since 5.0.3
*/
protected array $index_map = [
'name' => 'index_map_IndexName',
'type' => 'index_map_TypeName',
'namespace' => 'index_map_NameSpace',
'code' => 'index_map_CodePath',
'power' => 'index_map_PowerPath',
'settings' => 'index_map_IndexSettingsPath',
'path' => 'index_map_IndexPath',
'spk' => 'index_map_IndexKey',
'guid' => 'index_map_IndexGUID'
];
/**
* The item settings file path
*
* @var string
* @since 5.0.3
*/
protected string $settings_path = 'settings.json';
/**
* The index settings file path
*
* @var string
* @since 5.0.3
*/
protected string $index_settings_path = 'super-powers.json';
/**
* The Parser Class.
*
* @var Parser|null
* @since 5.0.2
*/
protected ?Parser $parser;
/**
* Constructor.
*
* @param array $repos The active repos
* @param Grep $grep The Grep Class.
* @param Items $items The Items Class.
* @param ItemReadme $itemReadme The Item Readme Class.
* @param MainReadme $mainReadme The Main Readme Class.
* @param Git $git The Contents Class.
* @param string|null $table The table name.
* @param string|null $settingsPath The settings path.
* @param string|null $settingsIndexPath The index settings path.
* @param Parser|null $parser The Parser Class.
*
* @since 3.2.2
*/
public function __construct(array $repos, Grep $grep, Items $items,
ItemReadme $itemReadme, MainReadme $mainReadme, Git $git,
?string $table = null, ?string $settingsPath = null,
?string $settingsIndexPath = null, ?Parser $parser = null)
{
parent::__construct($repos, $grep, $items, $itemReadme, $mainReadme,
$git, $table, $settingsPath, $settingsIndexPath);
$this->parser = $parser;
}
/**
* Map a single item value (extends)
*
* @param object $item The item to be mapped
* @param array $item The bucket to to place new values
* @param string $map The item map to be mapped
*
* @return void
* @since 5.0.2
*/
protected function mapItemValue_extends(object &$item, array &$power): void
{
if ($item->type !== 'interface')
{
$value = $item->extends ?? '';
$extends_custom = $item->extends_custom ?? null;
if ($value == -1 && $extends_custom !== null)
{
$power['extends_name'] = ClassfunctionHelper::safe(
$this->updatePlaceholders((string) $extends_custom)
);
$power['extends_custom'] = $extends_custom;
$power['extends'] = -1;
}
elseif (GuidHelper::valid($value))
{
$name = GuidHelper::item($value, 'power', 'a.name', 'componentbuilder');
if ($name !== null)
{
$power['extends_name'] = ClassfunctionHelper::safe(
$this->updatePlaceholders($name)
);
}
}
else
{
$power['extends'] = '';
}
// always rest these for normal classes
$power['extendsinterfaces'] = null;
$power['extendsinterfaces_custom'] = '';
}
}
/**
* Map a single item value (extendsinterfaces)
*
* @param object $item The item to be mapped
* @param array $item The bucket to to place new values
* @param string $map The item map to be mapped
*
* @return void
* @since 5.0.2
*/
protected function mapItemValue_extendsinterfaces(object &$item, array &$power): void
{
if ($item->type === 'interface')
{
$values = $item->extendsinterfaces ?? null;
if (!empty($values))
{
$values = (array) $values;
$extends_names = [];
$extendsinterfaces_custom = $item->extendsinterfaces_custom ?? null;
foreach ($values as $value)
{
if ($value == -1 && StringHelper::check($extendsinterfaces_custom))
{
$extends_names[] = ClassfunctionHelper::safe(
$this->updatePlaceholders($extendsinterfaces_custom)
);
$power['extendsinterfaces_custom'] = $extendsinterfaces_custom;
$extendsinterfaces_custom = null;
}
elseif (GuidHelper::valid($value))
{
$name = GuidHelper::item($value, 'power', 'a.name', 'componentbuilder');
if ($name !== null)
{
$extends_names[] = ClassfunctionHelper::safe(
$this->updatePlaceholders($name)
);
}
}
}
if ($extends_names !== [])
{
$power['extendsinterfaces'] = array_values($values);
$power['extends_name'] = implode(', ', $extends_names);
}
}
else
{
$power['extendsinterfaces'] = null;
$power['extendsinterfaces_custom'] = '';
}
// always rest these for interfaces
$power['extends'] = '';
$power['extends_custom'] = '';
}
}
/**
* Map a single item value (use_selection)
*
* @param object $item The item to be mapped
* @param array $item The bucket to to place new values
* @param string $map The item map to be mapped
*
* @return void
* @since 5.0.2
*/
protected function mapItemValue_use_selection(object &$item, array &$power): void
{
$value = $item->use_selection ?? null;
if (!empty($value))
{
$value = (array) $value;
$power['use_selection'] = $value;
}
else
{
$power['use_selection'] = null;
}
}
/**
* Map a single item value (load_selection)
*
* @param object $item The item to be mapped
* @param array $item The bucket to to place new values
* @param string $map The item map to be mapped
*
* @return void
* @since 5.0.2
*/
protected function mapItemValue_load_selection(object &$item, array &$power): void
{
$value = $item->load_selection ?? null;
if (!empty($value))
{
$value = (array) $value;
$power['load_selection'] = $value;
}
else
{
$power['load_selection'] = null;
}
}
/**
* Map a single item value (composer)
*
* @param object $item The item to be mapped
* @param array $item The bucket to to place new values
* @param string $map The item map to be mapped
*
* @return void
* @since 5.0.2
*/
protected function mapItemValue_composer(object &$item, array &$power): void
{
$value = $item->composer ?? null;
if (!empty($value))
{
$value = (array) $value;
$power['composer'] = array_values($value);
}
else
{
$power['composer'] = '';
}
}
/**
* Map a single item value (implements)
*
* @param object $item The item to be mapped
* @param array $item The bucket to to place new values
* @param string $map The item map to be mapped
*
* @return void
* @since 5.0.2
*/
protected function mapItemValue_implements(object &$item, array &$power): void
{
$values = $item->implements ?? '';
if (!empty($values))
{
$values = (array) $values;
$implement_names = [];
$implements_custom = $item->implements_custom ?? null;
foreach ($values as $value)
{
if ($value == -1 && StringHelper::check($implements_custom))
{
$implement_names[] = ClassfunctionHelper::safe(
$this->updatePlaceholders($implements_custom)
);
$implements_custom = null;
}
elseif (GuidHelper::valid($value))
{
$name = GuidHelper::item($value, 'power', 'a.name', 'componentbuilder');
if ($name !== null)
{
$implement_names[] = ClassfunctionHelper::safe(
$this->updatePlaceholders($name)
);
}
}
}
if ($implement_names !== [])
{
$power['implements'] = array_values($values);
$power['implement_names'] = $implement_names;
}
else
{
$power['implements'] = null;
}
}
}
/**
* update an existing item (if changed)
*
* @param object $item
* @param object $existing
* @param object $repo
*
* @return bool
* @since 5.0.3
*/
protected function updateItem(object $item, object $existing, object $repo): bool
{
// make sure there was a change
$sha = $existing->params->source[$repo->guid . '-settings'] ?? null;
$_existing = $this->mapItem($existing);
if ($sha === null || $this->areObjectsEqual($item, $_existing))
{
return false;
}
else
{
// strip these values form the settings
$code = (string) $item->main_class_code ?? '';
$extends_name = (string) $item->extends_name ?? '';
$implement_names = (string) $item->implement_names ?? '';
unset($item->main_class_code);
unset($item->extends_name);
unset($item->implement_names);
$this->git->update(
$repo->organisation, // The owner name.
$repo->repository, // The repository name.
'src/' . $item->guid . '/' . $this->getSettingsPath(), // The file path.
json_encode($item, JSON_PRETTY_PRINT), // The file content.
'Update ' . $item->system_name . ' settings', // The commit message.
$sha, // The blob SHA of the old file.
$repo->write_branch // The branch name.
);
$item->main_class_code = $code;
$item->extends_name = $extends_name;
$item->implement_names = $implement_names;
}
return $this->updatePower($item, $existing, $repo);
}
/**
* update an existing power code (if changed)
*
* @param object $item
* @param object $existing
* @param object $repo
*
* @return bool
* @since 5.0.3
*/
protected function updatePower(object $item, object $existing, object $repo): bool
{
// make sure there was a change
$sha = $existing->params->source[$repo->guid . '-power'] ?? null;
if ($sha === null)
{
return false;
}
// Calculate the new SHA from the current content
$power = $item->main_class_code ?? '';
$newSha = sha1("blob " . strlen($power) . "\0" . $power);
// Check if the new SHA matches the existing SHA
if ($sha === $newSha)
{
return false;
}
$this->git->update(
$repo->organisation, // The owner name.
$repo->repository, // The repository name.
'src/' . $item->guid . '/code.power', // The file path.
$power, // The file content.
'Update ' . $item->system_name . ' code', // The commit message.
$sha, // The blob SHA of the old file.
$repo->write_branch // The branch name.
);
return true;
}
/**
* create a new item
*
* @param object $item
* @param object $repo
*
* @return void
* @since 5.0.3
*/
protected function createItem(object $item, object $repo): void
{
// strip these values form the settings
$code = (string) $item->main_class_code ?? '';
$extends_name = (string) $item->extends_name ?? '';
$implement_names = (string) $item->implement_names ?? '';
unset($item->main_class_code);
unset($item->extends_name);
unset($item->implement_names);
$this->git->create(
$repo->organisation, // The owner name.
$repo->repository, // The repository name.
'src/' . $item->guid . '/' . $this->getSettingsPath(), // The file path.
json_encode($item, JSON_PRETTY_PRINT), // The file content.
'Create ' . $item->system_name . ' settings', // The commit message.
$repo->write_branch // The branch name.
);
$item->main_class_code = $code;
$item->extends_name = $extends_name;
$item->implement_names = $implement_names;
$this->createPower($item, $repo);
}
/**
* create a new power
*
* @param object $item
* @param object $repo
*
* @return void
* @since 5.0.3
*/
protected function createPower(object $item, object $repo): void
{
$this->git->create(
$repo->organisation, // The owner name.
$repo->repository, // The repository name.
'src/' . $item->guid . '/code.power', // The file path.
$item->main_class_code, // The file content.
'Create ' . $item->system_name . ' code', // The commit message.
$repo->write_branch // The branch name.
);
}
/**
* update an existing item readme
*
* @param object $item
* @param object $existing
* @param object $repo
*
* @return void
* @since 5.0.3
*/
protected function updateItemReadme(object $item, object $existing, object $repo): void
{
// make sure there was a change
$sha = $existing->params->source[$repo->guid . '-readme'] ?? null;
if ($sha === null)
{
return;
}
if ($this->parser !== null)
{
$item->parsed_class_code = $this->parser->code($item->main_class_code);
}
$item->code_name = $this->index_map_IndexName($item);
$item->_namespace = $this->index_map_NameSpace($item);
$readme = $this->itemReadme->get($item);
$newSha = sha1("blob " . strlen($readme) . "\0" . $readme);
// Check if the new SHA matches the existing SHA
if ($sha === $newSha)
{
return;
}
$this->git->update(
$repo->organisation, // The owner name.
$repo->repository, // The repository name.
'src/' . $item->guid . '/README.md', // The file path.
$readme, // The file content.
'Update ' . $item->system_name . ' readme file', // The commit message.
$sha, // The blob SHA of the old file.
$repo->write_branch // The branch name.
);
}
/**
* create a new item readme
*
* @param object $item
* @param object $repo
*
* @return void
* @since 5.0.3
*/
protected function createItemReadme(object $item, object $repo): void
{
if ($this->parser !== null)
{
$item->parsed_class_code = $this->parser->code($item->main_class_code);
}
$item->code_name = $this->index_map_IndexName($item);
$item->_namespace = $this->index_map_NameSpace($item);
$this->git->create(
$repo->organisation, // The owner name.
$repo->repository, // The repository name.
'src/' . $item->guid . '/README.md', // The file path.
$this->itemReadme->get($item), // The file content.
'Create ' . $item->system_name . ' readme file', // The commit message.
$repo->write_branch // The branch name.
);
}
/**
* check that we have a target repo of this item
*
* @param object $item The item
* @param object $repo The current repo
*
* @return bool
* @since 5.0.3
*/
protected function targetRepo(object $item, object $repo): bool
{
if (!isset($item->approved) || $item->approved != 1 ||
!isset($item->approved_paths) || !is_array($item->approved_paths))
{
return false;
}
$repo_path = "{$repo->organisation}/{$repo->repository}";
foreach ($item->approved_paths as $approved_path)
{
if ($repo_path === $approved_path)
{
return true;
}
}
return false;
}
/**
* Get the item name for the index values
*
* @param object $item
*
* @return string|null
* @since 5.0.3
*/
protected function index_map_IndexName(object $item): ?string
{
$name = $item->name ?? null;
if ($name !== null)
{
return ClassfunctionHelper::safe(
$this->updatePlaceholders($name)
);
}
return null;
}
/**
* Get the item type for the index values
*
* @param object $item
*
* @return string|null
* @since 5.0.3
*/
protected function index_map_TypeName(object $item): ?string
{
return $item->type ?? null;
}
/**
* Get the item code path for the index values
*
* @param object $item
*
* @return string|null
* @since 5.0.3
*/
protected function index_map_CodePath(object $item): ?string
{
return $this->index_map_IndexPath($item) . '/code.php';
}
/**
* Get the item power path for the index values
*
* @param object $item
*
* @return string|null
* @since 5.0.3
*/
protected function index_map_PowerPath(object $item): ?string
{
return $this->index_map_IndexPath($item) . '/code.power';
}
/**
* Get the item namespace for the index values
*
* @param object $item
*
* @return string|null
* @since 5.0.3
*/
protected function index_map_NameSpace(object $item): ?string
{
return $this->getNamespace($item->namespace ?? '', $item->name ?? '');
}
/**
* Set the namespace for this power
*
* @param string $namespace The raw namespace
* @param string $className The class name
*
* @return string|null
* @since 5.0.3
*/
protected function getNamespace(string $namespace, string $className): ?string
{
// set namespace
$namespace = $this->updatePlaceholders($namespace);
// validate namespace
if (strpos($namespace, '\\') === false)
{
// we break out here
return null;
}
// setup the path array
$path_array = (array) explode('\\', $namespace);
// make sure it has two or more
if (ArrayHelper::check($path_array) <= 1)
{
// we break out here
return null;
}
// get the file and class name (the last value in array)
$file_name = array_pop($path_array);
// do we have src folders
if (strpos($file_name, '.') !== false)
{
// we have src folders in the namespace
$src_array = (array) explode('.', $file_name);
// get the file and class name (the last value in array)
$file_name = array_pop($src_array);
// namespace array
$namespace_array = [...$path_array, ...$src_array];
}
else
{
// namespace array
$namespace_array = $path_array;
}
// the last value is the same as the class name
if ($file_name !== $className)
{
// we break out here
return null;
}
// make sure the arrays are namespace safe
$namespace_array =
array_map(
fn($val) => $this->getCleanNamespace($val),
$namespace_array
);
// set the actual class namespace
return implode('\\', $namespace_array);
}
/**
* Get Clean Namespace without use or ; as part of the name space
*
* @param string $namespace The actual name space
*
* @return string
* @since 5.0.3
*/
protected function getCleanNamespace(string $namespace): string
{
// trim possible (use) or (;) or (starting or ending \) added to the namespace
return NamespaceHelper::safe(str_replace(['use ', ';'], '', $namespace));
}
}

View File

@ -18,7 +18,11 @@ use VDM\Joomla\Componentbuilder\Power\Config;
use VDM\Joomla\Componentbuilder\Table;
use VDM\Joomla\Componentbuilder\Power\Grep;
use VDM\Joomla\Componentbuilder\Power\Remote\Get;
use VDM\Joomla\Componentbuilder\Power\Remote\Set;
use VDM\Joomla\Componentbuilder\Power\Parser;
use VDM\Joomla\Componentbuilder\Power\Plantuml;
use VDM\Joomla\Componentbuilder\Power\Readme\Item as ItemReadme;
use VDM\Joomla\Componentbuilder\Power\Readme\Main as MainReadme;
/**
@ -50,8 +54,20 @@ class Power implements ServiceProviderInterface
$container->alias(Get::class, 'Power.Remote.Get')
->share('Power.Remote.Get', [$this, 'getRemoteGet'], true);
$container->alias(Set::class, 'Power.Remote.Set')
->share('Power.Remote.Set', [$this, 'getRemoteSet'], true);
$container->alias(Parser::class, 'Power.Parser')
->share('Power.Parser', [$this, 'getParser'], true);
$container->alias(Plantuml::class, 'Power.Plantuml')
->share('Power.Plantuml', [$this, 'getPlantuml'], true);
$container->alias(ItemReadme::class, 'Power.Readme.Item')
->share('Power.Readme.Item', [$this, 'getItemReadme'], true);
$container->alias(MainReadme::class, 'Power.Readme.Main')
->share('Power.Readme.Main', [$this, 'getMainReadme'], true);
}
/**
@ -113,6 +129,55 @@ class Power implements ServiceProviderInterface
);
}
/**
* Get The Remote Set Class.
*
* @param Container $container The DI container.
*
* @return Set
* @since 5.0.3
*/
public function getRemoteSet(Container $container): Set
{
return new Set(
$container->get('Config')->approved_paths,
$container->get('Power.Grep'),
$container->get('Data.Items'),
$container->get('Power.Readme.Item'),
$container->get('Power.Readme.Main'),
$container->get('Gitea.Repository.Contents'), null, null, null,
$container->get('Power.Parser')
);
}
/**
* Get The Readme Class.
*
* @param Container $container The DI container.
*
* @return ItemReadme
* @since 5.0.3
*/
public function getItemReadme(Container $container): ItemReadme
{
return new ItemReadme(
$container->get('Power.Plantuml')
);
}
/**
* Get The Readme Class.
*
* @param Container $container The DI container.
*
* @return MainReadme
* @since 5.0.3
*/
public function getMainReadme(Container $container): MainReadme
{
return new MainReadme();
}
/**
* Get The Parser Class.
*
@ -124,6 +189,19 @@ class Power implements ServiceProviderInterface
public function getParser(Container $container): Parser
{
return new Parser();
}
/**
* Get The Plantuml Class.
*
* @param Container $container The DI container.
*
* @return Plantuml
* @since 5.0.3
*/
public function getPlantuml(Container $container): Plantuml
{
return new Plantuml();
}
}

View File

@ -9104,6 +9104,22 @@ final class Table extends BaseTable implements Tableinterface
'key' => true,
],
],
'addplaceholders' => [
'name' => 'addplaceholders',
'label' => 'COM_COMPONENTBUILDER_REPOSITORY_ADDPLACEHOLDERS_LABEL',
'type' => 'subform',
'title' => false,
'list' => 'repositories',
'store' => 'json',
'tab_name' => 'Placeholders',
'db' => [
'type' => 'TEXT',
'default' => 'EMPTY',
'null_switch' => 'NOT NULL',
'unique_key' => false,
'key' => false,
],
],
'access_repo' => [
'name' => 'access_repo',
'label' => 'COM_COMPONENTBUILDER_REPOSITORY_ACCESS_REPO_LABEL',

View File

@ -13,6 +13,8 @@ namespace VDM\Joomla\Componentbuilder\Utilities;
use Joomla\CMS\Factory;
use VDM\Joomla\Utilities\JsonHelper;
use VDM\Joomla\Utilities\ArrayHelper;
/**
@ -45,6 +47,7 @@ abstract class RepoHelper
'username',
'target',
'access_repo',
'addplaceholders',
'guid'
)))
->from($db->quoteName('#__componentbuilder_repository'))
@ -66,13 +69,43 @@ abstract class RepoHelper
unset($item->token);
}
unset($item->access_repo);
$item->placeholders = self::setPlaceholders($item->addplaceholders ?? '');
unset($item->addplaceholders);
$path = $item->organisation . '/' . $item->repository;
$options[$path] = $item;
}
return $options;
}
return null;
}
/**
* set the placeholders for this repo
*
* @param string $placeholders The repo placeholders
*
* @return array The result set
* @since 5.0.3
**/
protected static function setPlaceholders(string $placeholders): array
{
$bucket = [];
if (JsonHelper::check($placeholders))
{
$placeholders = json_decode((string) $placeholders, true);
if (ArrayHelper::check($placeholders))
{
foreach ($placeholders as $row)
{
$bucket[$row['target']] = $row['value'];
}
}
}
return $bucket;
}
}

View File

@ -39,17 +39,19 @@ abstract class ObjectHelper
}
/**
* Compare two objects for equality based on their property values.
* Checks if two objects are equal by comparing their properties and values.
*
* Note that this method works only for simple objects that don't
* contain any nested objects or resource references. If you need
* to compare more complex objects, you may need to use a
* more advanced method such as serialization or reflection.
* This method converts both input objects to
* associative arrays, sorts the arrays by keys,
* and compares these sorted arrays.
*
* @param object|null $obj1 The first object to compare.
* @param object|null $obj2 The second object to compare.
* If the arrays are identical, the objects are considered equal.
*
* @return bool True if the objects have the same key-value pairs and false otherwise.
* @param object|null $obj1 The first object to compare.
* @param object|null $obj2 The second object to compare.
*
* @return bool True if the objects are equal, false otherwise.
* @since 5.0.2
*/
public static function equal(?object $obj1, ?object $obj2): bool
{
@ -62,17 +64,41 @@ abstract class ObjectHelper
return false;
}
// Convert the objects to arrays of their property values using get_object_vars.
$array1 = get_object_vars($obj1);
$array2 = get_object_vars($obj2);
// Convert both objects to associative arrays
$array1 = json_decode(json_encode($obj1), true);
$array2 = json_decode(json_encode($obj2), true);
// Compare the arrays using array_diff_assoc to detect any differences.
$diff1 = array_diff_assoc($array1, $array2);
$diff2 = array_diff_assoc($array2, $array1);
// Sort the arrays by keys
self::recursiveKsort($array1);
self::recursiveKsort($array2);
// If the arrays have the same key-value pairs, they will have no differences, so return true.
return empty($diff1) && empty($diff2);
// Compare the sorted arrays
return $array1 === $array2;
}
/**
* Recursively sorts an associative array by keys.
*
* This method will sort an associative array by its keys at all levels.
*
* @param array &$array The array to sort.
*
* @return void
* @since 5.0.2
*/
protected static function recursiveKsort(array &$array): void
{
// Sort the array by its keys
ksort($array);
// Recursively sort nested arrays
foreach ($array as &$value)
{
if (is_array($value))
{
self::recursiveKsort($value);
}
}
}
}