Release of v5.0.1-beta1

Fix subform set methods. Improved the Joomla Power Push path. Fix the metadata, metadesc, metakey database issue.
This commit is contained in:
2024-07-17 02:30:54 +02:00
parent 18545c5b8d
commit a4a4a2ab9a
78 changed files with 3356 additions and 1203 deletions

View File

@@ -13,9 +13,12 @@ namespace VDM\Joomla\Abstraction;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Filesystem\Folder;
use Joomla\CMS\Application\CMSApplication;
use VDM\Joomla\Gitea\Repository\Contents;
use VDM\Joomla\Utilities\FileHelper;
use VDM\Joomla\Utilities\JsonHelper;
use VDM\Joomla\Interfaces\GrepInterface;
@@ -24,7 +27,7 @@ use VDM\Joomla\Interfaces\GrepInterface;
*
* The Grep feature will try to find your power in the repositories listed in the global
* Options of JCB in the super powers tab, and if it can't be found there will try the global core
* Super powers of JCB. All searches are performed according the the [algorithm:cascading]
* Super powers of JCB. All searches are performed according the [algorithm:cascading]
* See documentation for more details: https://git.vdm.dev/joomla/super-powers/wiki
*
* @since 3.2.1
@@ -34,7 +37,7 @@ abstract class Grep implements GrepInterface
/**
* The local path
*
* @var string
* @var string|null
* @since 3.2.0
**/
public ?string $path;
@@ -42,7 +45,7 @@ abstract class Grep implements GrepInterface
/**
* All approved paths
*
* @var array
* @var array|null
* @since 3.2.0
**/
public ?array $paths;
@@ -63,6 +66,22 @@ abstract class Grep implements GrepInterface
**/
protected string $branch_field = 'read_branch';
/**
* The target default branch name
*
* @var string|null
* @since 3.2.2
**/
protected ?string $branch_name = null;
/**
* The index file path
*
* @var string
* @since 3.2.2
*/
protected string $index_path = 'index.json';
/**
* Gitea Repository Contents
*
@@ -92,21 +111,65 @@ abstract class Grep implements GrepInterface
*/
public function __construct(Contents $contents, array $paths, ?string $path = null, ?CMSApplication $app = null)
{
$this->paths = $paths;
$this->contents = $contents;
$this->paths = $paths;
$this->path = $path;
$this->app = $app ?: Factory::getApplication();
$this->init();
$this->initializeInstances();
}
/**
* Get all remote powers GUID's
* Get an item
*
* @param string $guid The global unique id of the item
* @param array|null $order The search order
* @param object|null $repo The repository object to search. If null, all repos will be searched.
*
* @return object|null
* @since 3.2.2
*/
public function get(string $guid, ?array $order = null, ?object $repo = null): ?object
{
$order = $order ?? $this->order;
if ($repo !== null)
{
return $this->searchSingleRepo($guid, $order, $repo);
}
return $this->searchAllRepos($guid, $order);
}
/**
* Check if an item exists in any repo or in a specific repo.
*
* @param string $guid The unique identifier for the item.
* @param object|null $repo The repository object to check against. If null, all repos will be checked.
* @param array|null $order The order of the targets to check. If null, the default order will be used.
*
* @return bool True if the item exists, false otherwise.
* @since 3.2.2
*/
public function exists(string $guid, ?object $repo = null, ?array $order = null): bool
{
$order = $order ?? $this->order;
if ($repo !== null)
{
return $this->itemExistsInRepo($guid, $repo, $order);
}
return $this->itemExistsInAllRepos($guid, $order);
}
/**
* Get all remote GUID's
*
* @return array|null
* @since 3.2.0
*/
public function getRemotePowersGuid(): ?array
public function getRemoteGuid(): ?array
{
if (!is_array($this->paths) || $this->paths === [])
{
@@ -117,7 +180,7 @@ abstract class Grep implements GrepInterface
foreach ($this->paths as $path)
{
// Get remote index
$this->remoteIndex($path);
$this->indexRemote($path);
if (isset($path->index) && is_object($path->index))
{
@@ -131,7 +194,7 @@ abstract class Grep implements GrepInterface
/**
* Set the branch field
*
* @param string $field The global unique id of the power
* @param string $field The field to use to get the branch name from the data set
*
* @return void
* @since 3.2.2
@@ -142,28 +205,113 @@ abstract class Grep implements GrepInterface
}
/**
* Get a power
* Set the DEFAULT branch name (only used if branch field is not found)
*
* @param string $guid The global unique id of the power
* @param array|null $order The search order
* @param string|null $name The default branch to use if no name could be found
*
* @return void
* @since 3.2.2
*/
public function setBranchDefaultName(?string $name): void
{
$this->branch_name = $name;
}
/**
* Set the index path
*
* @param string $indexPath The repository index path
*
* @return void
* @since 3.2.2
*/
public function setIndexPath(string $indexPath): void
{
$this->index_path = $indexPath;
}
/**
* Get the index of a repo
*
* @param string $guid The unique identifier for the repo.
*
* @return object|null
* @since 3.2.0
* @since 3.2.2
*/
public function get(string $guid, ?array $order = null): ?object
public function getRemoteIndex(string $guid): ?object
{
if ($order === null)
if (!is_array($this->paths) || $this->paths === [] || empty($guid))
{
$order = $this->order;
return null;
}
// we can only search if we have paths
if (is_array($this->paths) && $this->paths !== [])
foreach ($this->paths as $path)
{
foreach ($order as $target)
if (!isset($path->guid) || $guid !== $path->guid)
{
if (($function_name = $this->getFunctionName($target)) !== null &&
($power = $this->{$function_name}($guid)) !== null)
continue;
}
// Get remote index
$this->indexRemote($path);
if (isset($path->index) && is_object($path->index))
{
return $path->index;
}
}
return null;
}
/**
* Set repository messages and errors based on given conditions.
*
* @param string $message The message to set (if error)
* @param string $path Path value
* @param string $repository Repository name
* @param string $organisation Organisation name
* @param string|null $base Base URL
*
* @return void
* @since 3.2.0
*/
abstract protected function setRemoteIndexMessage(string $message, string $path, string $repository, string $organisation, ?string $base): void;
/**
* Get function name
*
* @param string $name The targeted area name
* @param string $type The type of function name
*
* @return string|null
* @since 3.2.0
*/
protected function getFunctionName(string $name, string $type = 'search'): ?string
{
$function_name = $type . ucfirst(strtolower($name));
return method_exists($this, $function_name) ? $function_name : null;
}
/**
* Search a single repository for an item
*
* @param string $guid The unique identifier for the item.
* @param array $order The order of the targets to check.
* @param object $repo The repository object to check against.
*
* @return object|null
* @since 3.2.2
*/
protected function searchSingleRepo(string $guid, array $order, object $repo): ?object
{
foreach ($order as $target)
{
if ($this->itemExists($guid, $repo, $target))
{
$functionName = $this->getFunctionName($target, 'get');
if ($functionName !== null && ($power = $this->{$functionName}($repo, $guid)) !== null)
{
return $power;
}
@@ -173,6 +321,251 @@ abstract class Grep implements GrepInterface
return null;
}
/**
* Search all repositories for an item
*
* @param string $guid The unique identifier for the item.
* @param object $repo The repository object to check against.
*
* @return object|null
* @since 3.2.2
*/
protected function searchAllRepos(string $guid, array $order): ?object
{
if (is_array($this->paths) && $this->paths !== [])
{
foreach ($order as $target)
{
$functionName = $this->getFunctionName($target);
if ($functionName !== null && ($power = $this->{$functionName}($guid)) !== null)
{
return $power;
}
}
}
return null;
}
/**
* Check if an item exists in a specific repository.
*
* @param string $guid The unique identifier for the item.
* @param object $repo The repository object to check against.
* @param array $order The order of the targets to check.
*
* @return bool True if the item exists, false otherwise.
* @since 3.2.2
*/
protected function itemExistsInRepo(string $guid, object $repo, array $order): bool
{
foreach ($order as $target)
{
if ($this->itemExists($guid, $repo, $target))
{
return true;
}
}
return false;
}
/**
* Check if an item exists in any of the repositories.
*
* @param string $guid The unique identifier for the item.
* @param array $order The order of the targets to check.
*
* @return bool True if the item exists, false otherwise.
* @since 3.2.2
*/
protected function itemExistsInAllRepos(string $guid, array $order): bool
{
// We can only search if we have paths
if (is_array($this->paths) && $this->paths !== [])
{
foreach ($order as $target)
{
foreach ($this->paths as $path)
{
if ($this->itemExists($guid, $path, $target))
{
return true;
}
}
}
}
return false;
}
/**
* Get the branch field
*
* @return string
* @since 3.2.2
*/
protected function getBranchField(): string
{
return $this->branch_field;
}
/**
* Get the branch default name
*
* @return string|null
* @since 3.2.2
*/
protected function getBranchDefaultName(): ?string
{
return $this->branch_name;
}
/**
* Get the branch name
*
* @param object $item The item path
*
* @return string|null
* @since 3.2.2
*/
protected function getBranchName(object $item): ?string
{
// get the branch field name
$branch_field = $this->getBranchField();
return $item->{$branch_field} ?? $this->getBranchDefaultName();
}
/**
* Get the index path
*
* @return string
* @since 3.2.2
*/
protected function getIndexPath(): string
{
return $this->index_path;
}
/**
* Check if an item exists in a specific repo and target.
*
* @param string $guid The unique identifier for the item.
* @param object $repo The repository object to check against.
* @param string $target The target to check within the repo.
*
* @return bool True if the item exists, false otherwise.
* @since 3.2.2
*/
protected function itemExists(string $guid, object &$repo, string $target): bool
{
if (($function_name = $this->getFunctionName($target, 'index')) !== null)
{
$this->{$function_name}($repo);
if (($function_name = $this->getFunctionName($target, 'exists')) !== null &&
$this->{$function_name}($guid, $repo))
{
return true;
}
}
return false;
}
/**
* Check if item exists locally
*
* @param string $guid The global unique id of the item
*
* @return object|null return path object
* @since 3.2.2
*/
protected function existsLocally(string $guid): ?object
{
// we can only search if we have paths
if ($this->path && $this->paths)
{
foreach ($this->paths as $path)
{
// get local index
$this->indexLocal($path);
if ($this->existsLocal($guid, $path))
{
return $path;
}
}
}
return null;
}
/**
* Check if item exists remotely
*
* @param string $guid The global unique id of the item
*
* @return object|null return path object
* @since 3.2.2
*/
protected function existsRemotely(string $guid): ?object
{
// we can only search if we have paths
if ($this->paths)
{
foreach ($this->paths as $path)
{
// get local index
$this->indexRemote($path);
if ($this->existsRemote($guid, $path))
{
return $path;
}
}
}
return null;
}
/**
* Check if item exists locally
*
* @param string $guid The global unique id of the item
* @param object $path The path object
*
* @return bool true if it exists
* @since 3.2.2
*/
protected function existsLocal(string $guid, object $path): bool
{
if (!empty($path->local) && isset($path->local->{$guid}))
{
return true;
}
return false;
}
/**
* Check if item exists remotely
*
* @param string $guid The global unique id of the item
* @param object $path The path object
*
* @return bool true if it exists
* @since 3.2.2
*/
protected function existsRemote(string $guid, object $path): bool
{
if (!empty($path->index) && isset($path->index->{$guid}))
{
return true;
}
return false;
}
/**
* Load the remote repository index of powers
*
@@ -181,21 +574,55 @@ abstract class Grep implements GrepInterface
* @return void
* @since 3.2.0
*/
abstract protected function remoteIndex(object &$path): void;
protected function indexRemote(object &$path): void
{
if (isset($path->index))
{
return; // already set
}
try
{
// load the base and token if set
$this->contents->load_($path->base ?? null, $path->token ?? null);
$path->index = $this->contents->get($path->organisation, $path->repository, $this->getIndexPath(), $this->getBranchName($path));
}
catch (\Exception $e)
{
$path->index = null;
$this->setRemoteIndexMessage($e->getMessage(), $path->path, $path->repository, $path->organisation, $path->base ?? null);
}
finally
{
// reset back to the global base and token
$this->contents->reset_();
}
}
/**
* Get function name
* Load the local repository index of powers
*
* @param string $name The targeted function name
* @param object $path The repository path details
*
* @return string|null
* @return void
* @since 3.2.0
*/
protected function getFunctionName(string $name): ?string
protected function indexLocal(object &$path): void
{
$function_name = 'search' . ucfirst(strtolower($name));
if (isset($path->local) || !isset($path->full_path))
{
return;
}
return method_exists($this, $function_name) ? $function_name : null;
if (($content = FileHelper::getContent($path->full_path . '/' . $this->getIndexPath(), null)) !== null &&
JsonHelper::check($content))
{
$path->local = json_decode($content);
return;
}
$path->local = null;
}
/**
@@ -204,7 +631,7 @@ abstract class Grep implements GrepInterface
* @return void
* @since 3.2.0
*/
protected function init(): void
protected function initializeInstances(): void
{
if (is_array($this->paths) && $this->paths !== [])
{
@@ -216,12 +643,14 @@ abstract class Grep implements GrepInterface
// build the path
$path->path = trim($path->organisation) . '/' . trim($path->repository);
// update the branch
// get the branch field name
$branch_field = $this->getBranchField();
$branch = $path->{$branch_field} ?? null;
// get the branch name
$branch = $this->getBranchName($path);
if ($branch === 'default' || empty($branch))
{
// will allow us to target the default branch as set by the git system
$path->{$branch_field} = null;
}
@@ -240,14 +669,33 @@ abstract class Grep implements GrepInterface
}
/**
* Get the branch field
* Load the remote file
*
* @return string
* @since 3.2.2
* @param string $organisation The repository organisation
* @param string $repository The repository name
* @param string $path The repository path to file
* @param string|null $branch The repository branch name
*
* @return mixed
* @since 3.2.0
*/
public function getBranchField(): string
protected function loadRemoteFile(string $organisation, string $repository, string $path, ?string $branch)
{
return $this->branch_field;
try
{
$data = $this->contents->get($organisation, $repository, $path, $branch);
}
catch (\Exception $e)
{
$this->app->enqueueMessage(
Text::sprintf('COM_COMPONENTBUILDER_PFILE_AT_BSSB_GAVE_THE_FOLLOWING_ERRORBR_SP', $this->contents->api(), $path, $e->getMessage()),
'Error'
);
return null;
}
return $data;
}
}

View File

@@ -0,0 +1,169 @@
<?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\Abstraction\Remote;
use VDM\Joomla\Interfaces\GrepInterface as Grep;
use VDM\Joomla\Interfaces\Data\ItemInterface as Item;
use VDM\Joomla\Interfaces\Remote\GetInterface;
/**
* Get data based on global unique ids from remote system
*
* @since 3.2.0
*/
abstract class Get implements GetInterface
{
/**
* The Grep Class.
*
* @var Grep
* @since 3.2.0
*/
protected Grep $grep;
/**
* The Item Class.
*
* @var Item
* @since 3.2.0
*/
protected Item $item;
/**
* Table Name
*
* @var string
* @since 3.2.1
*/
protected string $table;
/**
* Constructor.
*
* @param Grep $grep The GrepInterface Class.
* @param Item $item The ItemInterface Class.
* @param string|null $table The table name.
*
* @since 3.2.0
*/
public function __construct(Grep $grep, Item $item, ?string $table = null)
{
$this->grep = $grep;
$this->item = $item;
if ($table !== null)
{
$this->table = $table;
}
}
/**
* Set the current active table
*
* @param string $table The table that should be active
*
* @return self
* @since 3.2.2
*/
public function table(string $table): self
{
$this->table = $table;
return $this;
}
/**
* Init all items not found in database
*
* @return bool
* @since 3.2.0
*/
public function init(): bool
{
if (($items = $this->grep->getRemoteGuid()) !== null)
{
foreach($items as $guid)
{
if ($this->item->table($this->getTable())->value($guid) !== null &&
($item = $this->grep->get($guid, ['remote'])) !== null)
{
$this->item->set($item);
}
}
return true;
}
return false;
}
/**
* Reset the items
*
* @param array $items The global unique ids of the items
*
* @return bool
* @since 3.2.0
*/
public function reset(array $items): bool
{
if ($items === [])
{
return false;
}
$success = true;
foreach($items as $guid)
{
if (!$this->item($guid, ['remote']))
{
$success = false;
}
}
return $success;
}
/**
* Load an item
*
* @param string $guid The global unique id of the item
* @param array $order The search order
* @param string|null $action The action to load power
*
* @return bool
* @since 3.2.0
*/
public function item(string $guid, array $order = ['remote', 'local'], ?string $action = null): bool
{
if (($item = $this->grep->get($guid, $order)) !== null)
{
return $this->item->table($this->getTable())->set($item);
}
return false;
}
/**
* Get the current active table
*
* @return string
* @since 3.2.2
*/
public function getTable(): string
{
return $this->table;
}
}

View File

@@ -0,0 +1,782 @@
<?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\Abstraction\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\Interfaces\Remote\SetInterface;
/**
* Set data based on global unique ids to remote repository
*
* @since 3.2.2
*/
abstract class Set implements SetInterface
{
/**
* The Grep Class.
*
* @var Grep
* @since 3.2.2
*/
protected Grep $grep;
/**
* The Items Class.
*
* @var Items
* @since 3.2.2
*/
protected Items $items;
/**
* The Item Readme Class.
*
* @var ItemReadme
* @since 3.2.2
*/
protected ItemReadme $itemReadme;
/**
* The Main Readme Class.
*
* @var MainReadme
* @since 3.2.2
*/
protected MainReadme $mainReadme;
/**
* The Contents Class.
*
* @var Git
* @since 3.2.2
*/
protected Git $git;
/**
* All active repos
*
* @var array
* @since 3.2.0
**/
public array $repos;
/**
* Table Name
*
* @var string
* @since 3.2.1
*/
protected string $table;
/**
* Area Name
*
* @var string
* @since 3.2.1
*/
protected string $area;
/**
* The item map
*
* @var array
* @since 3.2.2
*/
protected array $map;
/**
* The index map
*
* @var array
* @since 3.2.2
*/
protected array $index_map;
/**
* The repo main settings
*
* @var array
* @since 3.2.2
*/
protected array $settings;
/**
* Prefix Key
*
* @var string
* @since 3.2.2
*/
protected string $prefix_key = 'Super---';
/**
* Suffix Key
*
* @var string
* @since 3.2.2
*/
protected string $suffix_key = '---Power';
/**
* The item settings file path
*
* @var string
* @since 3.2.2
*/
protected string $settings_path = 'item.json';
/**
* The index settings file path
*
* @var string
* @since 3.2.2
*/
protected string $index_settings_path = 'index.json';
/**
* 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.
*
* @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)
{
$this->repos = $repos;
$this->grep = $grep;
$this->items = $items;
$this->itemReadme = $itemReadme;
$this->mainReadme = $mainReadme;
$this->git = $git;
if ($table !== null)
{
$this->table = $table;
}
if ($settingsPath !== null)
{
$this->settings_path = $settingsPath;
}
if ($settingsIndexPath !== null)
{
$this->setIndexSettingsPath($settingsIndexPath);
}
if (empty($this->area))
{
$this->area = ucfirst(str_replace('_', ' ', $this->table));
}
// set the branch to writing
$this->grep->setBranchField('write_branch');
}
/**
* Set the current active table
*
* @param string $table The table that should be active
*
* @return self
* @since 3.2.2
*/
public function table(string $table): self
{
$this->table = $table;
return $this;
}
/**
* Set the current active area
*
* @param string $area The area that should be active
*
* @return self
* @since 3.2.2
*/
public function area(string $area): self
{
$this->area = ucfirst(str_replace('_', ' ', $area));
return $this;
}
/**
* Set the settings path
*
* @param string $settingsPath The repository settings path
*
* @return self
* @since 3.2.2
*/
public function setSettingsPath(string $settingsPath): self
{
$this->settings_path = $settingsPath;
return $this;
}
/**
* Set the index settings path
*
* @param string $settingsIndexPath The repository index settings path
*
* @return self
* @since 3.2.2
*/
public function setIndexSettingsPath(string $settingsIndexPath): self
{
$this->index_settings_path = $settingsIndexPath;
$this->grep->setIndexPath($settingsIndexPath);
return $this;
}
/**
* Save items remotely
*
* @param array $guids The global unique id of the item
*
* @return bool
* @throws \Exception
* @since 3.2.2
*/
public function items(array $guids): bool
{
if (!$this->canWrite())
{
throw new \Exception("At least one [{$this->getArea()}] content repository must be configured with a [Write Branch] value in the repositories area for the push function to operate correctly.");
}
// we reset the index settings
$this->settings = [];
if (($items = $this->getLocalItems($guids)) === null)
{
throw new \Exception("At least one valid local [{$this->getArea()}] must exist for the push function to operate correctly.");
}
foreach ($items as $item)
{
$this->save($item);
}
// update the repos main readme and index settings
if ($this->settings !== [])
{
foreach ($this->settings as $repo)
{
$this->saveRepoMainSettings($repo);
}
}
return true;
}
/**
* update an existing item (if changed)
*
* @param object $item
* @param object $existing
* @param object $repo
*
* @return bool
* @since 3.2.2
*/
abstract protected function updateItem(object $item, object $existing, object $repo): bool;
/**
* create a new item
*
* @param object $item
* @param object $repo
*
* @return void
* @since 3.2.2
*/
abstract protected function createItem(object $item, object $repo): void;
/**
* update an existing item readme
*
* @param object $item
* @param object $existing
* @param object $repo
*
* @return void
* @since 3.2.2
*/
abstract protected function updateItemReadme(object $item, object $existing, object $repo): void;
/**
* create a new item readme
*
* @param object $item
* @param object $repo
*
* @return void
* @since 3.2.2
*/
abstract protected function createItemReadme(object $item, object $repo): void;
/**
* Get the current active table
*
* @return string
* @since 3.2.2
*/
protected function getTable(): string
{
return $this->table;
}
/**
* Get the current active area
*
* @return string
* @since 3.2.2
*/
protected function getArea(): string
{
return $this->area;
}
/**
* Update/Create the repo main readme and index
*
* @param array $repoBucket
*
* @return void
* @since 3.2.2
*/
protected function saveRepoMainSettings(array $repoBucket): void
{
$repo = $repoBucket['repo'] ?? null;
$settings = $repoBucket['items'] ?? null;
if ($this->isInvalidIndexRepo($repo, $settings))
{
return;
}
$repoGuid = $repo->guid ?? null;
if (empty($repoGuid))
{
return;
}
$settings = $this->mergeIndexSettings($repoGuid, $settings);
$this->updateIndexMainFile(
$repo,
$this->getIndexSettingsPath(),
json_encode($settings, JSON_PRETTY_PRINT),
'Update main index file'
);
$this->updateIndexMainFile(
$repo,
'README.md',
$this->mainReadme->get($settings),
'Update main readme file'
);
}
/**
* Validate repository and settings
*
* @param mixed $repo
* @param mixed $settings
*
* @return bool
* @since 3.2.2
*/
protected function isInvalidIndexRepo($repo, $settings): bool
{
return empty($repo) || empty($settings);
}
/**
* Merge current settings with new settings
*
* @param string $repoGuid
* @param array $settings
*
* @return array
* @since 3.2.2
*/
protected function mergeIndexSettings(string $repoGuid, array $settings): array
{
$current_settings = $this->grep->getRemoteIndex($repoGuid);
if ($current_settings === null || (array) $current_settings === [])
{
return $settings;
}
$mergedSettings = [];
foreach ($current_settings as $guid => $setting)
{
$mergedSettings[$guid] = (array) $setting;
}
foreach ($settings as $guid => $setting)
{
$mergedSettings[$guid] = (array) $setting;
}
return $mergedSettings;
}
/**
* Update a file in the repository
*
* @param object $repo
* @param string $path
* @param string $content
* @param string $message
*
* @return void
* @since 3.2.2
*/
protected function updateIndexMainFile(object $repo, string $path,
string $content, string $message): void
{
$meta = $this->git->metadata(
$repo->organisation,
$repo->repository,
$path,
$repo->write_branch
);
if ($meta !== null && isset($meta->sha))
{
$this->git->update(
$repo->organisation,
$repo->repository,
$path,
$content,
$message,
$meta->sha,
$repo->write_branch
);
}
}
/**
* Get items
*
* @param array $guids The global unique id of the item
*
* @return array|null
* @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
*
* @param object $item The item to be mapped
*
* @return object
* @since 3.2.2
*/
protected function mapItem(object $item): object
{
$power = [];
foreach ($this->map as $key => $map)
{
$power[$key] = $item->{$map} ?? null;
}
return (object) $power;
}
/**
* Save an item remotely
*
* @param object $item The item to save
*
* @return void
* @since 3.2.2
*/
protected function save(object $item): void
{
if (empty($item->guid))
{
return;
}
$index_item = null;
foreach ($this->repos as $key => $repo)
{
if (empty($repo->write_branch) || $repo->write_branch === 'default')
{
continue;
}
$this->git->load_($repo->base ?? null, $repo->token ?? null);
if (($existing = $this->grep->get($item->guid, ['remote'], $repo)) !== null)
{
if ($this->updateItem($item, $existing, $repo))
{
$this->updateItemReadme($item, $existing, $repo);
}
}
else
{
$this->createItem($item, $repo);
$this->createItemReadme($item, $repo);
$index_item ??= $this->getIndexItem($item);
if (!isset($this->settings[$key]))
{
$this->settings[$key] = ['repo' => $repo, 'items' => [$item->guid => $index_item]];
}
else
{
$this->settings[$key]['items'][$item->guid] = $index_item;
}
}
$this->git->reset_();
}
}
/**
* Get index values
*
* @param object $item The item
*
* @return array|null
* @since 3.2.0
*/
protected function getIndexItem(object $item): ?array
{
if (empty($this->index_map))
{
return null;
}
$index_item = [];
foreach ($this->index_map as $key => $function_name)
{
if (method_exists($this, $function_name))
{
$index_item[$key] = $this->{$function_name}($item);
}
}
return $index_item ?? null;
}
/**
* check that we have an active repo towards which we can write data
*
* @return bool
* @since 3.2.2
*/
protected function canWrite(): bool
{
foreach ($this->repos as $repo)
{
if (!empty($repo->write_branch) && $repo->write_branch !== 'default')
{
return true;
}
}
return false;
}
/**
* Checks if two objects are equal by comparing their JSON representations.
*
* 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 $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
*/
protected function areObjectsEqual(object $obj1, object $obj2): bool
{
// Convert both objects to JSON strings
$json1 = json_encode($obj1);
$json2 = json_encode($obj2);
// Compare the JSON strings
return $json1 === $json2;
}
/**
* Get the settings path
*
* @return string
* @since 3.2.2
*/
protected function getSettingsPath(): string
{
return $this->settings_path;
}
/**
* Get the index settings path
*
* @return string
* @since 3.2.2
*/
protected function getIndexSettingsPath(): string
{
return $this->index_settings_path;
}
//// index_map_ (area) /////////////////////////////////////////////
/**
* Get the item name for the index values
*
* @param object $item
*
* @return string|null
* @since 3.2.2
*/
protected function index_map_IndexName(object $item): ?string
{
return $item->system_name ?? null;
}
/**
* Get the item settings path for the index values
*
* @param object $item
*
* @return string
* @since 3.2.2
*/
protected function index_map_IndexSettingsPath(object $item): string
{
return "src/{$item->guid}/" . $this->getSettingsPath();
}
/**
* Get the item path for the index values
*
* @param object $item
*
* @return string
* @since 3.2.2
*/
protected function index_map_IndexPath(object $item): string
{
return "src/{$item->guid}";
}
/**
* Get the item JPK for the index values
*
* @param object $item
*
* @return string
* @since 3.2.2
*/
protected function index_map_IndexKey(object $item): string
{
return $this->prefix_key . str_replace('-', '_', $item->guid) . $this->suffix_key;
}
/**
* Get the item GUID for the index values
*
* @param object $item
*
* @return string
* @since 3.2.2
*/
protected function index_map_IndexGUID(object $item): string
{
return $item->guid;
}
}

View File

@@ -0,0 +1 @@
<html><body bgcolor="#FFFFFF"></body></html>

View File

@@ -0,0 +1,196 @@
<?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\Abstraction;
use Joomla\CMS\Factory;
use Joomla\CMS\Application\CMSApplication;
use VDM\Joomla\Interfaces\SchemaInterface as Schema;
use VDM\Joomla\Interfaces\Tableinterface as Table;
use VDM\Joomla\Utilities\ClassHelper;
use VDM\Joomla\Interfaces\SchemaCheckerInterface;
/**
* Schema Checker
*
* @since 3.2.2
*/
abstract class SchemaChecker implements SchemaCheckerInterface
{
/**
* The Table Class.
*
* @var Table|null
* @since 3.2.2
*/
protected ?Table $table;
/**
* The Schema Class.
*
* @var Schema|null
* @since 3.2.2
*/
protected ?Schema $schema;
/**
* Application object.
*
* @var CMSApplication
* @since 3.2.2
**/
protected CMSApplication $app;
/**
* Constructor.
*
* @param Schema|null $schema The Schema Class.
* @param Table|null $table The Table Class.
* @param CMSApplication|null $app The app object.
*
* @throws \Exception
* @since 3.2.2
*/
public function __construct(?Schema $schema = null, ?Table $table = null, ?CMSApplication $app = null)
{
$this->schema = $schema;
$this->table = $table;
$this->app = $app ?: Factory::getApplication();
// Validate classes are set
// Since this class is often called from outside a container
$this->initializeInstances();
// I don't care! I have more important thing to do, maybe later... (last updated in 1983 ;)
}
/**
* Make sure that the database schema is up-to-date.
*
* @return void
* @since 3.2.2
*/
public function run(): void
{
if ($this->schema === null)
{
$this->app->enqueueMessage('We failed to find/load the Schema class', 'warning');
return;
}
// try to load the update the tables with the schema class
try
{
$messages = $this->schema->update();
}
catch (\Exception $e)
{
$this->app->enqueueMessage($e->getMessage(), 'warning');
return;
}
foreach ($messages as $message)
{
$this->app->enqueueMessage($message, 'message');
}
}
/**
* Initialize the needed class instances if needed
*
* @return void
* @since 3.2.2
*/
protected function initializeInstances(): void
{
if ($this->schema !== null)
{
return;
}
if ($this->table === null)
{
$this->setTableInstance();
}
$this->setSchemaInstance();
}
/**
* set the schema class instance
*
* @return void
* @since 3.2.2
*/
protected function setSchemaInstance(): void
{
// make sure the class is loaded
if (ClassHelper::exists(
$this->getSchemaClass(), $this->getCode(), $this->getPowerPath()
))
{
// instantiate the schema class
$this->schema = new ($this->getSchemaClass())($this->table);
}
}
/**
* set the table class instance
*
* @return void
* @since 3.2.2
*/
protected function setTableInstance(): void
{
// make sure the class is loaded
if (ClassHelper::exists(
$this->getTableClass(), $this->getCode(), $this->getPowerPath()
))
{
// instantiate the table class
$this->table = new ($this->getTableClass())();
}
}
/**
* Get the targeted component code
*
* @return string
* @since 3.2.2
*/
abstract protected function getCode(): string;
/**
* Get the targeted component power path
*
* @return string
* @since 3.2.2
*/
abstract protected function getPowerPath(): string;
/**
* Get the fully qualified name of the schema class.
*
* @return string
* @since 3.2.2
*/
abstract protected function getSchemaClass(): string;
/**
* Get the fully qualified name of the table class.
*
* @return string
* @since 3.2.2
*/
abstract protected function getTableClass(): string;
}