Release of v5.1.0

Add [AllowDynamicProperties] in the base view class for J5. Move the _prepareDocument  above the display call in the base view class. Remove all backward compatibility issues, so JCB will not need the [Backward Compatibility] plugin to run. Added new import powers for custom import of spreadsheets. Move the setDocument and _prepareDocument above the display in the site view and custom admin view. Update the trashhelper layout to work in Joomla 5. Add AllowDynamicProperties (Joomla 4+5) to view class to allow Custom Dynamic Get methods to work without issues. Fix Save failed issue in dynamicGet. #1148. Move all [TEXT, EDITOR, TEXTAREA] fields from [NOT NULL] to [NULL]. Add the DateHelper class and improve the date methods. Add simple SessionHelper class. Add first classes for the new import engine. Improve the [VDM Registry] to be Joomla Registry Compatible. Move all registries to the [VDM Registry] class. Fix Checked Out to be null and not 0. (#1194). Fix created_by, modified_by, checked_out fields in the compiler of the SQL. (#1194). Update all core date fields in table class. (#1188). Update created_by, modified_by, checked_out fields in table class. Implementation of the decentralized Super-Power CORE repository network. (#1190). Fix the noticeboard to display Llewellyn's Joomla Social feed. Started compiling JCB5 on Joomla 5 with PHP 8.2. Add init_defaults option for dynamic form selection setup (to int new items with default values dynamically). Update all JCB 5 tables to utf8mb4_unicode_ci collation if misaligned. Move all internal ID linking to GUID inside of JCB 5. Updated the admin-tab-fields in add-fields view. #1205. Remove Custom Import Tab from admin view. Improved the customcode and placeholder search features.
This commit is contained in:
2025-02-14 22:55:55 +02:00
parent 82922eca5a
commit 442263e387
645 changed files with 42297 additions and 18475 deletions

View File

@@ -0,0 +1,33 @@
<?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\Network;
use VDM\Joomla\Abstraction\Registry;
/**
* The Network Core
*
* @since 5.0.4
*/
final class Core extends Registry
{
/**
* Path separator
*
* @var string|null
* @since 3.2.0
*/
protected ?string $separator = '|';
}

View File

@@ -0,0 +1,33 @@
<?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\Network;
use VDM\Joomla\Abstraction\Registry;
/**
* The Network Parsed Urls
*
* @since 5.0.4
*/
final class ParsedUrls extends Registry
{
/**
* Path separator
*
* @var string|null
* @since 3.2.0
*/
protected ?string $separator = '|';
}

View File

@@ -0,0 +1,179 @@
<?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\Network;
use Joomla\CMS\Log\Log;
use VDM\Joomla\Componentbuilder\Network\Url;
use VDM\Joomla\Componentbuilder\Network\Status;
/**
* The Network Resolver
*
* @since 5.0.4
*/
final class Resolve
{
/**
* The Url Class.
*
* @var Url
* @since 5.0.4
*/
protected Url $url;
/**
* The Status Class.
*
* @var Status
* @since 5.0.4
*/
protected Status $status;
/**
* Constructor.
*
* @param Url $url The Url Class.
* @param Status $status The Status Class.
*
* @since 5.0.4
*/
public function __construct(Url $url, Status $status)
{
$this->url = $url;
$this->status = $status;
}
/**
* Resolves the API for a repository if it is part of the core network.
*
* This method attempts to verify the status of the API and resolve an active URL if the current one is inactive.
*
* @param string $target The target network.
* @param string &$domain The API base domain (passed by reference).
* @param string &$organisation The repository organisation (passed by reference).
* @param string &$repository The repository name (passed by reference).
*
* @return void
* @since 5.0.4
*/
public function api(string $target, string &$domain, string &$organisation, string &$repository): void
{
try {
// Check the status of the current API
$status = $this->status->get($target, $domain, $repository, $organisation);
// If the API is inactive, attempt to find another active URL
if ($status == 0)
{
$this->resolve($target, $domain, $organisation, $repository);
}
} catch (\Exception $e) {
// ignore any none [in]active urls
$this->logError($e, 'Failed to resolve API status.');
}
}
/**
* Resolves an active API URL if the current API is inactive.
*
* Updates the `$domain`, `$organisation`, and `$repository` parameters to point to an active API URL.
*
* @param string $target The target network.
* @param string &$domain The API base domain (passed by reference).
* @param string &$organisation The repository organisation (passed by reference).
* @param string &$repository The repository name (passed by reference).
*
* @return void
* @throws \Exception
* @since 5.0.4
*/
private function resolve(string $target, string &$domain, string &$organisation, string &$repository): void
{
$activeRepo = $this->active($target);
if ($activeRepo === null) {
// No active API found, log or handle this case as needed
throw new \Exception('No active API found for the target: ' . $target);
}
try {
// Parse the active repository's URL and update the references
$parsedUrl = $this->url->parse($activeRepo->url);
$noneActiveDomain = "{$domain}/{$organisation}/{$repository}";
$activeDomain = "{$parsedUrl->scheme}://{$parsedUrl->domain}/{$parsedUrl->organisation}/{$parsedUrl->repository}";
// update the values passed by reference
$domain = $parsedUrl->scheme . '://' . $parsedUrl->domain;
$organisation = $parsedUrl->organisation ?? $organisation;
$repository = $parsedUrl->repository ?? $repository;
// add info
$this->logInfo("Resolved [{$noneActiveDomain}] to [{$activeDomain}]");
} catch (\Exception $e) {
// ignore any none [in]active urls
$this->logError($e, 'Failed to parse active repository URL.');
}
}
/**
* Retrieves a random active repository target, excluding the specified domain.
*
* @param string $target The target network.
*
* @return object|null The randomly selected active repository, or null if none found.
* @since 5.0.4
*/
private function active(string $target): ?object
{
try {
$activeRepo = $this->status->active($target);
} catch (\Exception $e) {
// ignore any none [in]active urls
$this->logError($e, "Failed to get an [{$target}] active repository.");
}
return $activeRepo;
}
/**
* Logs an info custom message.
*
* @param string $message A custom message to include with the log entry.
*
* @return void
* @since 5.0.4
*/
private function logInfo(string $message): void
{
Log::add($message, Log::INFO, 'jcb-network-resolve');
}
/**
* Logs an error with a custom message.
*
* This method is a placeholder for your actual logging mechanism.
*
* @param \Exception $exception The exception to log.
* @param string $message A custom message to include with the log entry.
*
* @return void
* @since 5.0.4
*/
private function logError(\Exception $exception, string $message): void
{
Log::add($message . ' Exception: ' . $exception->getMessage(), Log::ERROR, 'jcb-network-resolve');
}
}

View File

@@ -0,0 +1,258 @@
<?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\Network;
use VDM\Joomla\Componentbuilder\Api\Network;
use VDM\Joomla\Componentbuilder\Network\Core;
use VDM\Joomla\Componentbuilder\Network\Url;
/**
* The Network Status
*
* @since 5.0.4
*/
final class Status
{
/**
* The Network Class.
*
* @var Network
* @since 5.0.4
*/
protected Network $network;
/**
* The Core Class.
*
* @var Core
* @since 5.0.4
*/
protected Core $core;
/**
* The Url Class.
*
* @var Url
* @since 5.0.4
*/
protected Url $url;
/**
* Constructor.
*
* @param Network $network The Network Class.
* @param Core $core The Core Class.
* @param Url $url The Url Class.
*
* @since 5.0.4
*/
public function __construct(Network $network, Core $core, Url $url)
{
$this->network = $network;
$this->core = $core;
$this->url = $url;
}
/**
* Retrieves the status for the given network target, utilizing caching via the Core registry.
*
* @param string $target The target network.
* @param string $domain The domain to retrieve [example: codeberg.org].
* @param string $repository The repository name.
* @param string $organization The target repository organization. (default: joomla)
*
* @return int Will return 1 if active, 0 if not, and -1 if not part of the core.
*
* @since 5.0.4
*/
public function get(string $target, string $domain, string $repository, string $organization = 'joomla'): int
{
try {
$repo = $this->network($target, $domain, $organization, $repository);
if ($repo === null)
{
// Domain not found in the network data
return -1;
}
// Check if the repository is active
if (isset($repo->status) && is_numeric($repo->status))
{
return (int) $repo->status;
}
else
{
// 'status' property not found or not numeric
return -1;
}
}
catch (\Exception $e)
{
// In case of any exception, return -1
return -1;
}
}
/**
* Retrieves a random active repository target, excluding the specified domain.
*
* @param string $target The target network name.
* @param array|null $excludeDomains The domain to exclude [default: ['git.vdm.dev']].
*
* @return object|null The randomly selected active repository, or null if none found.
* @since 5.0.4
*/
public function active(string $target, ?array $excludeDomains = ['git.vdm.dev']): ?object
{
try {
// Get the network data for the target
$data = $this->network($target);
// Filter active repositories excluding the specified domain
$activeRepos = array_filter($data->network, function ($repo) use ($excludeDomains) {
$parsed = $this->url->parse($repo->url);
return isset($repo->status) &&
$repo->status == 1 &&
!in_array($parsed->domain, $excludeDomains);
});
// Reindex the array to ensure array_rand works correctly
$activeRepos = array_values($activeRepos);
// If there are active repositories, select one at random
if (!empty($activeRepos))
{
return $activeRepos[array_rand($activeRepos)];
}
else
{
// No active repositories found excluding the specified domain
return null;
}
}
catch (\Exception $e)
{
// In case of any exception, return null
return null;
}
}
/**
* Retrieves the data for the given network target, utilizing caching via the Core registry.
*
* If the data for the target is already cached in the Core registry, it returns that data.
* Otherwise, it fetches the data from the Network, caches it, and returns it.
*
* @param string $target The target network name.
* @param string|null $domain The domain to retrieve [example: codeberg.org].
* @param string|null $organization The target repository organization.
* @param string|null $repository The repository name.
*
* @return object|null The data retrieved for the target.
* @throws \Exception If an error occurs during the network call or if the result contains an 'error' key.
* @since 5.0.4
*/
public function network(string $target, ?string $domain = null, ?string $organization = null, ?string $repository = null): ?object
{
$networkData = $this->fetchNetworkData($target);
if ($domain !== null)
{
return $this->getDomainData($networkData->network, $domain, $organization, $repository);
}
return $networkData;
}
/**
* Retrieves the data filtered by domain, organization, and optionally repository.
*
* @param array $network The network data array.
* @param string $domain The domain to filter by.
* @param string|null $organization The organization to filter by.
* @param string|null $repository The repository to filter by.
*
* @return object|null The filtered data, or null if no match is found.
* @since 5.0.4
*/
private function getDomainData(array $network, string $domain, ?string $organization = null, ?string $repository = null): ?object
{
$domainBase = $this->url->base($domain);
foreach ($network as $repo)
{
$parsedUrl = $this->url->parse($repo->url);
if ($parsedUrl->domain === $domainBase)
{
if ($organization !== null && $parsedUrl->organization !== $organization)
{
continue;
}
if ($repository !== null && $parsedUrl->repository !== $repository)
{
continue;
}
return $repo;
}
}
return null;
}
/**
* Fetches and caches the network data for a given target.
*
* @param string $target The target network name.
*
* @return object The cached or freshly fetched network data.
* @throws \Exception If an error occurs during the network call.
* @since 5.0.4
*/
private function fetchNetworkData(string $target): object
{
// Check if data is cached
if (($cachedData = $this->core->get($target)) !== null)
{
return $cachedData;
}
try {
// Fetch data from the network
$networkData = $this->network->get($target);
} catch (\Exception $e) {
throw new \Exception('Network error: ' . $e->getMessage(), 0, $e);
}
// Validate the fetched data
if (!is_object($networkData) || !property_exists($networkData, 'network'))
{
throw new \Exception('Invalid network data: Missing "network" property.');
}
if (property_exists($networkData, 'error'))
{
throw new \Exception('Network error: ' . $networkData->error);
}
// Cache the result
$this->core->set($target, $networkData);
return $networkData;
}
}

View File

@@ -0,0 +1,237 @@
<?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\Network;
use VDM\Joomla\Componentbuilder\Network\ParsedUrls;
/**
* The Network Url
*
* @since 5.0.4
*/
final class Url
{
/**
* The ParsedUrls Class.
*
* @var ParsedUrls
* @since 5.0.4
*/
protected ParsedUrls $parsedurls;
/**
* Constructor.
*
* @param ParsedUrls $parsedurls The ParsedUrls Class.
*
* @since 5.0.4
*/
public function __construct(ParsedUrls $parsedurls)
{
$this->parsedurls = $parsedurls;
}
/**
* Parses a URL and extracts the domain, organization, and repository.
*
* This method takes a URL of the format 'https://[domain]/[organization]/[repository]'
* and returns an associative array with keys 'domain', 'organization', and 'repository'.
*
* @param string $url The URL to parse.
*
* @return object An object with keys 'domain', 'organization', and 'repository'.
* @throws \InvalidArgumentException If the URL is invalid or lacks required components.
* @since 5.0.4
*/
public function parse(string $url): object
{
// Check if the URL has already been parsed and is present in the cache
if (($parsed = $this->parsedurls->get($url)) !== null)
{
return (object) $parsed;
}
// Validate the URL format
if (!filter_var($url, FILTER_VALIDATE_URL))
{
throw new \InvalidArgumentException("Invalid URL format: $url");
}
// Parse the URL and extract its components
$parsedUrl = parse_url($url);
if ($parsedUrl === false)
{
throw new \InvalidArgumentException("Invalid URL provided: $url");
}
// Ensure the URL contains a host (domain)
if (empty($parsedUrl['host']))
{
throw new \InvalidArgumentException("The URL does not contain a valid domain: $url");
}
$domain = $parsedUrl['host'];
// Set the scheme
$scheme = $parsedUrl['scheme'] ?? 'https';
// Ensure the URL contains a path
if (empty($parsedUrl['path']))
{
throw new \InvalidArgumentException("The URL does not contain a valid path: $url");
}
// Remove leading and trailing slashes from the path
$path = trim($parsedUrl['path'], '/');
// Split the path into components
$pathParts = explode('/', $path);
// Ensure the path contains at least two components: organization and repository
if (count($pathParts) < 2)
{
throw new \InvalidArgumentException("The URL must contain both an organization and a repository: $url");
}
$organization = $pathParts[0];
$repository = $pathParts[1];
// Create a new Parsed Url array
$parsed = [
'scheme' => $scheme,
'domain' => $domain,
'organization' => $organization,
'repository' => $repository
];
// Store the parsed URL in the cache
$this->parsedurls->set($url, $parsed);
// Return the parsed URL object
return (object) $parsed;
}
/**
* Extract the base domain from a given URL or domain string.
*
* @param string $url The input URL or domain string.
*
* @return string The core domain (e.g., domain.com).
* @since 5.0.4
*/
public function base(string $url): string
{
// Parse the URL to extract host
$parsedUrl = parse_url($url, PHP_URL_HOST);
// If no host is found, check if the input itself is a domain without protocol
if (!$parsedUrl)
{
$parsedUrl = $url;
}
// Remove any trailing slashes or unnecessary characters
$parsedUrl = rtrim($parsedUrl, '/');
return $parsedUrl;
}
/**
* Compares two URLs and checks if their domain and repository are the same.
*
* This method returns true if both the domain and repository are identical in both URLs.
*
* @param string $url1 The first URL to compare.
* @param string $url2 The second URL to compare.
*
* @return bool Returns true if the domain and repository are the same; false otherwise.
* @throws InvalidArgumentException If any of the URLs are invalid or lack required components.
* @since 5.0.4
*/
public function equal(string $url1, string $url2): bool
{
return $this->compare($url1, $url2, ['domain', 'repository']);
}
/**
* Compares two URLs strictly and checks if their domain, organization, and repository are the same.
*
* This method returns true if the domain, organization, and repository are identical in both URLs.
*
* @param string $url1 The first URL to compare.
* @param string $url2 The second URL to compare.
*
* @return bool Returns true if the domain, organization, and repository are the same; false otherwise.
* @throws \InvalidArgumentException If any of the URLs are invalid or lack required components.
* @since 5.0.4
*/
public function equalStrict(string $url1, string $url2): bool
{
return $this->compare($url1, $url2, ['domain', 'organization', 'repository']);
}
/**
* Compares two URLs and checks if their repositories are the same.
*
* This method returns true if the repository names are identical in both URLs, regardless of domain and organization.
*
* @param string $url1 The first URL to compare.
* @param string $url2 The second URL to compare.
*
* @return bool Returns true if the repositories are the same; false otherwise.
* @throws \InvalidArgumentException If any of the URLs are invalid or lack required components.
* @since 5.0.4
*/
public function equalRepo(string $url1, string $url2): bool
{
return $this->compare($url1, $url2, ['repository']);
}
/**
* Compares two URLs based on specified fields.
*
* This method allows you to compare specific components of two URLs, such as 'domain',
* 'organization', and 'repository'. It returns true if all specified fields are equal.
*
* @param string $url1 The first URL to compare.
* @param string $url2 The second URL to compare.
* @param string[] $fields The fields to compare ('domain', 'organization', 'repository').
*
* @return bool Returns true if all specified fields are equal; false otherwise.
* @throws \InvalidArgumentException If any of the URLs are invalid or lack required components,
* or if an invalid field is specified.
* @since 5.0.4
*/
private function compare(string $url1, string $url2, array $fields): bool
{
$parsedUrl1 = $this->parse($url1);
$parsedUrl2 = $this->parse($url2);
foreach ($fields as $field)
{
if (!property_exists($parsedUrl1, $field) || !property_exists($parsedUrl2, $field))
{
throw new \InvalidArgumentException("Invalid field specified for comparison: $field");
}
if ($parsedUrl1->$field !== $parsedUrl2->$field)
{
return false;
}
}
return true;
}
}

View File

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