Release of v4.1.1-beta2
Adds new JCB package engine. Fix issue with loading the Component Builder Wiki. Adds advanced version update notice to the Component Builder Dashboard. Completely refactors the class that builds the Component Dashboard. #1134. Adds Initialize, Reset, and Push functionality to the Repository entities. Completely refactors the SQL teaks and SQL dump classes. Changes J4 fields to allow NULL. Fix a bug in Dynamic Get JavaScript that causes table columns to not load.
This commit is contained in:
367
libraries/vendor_jcb/VDM.Joomla.Git/src/Repository/Contents.php
Normal file
367
libraries/vendor_jcb/VDM.Joomla.Git/src/Repository/Contents.php
Normal file
@@ -0,0 +1,367 @@
|
||||
<?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\Git\Repository;
|
||||
|
||||
|
||||
use VDM\Joomla\Gitea\Repository\Contents as Gitea;
|
||||
use VDM\Joomla\Github\Repository\Contents as Github;
|
||||
use VDM\Joomla\Interfaces\Git\Repository\ContentsInterface;
|
||||
|
||||
|
||||
/**
|
||||
* The Git Repository Contents
|
||||
*
|
||||
* @since 5.1.1
|
||||
*/
|
||||
final class Contents implements ContentsInterface
|
||||
{
|
||||
/**
|
||||
* The target system
|
||||
*
|
||||
* @var string
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected string $target;
|
||||
|
||||
/**
|
||||
* The Contents Class.
|
||||
*
|
||||
* @var Gitea
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected Gitea $gitea;
|
||||
|
||||
/**
|
||||
* The Contents Class.
|
||||
*
|
||||
* @var Github
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected Github $github;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Gitea $gitea The Contents Class.
|
||||
* @param Github $github The Contents Class.
|
||||
*
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function __construct(Gitea $gitea, Github $github)
|
||||
{
|
||||
$this->gitea = $gitea;
|
||||
$this->github = $github;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the target system to use (gitea or github)
|
||||
*
|
||||
* @param string $system
|
||||
*
|
||||
* @return void
|
||||
* @throws \DomainException
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function setTarget(string $system): void
|
||||
{
|
||||
$system = strtolower(trim($system));
|
||||
|
||||
if (!in_array($system, ['gitea', 'github'], true))
|
||||
{
|
||||
throw new \DomainException("Invalid target system: {$system}");
|
||||
}
|
||||
|
||||
$this->target = $system;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure a valid target is set
|
||||
*
|
||||
* @return string
|
||||
* @throws \DomainException
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected function getTarget(): string
|
||||
{
|
||||
if ($this->target === null)
|
||||
{
|
||||
throw new \DomainException('No target system selected. Use $this->target("gitea"|"github") before calling this method.');
|
||||
}
|
||||
return $this->target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load/Reload API.
|
||||
*
|
||||
* @param string|null $url The url.
|
||||
* @param string|null $token The token.
|
||||
* @param bool $backup The backup swapping switch.
|
||||
*
|
||||
* @return void
|
||||
* @since 5.1.1
|
||||
**/
|
||||
public function load_(?string $url = null, ?string $token = null, bool $backup = true): void
|
||||
{
|
||||
$target = $this->getTarget();
|
||||
$this->{$target}->load_($url, $token, $backup);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset to previous toke, url it set
|
||||
*
|
||||
* @return void
|
||||
* @since 5.1.1
|
||||
**/
|
||||
public function reset_(): void
|
||||
{
|
||||
$target = $this->getTarget();
|
||||
$this->{$target}->reset_();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the API url
|
||||
*
|
||||
* @return string
|
||||
* @since 5.1.1
|
||||
**/
|
||||
public function api()
|
||||
{
|
||||
$target = $this->getTarget();
|
||||
$this->{$target}->api();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a file from a repository.
|
||||
*
|
||||
* @param string $owner The owner name.
|
||||
* @param string $repo The repository name.
|
||||
* @param string $filepath The file path.
|
||||
* @param string|null $ref Optional. The name of the commit/branch/tag.
|
||||
* Default the repository's default branch (usually master).
|
||||
*
|
||||
* @return mixed
|
||||
* @since 5.1.1
|
||||
**/
|
||||
public function get(string $owner, string $repo, string $filepath, ?string $ref = null)
|
||||
{
|
||||
$target = $this->getTarget();
|
||||
return $this->{$target}->get($owner, $repo, $filepath, $ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the metadata and contents (if a file) of an entry in a repository,
|
||||
* or a list of entries if a directory.
|
||||
*
|
||||
* @param string $owner The owner name.
|
||||
* @param string $repo The repository name.
|
||||
* @param string $filepath The file or directory path.
|
||||
* @param string|null $ref Optional. The name of the commit/branch/tag.
|
||||
* Default the repository's default branch (usually master).
|
||||
*
|
||||
* @return null|array|object
|
||||
* @since 5.1.1
|
||||
**/
|
||||
public function metadata(string $owner, string $repo, string $filepath, ?string $ref = null): null|array|object
|
||||
{
|
||||
$target = $this->getTarget();
|
||||
return $this->{$target}->metadata($owner, $repo, $filepath, $ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a file in a repository.
|
||||
*
|
||||
* @param string $owner The owner name.
|
||||
* @param string $repo The repository name.
|
||||
* @param string $filepath The file path.
|
||||
* @param string $content The file content.
|
||||
* @param string $message The commit message.
|
||||
* @param string $branch The branch name. Defaults to the repository's default branch.
|
||||
* @param string|null $authorName The author's name.
|
||||
* @param string|null $authorEmail The author's email.
|
||||
* @param string|null $committerName The committer's name.
|
||||
* @param string|null $committerEmail The committer's email.
|
||||
* @param string|null $newBranch Whether to create a new branch. Defaults to null.
|
||||
* @param string|null $authorDate The author's date.
|
||||
* @param string|null $committerDate The committer's date.
|
||||
* @param bool|null $signoff Add a Signed-off-by trailer. Defaults to null.
|
||||
*
|
||||
* @return object|null
|
||||
* @since 5.1.1
|
||||
**/
|
||||
public function create(
|
||||
string $owner,
|
||||
string $repo,
|
||||
string $filepath,
|
||||
string $content,
|
||||
string $message,
|
||||
string $branch = 'master',
|
||||
?string $authorName = null,
|
||||
?string $authorEmail = null,
|
||||
?string $committerName = null,
|
||||
?string $committerEmail = null,
|
||||
?string $newBranch = null,
|
||||
?string $authorDate = null,
|
||||
?string $committerDate = null,
|
||||
?bool $signoff = null
|
||||
): ?object {
|
||||
$target = $this->getTarget();
|
||||
return $this->{$target}->create(
|
||||
$owner, $repo, $filepath, $content, $message, $branch,
|
||||
$authorName, $authorEmail, $committerName, $committerEmail,
|
||||
$newBranch, $authorDate, $committerDate, $signoff
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the metadata of all the entries of the root directory.
|
||||
*
|
||||
* @param string $owner The owner name.
|
||||
* @param string $repo The repository name.
|
||||
* @param string|null $ref The name of the commit/branch/tag. Default the repository's default branch (usually master).
|
||||
*
|
||||
* @return array|null
|
||||
* @since 5.1.1
|
||||
**/
|
||||
public function root(string $owner, string $repo, ?string $ref = null): ?array
|
||||
{
|
||||
$target = $this->getTarget();
|
||||
return $this->{$target}->root($owner, $repo, $ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a file in a repository.
|
||||
*
|
||||
* @param string $owner The owner name.
|
||||
* @param string $repo The repository name.
|
||||
* @param string $filepath The file path.
|
||||
* @param string $content The file content.
|
||||
* @param string $message The commit message.
|
||||
* @param string $sha The blob SHA of the file.
|
||||
* @param string $branch The branch name. Defaults to the repository's default branch.
|
||||
* @param string|null $authorName The author name. Defaults to the authenticated user.
|
||||
* @param string|null $authorEmail The author email. Defaults to the authenticated user.
|
||||
* @param string|null $committerName The committer name. Defaults to the authenticated user.
|
||||
* @param string|null $committerEmail The committer email. Defaults to the authenticated user.
|
||||
* @param string|null $authorDate The author date.
|
||||
* @param string|null $committerDate The committer date.
|
||||
* @param string|null $fromPath The original file path to move/rename.
|
||||
* @param string|null $newBranch The new branch to create from the specified branch.
|
||||
* @param bool|null $signoff Add a Signed-off-by trailer.
|
||||
*
|
||||
* @return object|null
|
||||
* @since 5.1.1
|
||||
**/
|
||||
public function update(
|
||||
string $owner,
|
||||
string $repo,
|
||||
string $filepath,
|
||||
string $content,
|
||||
string $message,
|
||||
string $sha,
|
||||
string $branch = 'master',
|
||||
?string $authorName = null,
|
||||
?string $authorEmail = null,
|
||||
?string $committerName = null,
|
||||
?string $committerEmail = null,
|
||||
?string $authorDate = null,
|
||||
?string $committerDate = null,
|
||||
?string $fromPath = null,
|
||||
?string $newBranch = null,
|
||||
?bool $signoff = null
|
||||
): ?object {
|
||||
$target = $this->getTarget();
|
||||
return $this->{$target}->update(
|
||||
$owner, $repo, $filepath, $content, $message, $sha, $branch,
|
||||
$authorName, $authorEmail, $committerName, $committerEmail,
|
||||
$authorDate, $committerDate, $fromPath, $newBranch, $signoff
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a file in a repository.
|
||||
*
|
||||
* @param string $owner The owner name.
|
||||
* @param string $repo The repository name.
|
||||
* @param string $filepath The file path.
|
||||
* @param string $message The commit message.
|
||||
* @param string $sha The blob SHA of the file.
|
||||
* @param string|null $branch The branch name (optional).
|
||||
* @param string|null $authorName The author name (optional).
|
||||
* @param string|null $authorEmail The author email (optional).
|
||||
* @param string|null $committerName The committer name (optional).
|
||||
* @param string|null $committerEmail The committer email (optional).
|
||||
* @param string|null $authorDate The author date (optional).
|
||||
* @param string|null $committerDate The committer date (optional).
|
||||
* @param string|null $newBranch The new branch name (optional).
|
||||
* @param bool|null $signoff Add a Signed-off-by trailer (optional).
|
||||
*
|
||||
* @return object|null
|
||||
* @since 5.1.1
|
||||
**/
|
||||
public function delete(
|
||||
string $owner,
|
||||
string $repo,
|
||||
string $filepath,
|
||||
string $message,
|
||||
string $sha,
|
||||
?string $branch = null,
|
||||
?string $authorName = null,
|
||||
?string $authorEmail = null,
|
||||
?string $committerName = null,
|
||||
?string $committerEmail = null,
|
||||
?string $authorDate = null,
|
||||
?string $committerDate = null,
|
||||
?string $newBranch = null,
|
||||
?bool $signoff = null
|
||||
): ?object {
|
||||
$target = $this->getTarget();
|
||||
return $this->{$target}->delete(
|
||||
$owner, $repo, $filepath, $message, $sha, $branch,
|
||||
$authorName, $authorEmail, $committerName, $committerEmail,
|
||||
$authorDate, $committerDate, $newBranch, $signoff
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the EditorConfig definitions of a file in a repository.
|
||||
*
|
||||
* @param string $owner The owner name.
|
||||
* @param string $repo The repository name.
|
||||
* @param string $filepath The file path.
|
||||
* @param string|null $ref The name of the commit/branch/tag.
|
||||
*
|
||||
* @return string|null
|
||||
* @since 5.1.1
|
||||
**/
|
||||
public function editor(string $owner, string $repo, string $filepath, string $ref = null): ?string
|
||||
{
|
||||
$target = $this->getTarget();
|
||||
return $this->{$target}->editor($owner, $repo, $filepath, $ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the blob of a repository.
|
||||
*
|
||||
* @param string $owner The owner name.
|
||||
* @param string $repo The repository name.
|
||||
* @param string $sha The SHA hash of the blob.
|
||||
*
|
||||
* @return object|null
|
||||
* @since 5.1.1
|
||||
**/
|
||||
public function blob(string $owner, string $repo, string $sha): ?object
|
||||
{
|
||||
$target = $this->getTarget();
|
||||
return $this->{$target}->blob($owner, $repo, $sha);
|
||||
}
|
||||
}
|
||||
|
@@ -154,7 +154,7 @@ class Issue extends Api
|
||||
return $this->response->get(
|
||||
$this->http->post(
|
||||
$this->uri->get($path), json_encode($data)
|
||||
)
|
||||
), 201
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -12,6 +12,7 @@
|
||||
namespace VDM\Joomla\Gitea\Repository;
|
||||
|
||||
|
||||
use VDM\Joomla\Interfaces\Git\Repository\TagsInterface;
|
||||
use VDM\Joomla\Gitea\Abstraction\Api;
|
||||
|
||||
|
||||
@@ -20,7 +21,7 @@ use VDM\Joomla\Gitea\Abstraction\Api;
|
||||
*
|
||||
* @since 3.2.0
|
||||
*/
|
||||
class Tags extends Api
|
||||
class Tags extends Api implements TagsInterface
|
||||
{
|
||||
/**
|
||||
* List a repository's tags
|
||||
|
@@ -12,6 +12,7 @@
|
||||
namespace VDM\Joomla\Gitea\Repository;
|
||||
|
||||
|
||||
use VDM\Joomla\Interfaces\Git\Repository\WikiInterface;
|
||||
use VDM\Joomla\Gitea\Abstraction\Api;
|
||||
|
||||
|
||||
@@ -20,16 +21,16 @@ use VDM\Joomla\Gitea\Abstraction\Api;
|
||||
*
|
||||
* @since 3.2.0
|
||||
*/
|
||||
class Wiki extends Api
|
||||
class Wiki extends Api implements WikiInterface
|
||||
{
|
||||
/**
|
||||
* Create a wiki page.
|
||||
*
|
||||
* @param string $owner The owner name.
|
||||
* @param string $repo The repository name.
|
||||
* @param string $title The title of the wiki page.
|
||||
* @param string $contentBase64 The base64 encoded content of the wiki page.
|
||||
* @param string|null $message Optional commit message summarizing the change.
|
||||
* @param string $owner The owner name.
|
||||
* @param string $repo The repository name.
|
||||
* @param string $title The title of the wiki page.
|
||||
* @param string $contentBase64 The base64 encoded content of the wiki page.
|
||||
* @param string|null $message Optional commit message summarizing the change.
|
||||
*
|
||||
* @return object|null
|
||||
* @since 3.2.0
|
||||
@@ -67,9 +68,9 @@ class Wiki extends Api
|
||||
/**
|
||||
* Get a wiki page.
|
||||
*
|
||||
* @param string $owner The owner name.
|
||||
* @param string $repo The repository name.
|
||||
* @param string $pageName The name of the wiki page.
|
||||
* @param string $owner The owner name.
|
||||
* @param string $repo The repository name.
|
||||
* @param string $pageName The name of the wiki page.
|
||||
*
|
||||
* @return object|null
|
||||
* @since 3.2.0
|
||||
@@ -95,10 +96,10 @@ class Wiki extends Api
|
||||
/**
|
||||
* Get all wiki pages.
|
||||
*
|
||||
* @param string $owner The owner name.
|
||||
* @param string $repo The repository name.
|
||||
* @param int $page Page number of results to return (1-based).
|
||||
* @param int $limit Page size of results.
|
||||
* @param string $owner The owner name.
|
||||
* @param string $repo The repository name.
|
||||
* @param int $page Page number of results to return (1-based).
|
||||
* @param int $limit Page size of results.
|
||||
*
|
||||
* @return array|null
|
||||
* @since 3.2.0
|
||||
@@ -127,9 +128,9 @@ class Wiki extends Api
|
||||
/**
|
||||
* Delete a wiki page.
|
||||
*
|
||||
* @param string $owner The owner name.
|
||||
* @param string $repo The repository name.
|
||||
* @param string $pageName The name of the wiki page.
|
||||
* @param string $owner The owner name.
|
||||
* @param string $repo The repository name.
|
||||
* @param string $pageName The name of the wiki page.
|
||||
*
|
||||
* @return string
|
||||
* @since 3.2.0
|
||||
@@ -155,12 +156,12 @@ class Wiki extends Api
|
||||
/**
|
||||
* Edit a wiki page.
|
||||
*
|
||||
* @param string $owner The owner name.
|
||||
* @param string $repo The repository name.
|
||||
* @param string $pageName The name of the wiki page.
|
||||
* @param string $title The new title of the wiki page.
|
||||
* @param string $content The new content of the wiki page.
|
||||
* @param string $message The optional commit message summarizing the change.
|
||||
* @param string $owner The owner name.
|
||||
* @param string $repo The repository name.
|
||||
* @param string $pageName The name of the wiki page.
|
||||
* @param string $title The new title of the wiki page.
|
||||
* @param string $content The new content of the wiki page.
|
||||
* @param string $message The optional commit message summarizing the change.
|
||||
*
|
||||
* @return object|null
|
||||
* @since 3.2.0
|
||||
@@ -199,10 +200,10 @@ class Wiki extends Api
|
||||
/**
|
||||
* Get revisions of a wiki page.
|
||||
*
|
||||
* @param string $owner The owner name.
|
||||
* @param string $repo The repository name.
|
||||
* @param string $pageName The name of the wiki page.
|
||||
* @param int $page The page number of results to return (1-based).
|
||||
* @param string $owner The owner name.
|
||||
* @param string $repo The repository name.
|
||||
* @param string $pageName The name of the wiki page.
|
||||
* @param int $page The page number of results to return (1-based).
|
||||
*
|
||||
* @return object|null
|
||||
* @since 3.2.0
|
||||
@@ -226,7 +227,6 @@ class Wiki extends Api
|
||||
$this->uri->get($path)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -13,7 +13,6 @@ namespace VDM\Joomla\Gitea\Utilities;
|
||||
|
||||
|
||||
use Joomla\CMS\Http\Http as JoomlaHttp;
|
||||
use Joomla\CMS\Uri\Uri;
|
||||
use Joomla\Registry\Registry;
|
||||
|
||||
|
||||
|
@@ -12,7 +12,7 @@
|
||||
namespace VDM\Joomla\Gitea\Utilities;
|
||||
|
||||
|
||||
use Joomla\CMS\Http\Response as JoomlaResponse;
|
||||
use Joomla\Http\Response as JoomlaResponse;
|
||||
use VDM\Joomla\Utilities\JsonHelper;
|
||||
use VDM\Joomla\Utilities\StringHelper;
|
||||
|
||||
@@ -36,7 +36,7 @@ final class Response
|
||||
* @since 3.2.0
|
||||
* @throws \DomainException
|
||||
**/
|
||||
public function get($response, int $expectedCode = 200, $default = null)
|
||||
public function get(JoomlaResponse $response, int $expectedCode = 200, $default = null)
|
||||
{
|
||||
// Validate the response code.
|
||||
if ($response->code != $expectedCode)
|
||||
@@ -44,8 +44,7 @@ final class Response
|
||||
// Decode the error response and throw an exception.
|
||||
$message = $this->error($response);
|
||||
|
||||
throw new \DomainException("Invalid response received from API. $message", $response->code);
|
||||
|
||||
throw new \DomainException("Invalid response received from Gitea API. $message", $response->code);
|
||||
}
|
||||
|
||||
return $this->body($response, $default);
|
||||
@@ -62,7 +61,7 @@ final class Response
|
||||
* @since 3.2.0
|
||||
* @throws \DomainException
|
||||
**/
|
||||
public function get_($response, array $validate = [200 => null])
|
||||
public function get_(JoomlaResponse $response, array $validate = [200 => null])
|
||||
{
|
||||
// Validate the response code.
|
||||
if (!isset($validate[$response->code]))
|
||||
@@ -70,8 +69,7 @@ final class Response
|
||||
// Decode the error response and throw an exception.
|
||||
$message = $this->error($response);
|
||||
|
||||
throw new \DomainException("Invalid response received from API. $message", $response->code);
|
||||
|
||||
throw new \DomainException("Invalid response received from Gitea API. $message", $response->code);
|
||||
}
|
||||
|
||||
return $this->body($response, $validate[$response->code]);
|
||||
@@ -86,11 +84,14 @@ final class Response
|
||||
* @return mixed
|
||||
* @since 3.2.0
|
||||
**/
|
||||
protected function body($response, $default = null)
|
||||
protected function body(JoomlaResponse $response, $default = null)
|
||||
{
|
||||
$body = $response->body ?? null;
|
||||
$body = is_object($response) && method_exists($response, 'getBody')
|
||||
? (string) $response->getBody()
|
||||
: (isset($response->body) ? (string) $response->body : null);
|
||||
|
||||
// check that we have a body
|
||||
if (StringHelper::check($body))
|
||||
if ($body !== null && StringHelper::check($body))
|
||||
{
|
||||
if (JsonHelper::check($body))
|
||||
{
|
||||
@@ -109,36 +110,51 @@ final class Response
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the error message from the return object
|
||||
* Extract an error message from a response object.
|
||||
*
|
||||
* @param JoomlaResponse $response The response.
|
||||
* @param JoomlaResponse $response The response object.
|
||||
*
|
||||
* @return string
|
||||
* @return string The extracted error message, or an empty string.
|
||||
* @since 3.2.0
|
||||
**/
|
||||
protected function error($response): string
|
||||
*/
|
||||
protected function error(JoomlaResponse $response): string
|
||||
{
|
||||
// do we have a json string
|
||||
if (isset($response->body) && JsonHelper::check($response->body))
|
||||
// Try to get the raw response body
|
||||
$body = method_exists($response, 'getBody') ? (string) $response->getBody() : '';
|
||||
|
||||
// Try to decode as JSON object
|
||||
$errorData = JsonHelper::check($body) ? json_decode($body) : null;
|
||||
|
||||
if (is_object($errorData))
|
||||
{
|
||||
$error = json_decode($response->body);
|
||||
}
|
||||
else
|
||||
{
|
||||
return '';
|
||||
// Try to extract a useful error field
|
||||
if (!empty($errorData->error))
|
||||
{
|
||||
return $errorData->error;
|
||||
}
|
||||
|
||||
if (!empty($errorData->message))
|
||||
{
|
||||
return $errorData->message;
|
||||
}
|
||||
|
||||
// Fallback to a serialized message
|
||||
return json_encode($errorData, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
// check
|
||||
if (isset($error->error))
|
||||
// Fallback to reason phrase or body
|
||||
if (!empty($body))
|
||||
{
|
||||
return $error->error;
|
||||
}
|
||||
elseif (isset($error->message))
|
||||
{
|
||||
return $error->message;
|
||||
return $body;
|
||||
}
|
||||
|
||||
return '';
|
||||
// Try getting the reason phrase from response
|
||||
if (method_exists($response, 'getReasonPhrase'))
|
||||
{
|
||||
return $response->getReasonPhrase();
|
||||
}
|
||||
|
||||
return 'No error information found in Gitea API response.';
|
||||
}
|
||||
}
|
||||
|
||||
|
158
libraries/vendor_jcb/VDM.Joomla.Github/src/Abstraction/Api.php
Normal file
158
libraries/vendor_jcb/VDM.Joomla.Github/src/Abstraction/Api.php
Normal file
@@ -0,0 +1,158 @@
|
||||
<?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\Github\Abstraction;
|
||||
|
||||
|
||||
use VDM\Joomla\Github\Utilities\Http;
|
||||
use VDM\Joomla\Github\Utilities\Uri;
|
||||
use VDM\Joomla\Github\Utilities\Response;
|
||||
use VDM\Joomla\Interfaces\Git\ApiInterface;
|
||||
|
||||
|
||||
/**
|
||||
* The Github Api
|
||||
*
|
||||
* @since 5.1.1
|
||||
*/
|
||||
abstract class Api implements ApiInterface
|
||||
{
|
||||
/**
|
||||
* The Http class
|
||||
*
|
||||
* @var Http
|
||||
* @since 3.2.0
|
||||
*/
|
||||
protected Http $http;
|
||||
|
||||
/**
|
||||
* The Uri class
|
||||
*
|
||||
* @var Uri
|
||||
* @since 3.2.0
|
||||
*/
|
||||
protected Uri $uri;
|
||||
|
||||
/**
|
||||
* The Response class
|
||||
*
|
||||
* @var Response
|
||||
* @since 3.2.0
|
||||
*/
|
||||
protected Response $response;
|
||||
|
||||
/**
|
||||
* The Url string
|
||||
*
|
||||
* @var string|null
|
||||
* @since 3.2.0
|
||||
*/
|
||||
protected ?string $url = null;
|
||||
|
||||
/**
|
||||
* The token string
|
||||
*
|
||||
* @var string|null
|
||||
* @since 3.2.0
|
||||
*/
|
||||
protected ?string $token = null;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Http $http The http class.
|
||||
* @param Uri $uri The uri class.
|
||||
* @param Response $response The response class.
|
||||
*
|
||||
* @since 3.2.0
|
||||
**/
|
||||
public function __construct(Http $http, Uri $uri, Response $response)
|
||||
{
|
||||
$this->http = $http;
|
||||
$this->uri = $uri;
|
||||
$this->response = $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load/Reload API.
|
||||
*
|
||||
* @param string|null $url The url.
|
||||
* @param token|null $token The token.
|
||||
* @param bool $backup The backup swapping switch.
|
||||
*
|
||||
* @return void
|
||||
* @since 3.2.0
|
||||
**/
|
||||
public function load_(?string $url = null, ?string $token = null, bool $backup = true): void
|
||||
{
|
||||
// we keep the old values
|
||||
// so we can reset after our call
|
||||
// for the rest of the container
|
||||
if ($backup)
|
||||
{
|
||||
// Github has only one URL
|
||||
// if ($url !== null)
|
||||
// {
|
||||
// $this->url = $this->uri->getUrl();
|
||||
// }
|
||||
|
||||
if ($token !== null)
|
||||
{
|
||||
$this->token = $this->http->getToken();
|
||||
}
|
||||
}
|
||||
|
||||
// Github has only one URL
|
||||
// if ($url !== null)
|
||||
// {
|
||||
// $this->uri->setUrl($url);
|
||||
// }
|
||||
|
||||
if ($token !== null)
|
||||
{
|
||||
$this->http->setToken($token);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset to previous toke, url it set
|
||||
*
|
||||
* @return void
|
||||
* @since 3.2.0
|
||||
**/
|
||||
public function reset_(): void
|
||||
{
|
||||
// Github has only one URL
|
||||
// if ($this->url !== null)
|
||||
// {
|
||||
// $this->uri->setUrl($this->url);
|
||||
// $this->url = null;
|
||||
// }
|
||||
|
||||
if ($this->token !== null)
|
||||
{
|
||||
$this->http->setToken($this->token);
|
||||
$this->token = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the API url
|
||||
*
|
||||
* @return string
|
||||
* @since 3.2.0
|
||||
**/
|
||||
public function api()
|
||||
{
|
||||
return $this->uri->api();
|
||||
}
|
||||
}
|
||||
|
51
libraries/vendor_jcb/VDM.Joomla.Github/src/Factory.php
Normal file
51
libraries/vendor_jcb/VDM.Joomla.Github/src/Factory.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?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\Github;
|
||||
|
||||
|
||||
use Joomla\DI\Container;
|
||||
use VDM\Joomla\Github\Service\Utilities;
|
||||
use VDM\Joomla\Componentbuilder\Power\Service\Github;
|
||||
use VDM\Joomla\Interfaces\FactoryInterface;
|
||||
use VDM\Joomla\Abstraction\Factory as ExtendingFactory;
|
||||
|
||||
|
||||
/**
|
||||
* Github Factory
|
||||
*
|
||||
* @since 5.1.1
|
||||
*/
|
||||
abstract class Factory extends ExtendingFactory implements FactoryInterface
|
||||
{
|
||||
/**
|
||||
* Package Container
|
||||
*
|
||||
* @var Container|null
|
||||
* @since 5.0.3
|
||||
**/
|
||||
protected static ?Container $container = null;
|
||||
|
||||
/**
|
||||
* Create a container object
|
||||
*
|
||||
* @return Container
|
||||
* @since 3.2.0
|
||||
*/
|
||||
protected static function createContainer(): Container
|
||||
{
|
||||
return (new Container())
|
||||
->registerServiceProvider(new Utilities())
|
||||
->registerServiceProvider(new Github());
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,482 @@
|
||||
<?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\Github\Repository;
|
||||
|
||||
|
||||
use VDM\Joomla\Interfaces\Git\Repository\ContentsInterface;
|
||||
use VDM\Joomla\Github\Abstraction\Api;
|
||||
|
||||
|
||||
/**
|
||||
* The Github Repository Contents
|
||||
*
|
||||
* @since 5.1.1
|
||||
*/
|
||||
final class Contents extends Api implements ContentsInterface
|
||||
{
|
||||
/**
|
||||
* Get a file from a repository.
|
||||
*
|
||||
* @param string $owner The owner name.
|
||||
* @param string $repo The repository name.
|
||||
* @param string $filepath The file path.
|
||||
* @param string|null $ref Optional. The name of the commit/branch/tag.
|
||||
* Default the repository's default branch (usually master).
|
||||
*
|
||||
* @return mixed
|
||||
* @since 5.1.1
|
||||
**/
|
||||
public function get(string $owner, string $repo, string $filepath, ?string $ref = null)
|
||||
{
|
||||
// Build the request path.
|
||||
$path = "/repos/{$owner}/{$repo}/contents/{$filepath}";
|
||||
|
||||
// Get the URI with the specified path.
|
||||
$uri = $this->uri->get($path);
|
||||
|
||||
// Add the ref parameter if provided.
|
||||
if ($ref !== null)
|
||||
{
|
||||
$uri->setVar('ref', $ref);
|
||||
}
|
||||
|
||||
// Send the get request.
|
||||
return $this->response->get(
|
||||
$this->http->raw()->get($uri)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the metadata and contents (if a file) of an entry in a repository,
|
||||
* or a list of entries if a directory.
|
||||
*
|
||||
* @param string $owner The owner name.
|
||||
* @param string $repo The repository name.
|
||||
* @param string $filepath The file or directory path.
|
||||
* @param string|null $ref Optional. The name of the commit/branch/tag.
|
||||
* Default the repository's default branch (usually master).
|
||||
*
|
||||
* @return null|array|object
|
||||
* @since 5.1.1
|
||||
**/
|
||||
public function metadata(string $owner, string $repo, string $filepath, ?string $ref = null): null|array|object
|
||||
{
|
||||
// Build the request path.
|
||||
$path = "/repos/{$owner}/{$repo}/contents/{$filepath}";
|
||||
|
||||
// Get the URI with the specified path.
|
||||
$uri = $this->uri->get($path);
|
||||
|
||||
// Add the ref parameter if provided.
|
||||
if ($ref !== null)
|
||||
{
|
||||
$uri->setVar('ref', $ref);
|
||||
}
|
||||
|
||||
// Send the get request.
|
||||
return $this->response->get(
|
||||
$this->http->json()->get($uri)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a file in a repository.
|
||||
*
|
||||
* @param string $owner The owner name.
|
||||
* @param string $repo The repository name.
|
||||
* @param string $filepath The file path.
|
||||
* @param string $content The file content.
|
||||
* @param string $message The commit message.
|
||||
* @param string $branch The branch name. Defaults to the repository's default branch.
|
||||
* @param string|null $authorName The author's name.
|
||||
* @param string|null $authorEmail The author's email.
|
||||
* @param string|null $committerName The committer's name.
|
||||
* @param string|null $committerEmail The committer's email.
|
||||
* @param string|null $newBranch Whether to create a new branch. Defaults to null.
|
||||
* @param string|null $authorDate The author's date.
|
||||
* @param string|null $committerDate The committer's date.
|
||||
* @param bool|null $signoff Add a Signed-off-by trailer. Defaults to null.
|
||||
*
|
||||
* @return object|null
|
||||
* @since 5.1.1
|
||||
**/
|
||||
public function create(
|
||||
string $owner,
|
||||
string $repo,
|
||||
string $filepath,
|
||||
string $content,
|
||||
string $message,
|
||||
string $branch = 'master',
|
||||
?string $authorName = null,
|
||||
?string $authorEmail = null,
|
||||
?string $committerName = null,
|
||||
?string $committerEmail = null,
|
||||
?string $newBranch = null,
|
||||
?string $authorDate = null,
|
||||
?string $committerDate = null,
|
||||
?bool $signoff = null
|
||||
): ?object {
|
||||
// Build the request path.
|
||||
$path = "/repos/{$owner}/{$repo}/contents/{$filepath}";
|
||||
|
||||
// Set the post data
|
||||
$data = new \stdClass();
|
||||
$data->content = base64_encode($content);
|
||||
$data->message = $message;
|
||||
$data->branch = $branch;
|
||||
|
||||
if ($authorName !== null || $authorEmail !== null)
|
||||
{
|
||||
$data->author = new \stdClass();
|
||||
if ($authorName !== null)
|
||||
{
|
||||
$data->author->name = $authorName;
|
||||
}
|
||||
if ($authorEmail !== null)
|
||||
{
|
||||
$data->author->email = $authorEmail;
|
||||
}
|
||||
}
|
||||
|
||||
if ($committerName !== null || $committerEmail !== null)
|
||||
{
|
||||
$data->committer = new \stdClass();
|
||||
if ($committerName !== null)
|
||||
{
|
||||
$data->committer->name = $committerName;
|
||||
}
|
||||
if ($committerEmail !== null)
|
||||
{
|
||||
$data->committer->email = $committerEmail;
|
||||
}
|
||||
}
|
||||
|
||||
if ($newBranch !== null)
|
||||
{
|
||||
$data->new_branch = $newBranch;
|
||||
}
|
||||
|
||||
if ($authorDate !== null || $committerDate !== null)
|
||||
{
|
||||
$data->dates = new \stdClass();
|
||||
if ($authorDate !== null)
|
||||
{
|
||||
$data->dates->author = $authorDate;
|
||||
}
|
||||
if ($committerDate !== null)
|
||||
{
|
||||
$data->dates->committer = $committerDate;
|
||||
}
|
||||
}
|
||||
|
||||
if ($signoff !== null)
|
||||
{
|
||||
$data->signoff = $signoff;
|
||||
}
|
||||
|
||||
// Send the post request.
|
||||
return $this->response->get(
|
||||
$this->http->json()->put(
|
||||
$this->uri->get($path), json_encode($data)
|
||||
), 201
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the metadata of all the entries of the root directory.
|
||||
*
|
||||
* @param string $owner The owner name.
|
||||
* @param string $repo The repository name.
|
||||
* @param string|null $ref The name of the commit/branch/tag. Default the repository's default branch (usually master).
|
||||
*
|
||||
* @return array|null
|
||||
* @since 5.1.1
|
||||
**/
|
||||
public function root(string $owner, string $repo, ?string $ref = null): ?array
|
||||
{
|
||||
return $this->metadata($owner, $repo, '', $ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a file in a repository.
|
||||
*
|
||||
* @param string $owner The owner name.
|
||||
* @param string $repo The repository name.
|
||||
* @param string $filepath The file path.
|
||||
* @param string $content The file content.
|
||||
* @param string $message The commit message.
|
||||
* @param string $sha The blob SHA of the file.
|
||||
* @param string $branch The branch name. Defaults to the repository's default branch.
|
||||
* @param string|null $authorName The author name. Defaults to the authenticated user.
|
||||
* @param string|null $authorEmail The author email. Defaults to the authenticated user.
|
||||
* @param string|null $committerName The committer name. Defaults to the authenticated user.
|
||||
* @param string|null $committerEmail The committer email. Defaults to the authenticated user.
|
||||
* @param string|null $authorDate The author date.
|
||||
* @param string|null $committerDate The committer date.
|
||||
* @param string|null $fromPath The original file path to move/rename.
|
||||
* @param string|null $newBranch The new branch to create from the specified branch.
|
||||
* @param bool|null $signoff Add a Signed-off-by trailer.
|
||||
*
|
||||
* @return object|null
|
||||
* @since 5.1.1
|
||||
**/
|
||||
public function update(
|
||||
string $owner,
|
||||
string $repo,
|
||||
string $filepath,
|
||||
string $content,
|
||||
string $message,
|
||||
string $sha,
|
||||
string $branch = 'master',
|
||||
?string $authorName = null,
|
||||
?string $authorEmail = null,
|
||||
?string $committerName = null,
|
||||
?string $committerEmail = null,
|
||||
?string $authorDate = null,
|
||||
?string $committerDate = null,
|
||||
?string $fromPath = null,
|
||||
?string $newBranch = null,
|
||||
?bool $signoff = null
|
||||
): ?object {
|
||||
// Build the request path.
|
||||
$path = "/repos/{$owner}/{$repo}/contents/{$filepath}";
|
||||
|
||||
// Set the file data.
|
||||
$data = new \stdClass();
|
||||
$data->content = base64_encode($content);
|
||||
$data->message = $message;
|
||||
$data->branch = $branch;
|
||||
$data->sha = $sha;
|
||||
|
||||
if ($authorName !== null || $authorEmail !== null)
|
||||
{
|
||||
$data->author = new \stdClass();
|
||||
|
||||
if ($authorName !== null)
|
||||
{
|
||||
$data->author->name = $authorName;
|
||||
}
|
||||
|
||||
if ($authorEmail !== null)
|
||||
{
|
||||
$data->author->email = $authorEmail;
|
||||
}
|
||||
}
|
||||
|
||||
if ($committerName !== null || $committerEmail !== null)
|
||||
{
|
||||
$data->committer = new \stdClass();
|
||||
|
||||
if ($committerName !== null)
|
||||
{
|
||||
$data->committer->name = $committerName;
|
||||
}
|
||||
|
||||
if ($committerEmail !== null)
|
||||
{
|
||||
$data->committer->email = $committerEmail;
|
||||
}
|
||||
}
|
||||
|
||||
if ($authorDate !== null || $committerDate !== null)
|
||||
{
|
||||
$data->dates = new \stdClass();
|
||||
|
||||
if ($authorDate !== null)
|
||||
{
|
||||
$data->dates->author = $authorDate;
|
||||
}
|
||||
|
||||
if ($committerDate !== null)
|
||||
{
|
||||
$data->dates->committer = $committerDate;
|
||||
}
|
||||
}
|
||||
|
||||
if ($fromPath !== null)
|
||||
{
|
||||
$data->from_path = $fromPath;
|
||||
}
|
||||
|
||||
if ($newBranch !== null)
|
||||
{
|
||||
$data->new_branch = $newBranch;
|
||||
}
|
||||
|
||||
if ($signoff !== null)
|
||||
{
|
||||
$data->signoff = $signoff;
|
||||
}
|
||||
|
||||
// Send the put request.
|
||||
return $this->response->get(
|
||||
$this->http->json()->put(
|
||||
$this->uri->get($path),
|
||||
json_encode($data)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a file in a repository.
|
||||
*
|
||||
* @param string $owner The owner name.
|
||||
* @param string $repo The repository name.
|
||||
* @param string $filepath The file path.
|
||||
* @param string $message The commit message.
|
||||
* @param string $sha The blob SHA of the file.
|
||||
* @param string|null $branch The branch name (optional).
|
||||
* @param string|null $authorName The author name (optional).
|
||||
* @param string|null $authorEmail The author email (optional).
|
||||
* @param string|null $committerName The committer name (optional).
|
||||
* @param string|null $committerEmail The committer email (optional).
|
||||
* @param string|null $authorDate The author date (optional).
|
||||
* @param string|null $committerDate The committer date (optional).
|
||||
* @param string|null $newBranch The new branch name (optional).
|
||||
* @param bool|null $signoff Add a Signed-off-by trailer (optional).
|
||||
*
|
||||
* @return object|null
|
||||
* @since 5.1.1
|
||||
**/
|
||||
public function delete(
|
||||
string $owner,
|
||||
string $repo,
|
||||
string $filepath,
|
||||
string $message,
|
||||
string $sha,
|
||||
?string $branch = null,
|
||||
?string $authorName = null,
|
||||
?string $authorEmail = null,
|
||||
?string $committerName = null,
|
||||
?string $committerEmail = null,
|
||||
?string $authorDate = null,
|
||||
?string $committerDate = null,
|
||||
?string $newBranch = null,
|
||||
?bool $signoff = null
|
||||
): ?object {
|
||||
// Build the request path.
|
||||
$path = "/repos/{$owner}/{$repo}/contents/{$filepath}";
|
||||
|
||||
// Set the file data.
|
||||
$data = new \stdClass();
|
||||
$data->message = $message;
|
||||
$data->sha = $sha;
|
||||
|
||||
if ($branch !== null) {
|
||||
$data->branch = $branch;
|
||||
}
|
||||
|
||||
if ($authorName !== null || $authorEmail !== null)
|
||||
{
|
||||
$data->author = new \stdClass();
|
||||
|
||||
if ($authorName !== null)
|
||||
{
|
||||
$data->author->name = $authorName;
|
||||
}
|
||||
|
||||
if ($authorEmail !== null)
|
||||
{
|
||||
$data->author->email = $authorEmail;
|
||||
}
|
||||
}
|
||||
|
||||
if ($committerName !== null || $committerEmail !== null)
|
||||
{
|
||||
$data->committer = new \stdClass();
|
||||
|
||||
if ($committerName !== null)
|
||||
{
|
||||
$data->committer->name = $committerName;
|
||||
}
|
||||
|
||||
if ($committerEmail !== null)
|
||||
{
|
||||
$data->committer->email = $committerEmail;
|
||||
}
|
||||
}
|
||||
|
||||
if ($authorDate !== null || $committerDate !== null)
|
||||
{
|
||||
$data->dates = new \stdClass();
|
||||
|
||||
if ($authorDate !== null)
|
||||
{
|
||||
$data->dates->author = $authorDate;
|
||||
}
|
||||
|
||||
if ($committerDate !== null)
|
||||
{
|
||||
$data->dates->committer = $committerDate;
|
||||
}
|
||||
}
|
||||
|
||||
if ($newBranch !== null)
|
||||
{
|
||||
$data->new_branch = $newBranch;
|
||||
}
|
||||
|
||||
if ($signoff !== null)
|
||||
{
|
||||
$data->signoff = $signoff;
|
||||
}
|
||||
|
||||
// Send the delete request.
|
||||
return $this->response->get(
|
||||
$this->http->json()->delete(
|
||||
$this->uri->get($path), [], null,
|
||||
json_encode($data)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the EditorConfig definitions of a file in a repository.
|
||||
*
|
||||
* @param string $owner The owner name.
|
||||
* @param string $repo The repository name.
|
||||
* @param string $filepath The file path.
|
||||
* @param string|null $ref The name of the commit/branch/tag.
|
||||
*
|
||||
* @return string|null
|
||||
* @since 5.1.1
|
||||
**/
|
||||
public function editor(string $owner, string $repo, string $filepath, string $ref = null): ?string
|
||||
{
|
||||
// Not supported in GitHub
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the blob of a repository.
|
||||
*
|
||||
* @param string $owner The owner name.
|
||||
* @param string $repo The repository name.
|
||||
* @param string $sha The SHA hash of the blob.
|
||||
*
|
||||
* @return object|null
|
||||
* @since 5.1.1
|
||||
**/
|
||||
public function blob(string $owner, string $repo, string $sha): ?object
|
||||
{
|
||||
// Build the request path.
|
||||
$path = "/repos/{$owner}/{$repo}/git/blobs/{$sha}";
|
||||
|
||||
// Send the get request.
|
||||
return $this->response->get(
|
||||
$this->http->json()->get(
|
||||
$this->uri->get($path)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
186
libraries/vendor_jcb/VDM.Joomla.Github/src/Repository/Tags.php
Normal file
186
libraries/vendor_jcb/VDM.Joomla.Github/src/Repository/Tags.php
Normal file
@@ -0,0 +1,186 @@
|
||||
<?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\Github\Repository;
|
||||
|
||||
|
||||
use VDM\Joomla\Interfaces\Git\Repository\TagsInterface;
|
||||
use VDM\Joomla\Github\Abstraction\Api;
|
||||
|
||||
|
||||
/**
|
||||
* The Github Repository Tags
|
||||
*
|
||||
* @since 5.1.1
|
||||
*/
|
||||
final class Tags extends Api implements TagsInterface
|
||||
{
|
||||
/**
|
||||
* List a repository's tags
|
||||
*
|
||||
* @param string $owner The owner of the repo.
|
||||
* @param string $repo The name of the repo.
|
||||
* @param int|null $page The page number of results to return (1-based).
|
||||
* @param int|null $limit The page size of results. GitHub default is 30, max 100. Here we fix it to 10.
|
||||
*
|
||||
* @return array|null
|
||||
* @since 3.2.0
|
||||
*/
|
||||
public function list(
|
||||
string $owner,
|
||||
string $repo,
|
||||
?int $page = 1,
|
||||
?int $limit = 10
|
||||
): ?array {
|
||||
$path = "/repos/{$owner}/{$repo}/tags";
|
||||
$uri = $this->uri->get($path);
|
||||
|
||||
$uri->setVar('page', $page ?? 1);
|
||||
$uri->setVar('per_page', $limit ?? 10);
|
||||
|
||||
return $this->response->get(
|
||||
$this->http->get($uri)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the tag object by tag name (loop until found or exhausted).
|
||||
*
|
||||
* @param string $owner The owner name.
|
||||
* @param string $repo The repository name.
|
||||
* @param string $tag The tag name to find.
|
||||
*
|
||||
* @return object|null
|
||||
* @since 3.2.0
|
||||
*/
|
||||
public function get(string $owner, string $repo, string $tag): ?object
|
||||
{
|
||||
$page = 1;
|
||||
$limit = 10;
|
||||
|
||||
do {
|
||||
$tags = $this->list($owner, $repo, $page, $limit);
|
||||
|
||||
if (empty($tags))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach ($tags as $entry)
|
||||
{
|
||||
if (isset($entry->name) && $entry->name === $tag)
|
||||
{
|
||||
return $entry;
|
||||
}
|
||||
}
|
||||
|
||||
$page++;
|
||||
} while (count($tags) === $limit);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the annotated tag object by SHA.
|
||||
*
|
||||
* @param string $owner The owner of the repo.
|
||||
* @param string $repo The repository name.
|
||||
* @param string $sha The tag object SHA.
|
||||
*
|
||||
* @return object|null
|
||||
* @since 3.2.0
|
||||
*/
|
||||
public function sha(string $owner, string $repo, string $sha): ?object
|
||||
{
|
||||
$path = "/repos/{$owner}/{$repo}/git/tags/{$sha}";
|
||||
return $this->response->get(
|
||||
$this->http->get($this->uri->get($path))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new annotated tag and attach it to the repository.
|
||||
*
|
||||
* GitHub requires two steps to create a tag:
|
||||
* 1. Create an annotated tag object.
|
||||
* 2. Create a reference to the tag under `refs/tags/*`.
|
||||
*
|
||||
* @param string $owner The owner of the repo.
|
||||
* @param string $repo The repository name.
|
||||
* @param string $tagName The name of the tag.
|
||||
* @param string $target The SHA the tag points to (usually a commit SHA).
|
||||
* @param string $message The tag message.
|
||||
*
|
||||
* @return object|null
|
||||
* @since 3.2.0
|
||||
*/
|
||||
public function create(string $owner, string $repo, string $tagName, string $target, string $message): ?object
|
||||
{
|
||||
// Step 1: Create the tag object
|
||||
$tagObject = (object) [
|
||||
'tag' => $tagName,
|
||||
'message' => $message,
|
||||
'object' => $target,
|
||||
'type' => 'commit'
|
||||
];
|
||||
|
||||
$tagResponse = $this->response->get(
|
||||
$this->http->post(
|
||||
$this->uri->get("/repos/{$owner}/{$repo}/git/tags"),
|
||||
json_encode($tagObject)
|
||||
)
|
||||
);
|
||||
|
||||
if (!isset($tagResponse->sha))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Step 2: Create the ref pointing to the tag object
|
||||
$refData = (object) [
|
||||
'ref' => "refs/tags/{$tagName}",
|
||||
'sha' => $tagResponse->sha
|
||||
];
|
||||
|
||||
return $this->response->get(
|
||||
$this->http->post(
|
||||
$this->uri->get("/repos/{$owner}/{$repo}/git/refs"),
|
||||
json_encode($refData)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a tag reference by tag name.
|
||||
*
|
||||
* GitHub deletes tags via refs.
|
||||
*
|
||||
* @param string $owner The owner name.
|
||||
* @param string $repo The repository name.
|
||||
* @param string $tag The tag name to delete.
|
||||
*
|
||||
* @return string Returns 'success' on successful deletion.
|
||||
* @since 3.2.0
|
||||
*/
|
||||
public function delete(string $owner, string $repo, string $tag): string
|
||||
{
|
||||
$path = "/repos/{$owner}/{$repo}/git/refs/tags/{$tag}";
|
||||
|
||||
return $this->response->get(
|
||||
$this->http->delete(
|
||||
$this->uri->get($path)
|
||||
),
|
||||
204,
|
||||
'success'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
166
libraries/vendor_jcb/VDM.Joomla.Github/src/Repository/Wiki.php
Normal file
166
libraries/vendor_jcb/VDM.Joomla.Github/src/Repository/Wiki.php
Normal file
@@ -0,0 +1,166 @@
|
||||
<?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\Github\Repository;
|
||||
|
||||
|
||||
use VDM\Joomla\Interfaces\Git\Repository\WikiInterface;
|
||||
use VDM\Joomla\Github\Abstraction\Api;
|
||||
use Joomla\CMS\Uri\Uri;
|
||||
|
||||
|
||||
/**
|
||||
* The Github Repository Wiki
|
||||
*
|
||||
* @since 5.1.1
|
||||
*/
|
||||
class Wiki extends Api implements WikiInterface
|
||||
{
|
||||
/**
|
||||
* Create a new wiki page or update it if it already exists.
|
||||
*
|
||||
* @param string $owner Repository owner (user or organization).
|
||||
* @param string $repo Repository name (without `.wiki`).
|
||||
* @param string $title Title of the wiki page.
|
||||
* @param string $contentBase64 Base64-encoded Markdown content.
|
||||
* @param string|null $message Optional commit message.
|
||||
*
|
||||
* @return object|null API response object or null on failure.
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function create(
|
||||
string $owner,
|
||||
string $repo,
|
||||
string $title,
|
||||
string $contentBase64,
|
||||
?string $message = null
|
||||
): ?object
|
||||
{
|
||||
return null; // github does not support wiki over API
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the content of a specific wiki page.
|
||||
*
|
||||
* @param string $owner Repository owner (user or organization).
|
||||
* @param string $repo Repository name (without `.wiki`).
|
||||
* @param string $pageName Name of the page (excluding `.md`).
|
||||
*
|
||||
* @return object|null Page details including content and metadata, or null on failure.
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function get(
|
||||
string $owner,
|
||||
string $repo,
|
||||
string $pageName
|
||||
): ?object
|
||||
{
|
||||
// Build the raw wiki URL
|
||||
$url = "https://raw.githubusercontent.com/wiki/{$owner}/{$repo}/{$pageName}.md";
|
||||
|
||||
// Use a direct HTTP GET request (bypasses GitHub API)
|
||||
$body = $this->response->get(
|
||||
$this->http->get(new Uri($url), [])
|
||||
);
|
||||
|
||||
return (object) [
|
||||
'name' => "{$pageName}.md",
|
||||
'content' => base64_encode($body),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* List all wiki pages in the repository.
|
||||
*
|
||||
* @param string $owner Repository owner (user or organization).
|
||||
* @param string $repo Repository name (without `.wiki`).
|
||||
* @param int $page Pagination index (1-based).
|
||||
* @param int $limit Number of results per page.
|
||||
*
|
||||
* @return array|null List of page metadata or null on failure.
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function pages(
|
||||
string $owner,
|
||||
string $repo,
|
||||
int $page = 1,
|
||||
int $limit = 10
|
||||
): ?array
|
||||
{
|
||||
return null; // github does not support wiki over API
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a wiki page from the repository.
|
||||
*
|
||||
* @param string $owner Repository owner (user or organization).
|
||||
* @param string $repo Repository name (without `.wiki`).
|
||||
* @param string $pageName Name of the page to delete (excluding `.md`).
|
||||
*
|
||||
* @return string 'success' on deletion, or error message if the page was not found.
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function delete(
|
||||
string $owner,
|
||||
string $repo,
|
||||
string $pageName
|
||||
): string
|
||||
{
|
||||
return 'error'; // github does not support wiki over API
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit an existing wiki page.
|
||||
*
|
||||
* @param string $owner Repository owner (user or organization).
|
||||
* @param string $repo Repository name (without `.wiki`).
|
||||
* @param string $pageName Name of the page to edit (excluding `.md`).
|
||||
* @param string $title New title of the page (used to rename if applicable).
|
||||
* @param string $content Updated Markdown content.
|
||||
* @param string|null $message Optional commit message.
|
||||
*
|
||||
* @return object|null API response object or null if the page doesn't exist.
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function edit(
|
||||
string $owner,
|
||||
string $repo,
|
||||
string $pageName,
|
||||
string $title,
|
||||
string $content,
|
||||
string $message = null
|
||||
): ?object
|
||||
{
|
||||
return null; // github does not support wiki over API
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the commit history (revisions) for a specific wiki page.
|
||||
*
|
||||
* @param string $owner Repository owner (user or organization).
|
||||
* @param string $repo Repository name (without `.wiki`).
|
||||
* @param string $pageName Name of the page to retrieve revisions for (excluding `.md`).
|
||||
* @param int $page Pagination index (1-based).
|
||||
*
|
||||
* @return object|null API response object with commit history or null on failure.
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function revisions(
|
||||
string $owner,
|
||||
string $repo,
|
||||
string $pageName,
|
||||
int $page = 1
|
||||
): ?object
|
||||
{
|
||||
return null; // github does not support wiki over API
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1 @@
|
||||
<html><body bgcolor="#FFFFFF"></body></html>
|
@@ -0,0 +1,91 @@
|
||||
<?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\Github\Service;
|
||||
|
||||
|
||||
use Joomla\DI\Container;
|
||||
use Joomla\DI\ServiceProviderInterface;
|
||||
use VDM\Joomla\Utilities\Component\Helper;
|
||||
use VDM\Joomla\Github\Utilities\Http;
|
||||
use VDM\Joomla\Github\Utilities\Uri;
|
||||
use VDM\Joomla\Github\Utilities\Response;
|
||||
|
||||
|
||||
/**
|
||||
* The Github Utilities Service
|
||||
*
|
||||
* @since 5.1.1
|
||||
*/
|
||||
class Utilities implements ServiceProviderInterface
|
||||
{
|
||||
/**
|
||||
* Registers the service provider with a DI container.
|
||||
*
|
||||
* @param Container $container The DI container.
|
||||
*
|
||||
* @return void
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function register(Container $container)
|
||||
{
|
||||
$container->alias(Http::class, 'Github.Utilities.Http')
|
||||
->share('Github.Utilities.Http', [$this, 'getHttp'], true);
|
||||
|
||||
$container->alias(Uri::class, 'Github.Utilities.Uri')
|
||||
->share('Github.Utilities.Uri', [$this, 'getUri'], true);
|
||||
|
||||
$container->alias(Response::class, 'Github.Utilities.Response')
|
||||
->share('Github.Utilities.Response', [$this, 'getResponse'], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Http class
|
||||
*
|
||||
* @param Container $container The DI container.
|
||||
*
|
||||
* @return Http
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function getHttp(Container $container): Http
|
||||
{
|
||||
return new Http(
|
||||
Helper::getParams('com_componentbuilder')->get('github_access_token') ?? null
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Uri class
|
||||
*
|
||||
* @param Container $container The DI container.
|
||||
*
|
||||
* @return Uri
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function getUri(Container $container): Uri
|
||||
{
|
||||
return new Uri();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Response class
|
||||
*
|
||||
* @param Container $container The DI container.
|
||||
*
|
||||
* @return Response
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function getResponse(Container $container): Response
|
||||
{
|
||||
return new Response();
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1 @@
|
||||
<html><body bgcolor="#FFFFFF"></body></html>
|
163
libraries/vendor_jcb/VDM.Joomla.Github/src/Utilities/Http.php
Normal file
163
libraries/vendor_jcb/VDM.Joomla.Github/src/Utilities/Http.php
Normal file
@@ -0,0 +1,163 @@
|
||||
<?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\Github\Utilities;
|
||||
|
||||
|
||||
use Joomla\CMS\Http\Http as JoomlaHttp;
|
||||
use Joomla\Registry\Registry;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The Github Http
|
||||
*
|
||||
* @since 5.1.1
|
||||
*/
|
||||
final class Http extends JoomlaHttp
|
||||
{
|
||||
/**
|
||||
* The token
|
||||
*
|
||||
* @var string|null
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected ?string $_token_; // to avoid collisions (but allow swapping)
|
||||
|
||||
/**
|
||||
* The GitHub API version header
|
||||
*
|
||||
* @var string
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected string $apiVersion;
|
||||
|
||||
/**
|
||||
* The GitHub default headers
|
||||
*
|
||||
* @var string
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected array $defaultHeaders;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string|null $token The Gitea API token.
|
||||
* @param string $version GitHub API Version (e.g. 2022-11-28)
|
||||
*
|
||||
* @since 5.1.1
|
||||
* @throws \InvalidArgumentException
|
||||
**/
|
||||
public function __construct(?string $token = null, string $version = '2022-11-28')
|
||||
{
|
||||
$this->apiVersion = $version;
|
||||
|
||||
$this->defaultHeaders = [
|
||||
'Content-Type' => 'application/json',
|
||||
'Accept' => 'application/vnd.github+json',
|
||||
'X-GitHub-Api-Version' => $this->apiVersion
|
||||
];
|
||||
|
||||
// setup config
|
||||
$config = [
|
||||
'userAgent' => 'JoomlaGitHub/3.0',
|
||||
'headers' => $this->defaultHeaders
|
||||
];
|
||||
|
||||
// add the token if given
|
||||
if (is_string($token) && !empty($token))
|
||||
{
|
||||
$config['headers']['Authorization'] = 'Bearer ' . $token;
|
||||
$this->_token_ = $token;
|
||||
}
|
||||
|
||||
$options = new Registry($config);
|
||||
|
||||
// run parent constructor
|
||||
parent::__construct($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the Token.
|
||||
*
|
||||
* @param string $token The Gitea API token.
|
||||
*
|
||||
* @since 5.1.1
|
||||
**/
|
||||
public function setToken(string $token): void
|
||||
{
|
||||
// get the current headers
|
||||
$headers = (array) $this->getOption('headers', $this->defaultHeaders);
|
||||
|
||||
if (empty($token))
|
||||
{
|
||||
unset($headers['Authorization']);
|
||||
}
|
||||
else
|
||||
{
|
||||
// add the token
|
||||
$headers['Authorization'] = 'Bearer ' . $token;
|
||||
$this->_token_ = $token;
|
||||
}
|
||||
|
||||
$this->setOption('headers', $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Token.
|
||||
*
|
||||
* @return string|null
|
||||
* @since 5.1.1
|
||||
**/
|
||||
public function getToken(): ?string
|
||||
{
|
||||
return $this->_token_ ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Accept header to 'application/vnd.github+json'
|
||||
*
|
||||
* @return static
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function json(): self
|
||||
{
|
||||
$this->setAcceptHeader('application/vnd.github+json');
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Accept header to 'application/vnd.github.raw+json'
|
||||
*
|
||||
* @return static
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function raw(): self
|
||||
{
|
||||
$this->setAcceptHeader('application/vnd.github.raw+json');
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Accept header
|
||||
*
|
||||
* @return void
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected function setAcceptHeader(string $value): void
|
||||
{
|
||||
$headers = (array) $this->getOption('headers', $this->defaultHeaders);
|
||||
$headers['Accept'] = $value;
|
||||
$this->setOption('headers', $headers);
|
||||
}
|
||||
}
|
||||
|
@@ -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\Github\Utilities;
|
||||
|
||||
|
||||
use Joomla\Http\Response as JoomlaResponse;
|
||||
use VDM\Joomla\Utilities\JsonHelper;
|
||||
use VDM\Joomla\Utilities\StringHelper;
|
||||
|
||||
|
||||
/**
|
||||
* The Github Response
|
||||
*
|
||||
* @since 5.1.1
|
||||
*/
|
||||
final class Response
|
||||
{
|
||||
/**
|
||||
* Process the response and decode it.
|
||||
*
|
||||
* @param JoomlaResponse $response The response.
|
||||
* @param integer $expectedCode The expected "good" code.
|
||||
* @param mixed $default The default if body not have length
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @since 5.1.1
|
||||
* @throws \DomainException
|
||||
**/
|
||||
public function get(JoomlaResponse $response, int $expectedCode = 200, $default = null)
|
||||
{
|
||||
// Validate the response code.
|
||||
if ($response->code != $expectedCode)
|
||||
{
|
||||
// Decode the error response and throw an exception.
|
||||
$message = $this->error($response);
|
||||
|
||||
throw new \DomainException("Invalid response received from GitHub API. {$message}", $response->code);
|
||||
}
|
||||
|
||||
return $this->body($response, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the response and decode it. (when we have multiple success codes)
|
||||
*
|
||||
* @param JoomlaResponse $response The response.
|
||||
* @param array [$expectedCode => $default] The expected "good" code. and The default if body not have length
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @since 5.1.1
|
||||
* @throws \DomainException
|
||||
**/
|
||||
public function get_(JoomlaResponse $response, array $validate = [200 => null])
|
||||
{
|
||||
// Validate the response code.
|
||||
if (!array_key_exists($response->code, $validate))
|
||||
{
|
||||
// Decode the error response and throw an exception.
|
||||
$message = $this->error($response);
|
||||
|
||||
throw new \DomainException("Invalid response received from GitHub API. {$message}", $response->code);
|
||||
}
|
||||
|
||||
return $this->body($response, $validate[$response->code]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the body from the response
|
||||
*
|
||||
* @param JoomlaResponse $response The response.
|
||||
* @param mixed $default The default if body not have length
|
||||
*
|
||||
* @return mixed
|
||||
* @since 5.1.1
|
||||
**/
|
||||
protected function body(JoomlaResponse $response, $default = null)
|
||||
{
|
||||
$body = method_exists($response, 'getBody')
|
||||
? (string) $response->getBody()
|
||||
: ($response->body ?? null);
|
||||
|
||||
// check that we have a body
|
||||
if ($body !== null && StringHelper::check($body))
|
||||
{
|
||||
if (JsonHelper::check($body))
|
||||
{
|
||||
$body = json_decode((string) $body);
|
||||
|
||||
if (isset($body->content) && isset($body->encoding) && $body->encoding === 'base64')
|
||||
{
|
||||
$body->decoded_content = base64_decode((string) $body->content);
|
||||
}
|
||||
}
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract an error message from a response object.
|
||||
*
|
||||
* @param JoomlaResponse $response The response object.
|
||||
*
|
||||
* @return string The extracted error message, or an empty string.
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected function error(JoomlaResponse $response): string
|
||||
{
|
||||
// Try to get the raw response body
|
||||
$body = method_exists($response, 'getBody') ? (string) $response->getBody() : '';
|
||||
|
||||
// Try to decode as JSON object
|
||||
$errorData = JsonHelper::check($body) ? json_decode($body) : null;
|
||||
|
||||
if (is_object($errorData))
|
||||
{
|
||||
// GitHub's error structure may have a message and/or an errors array
|
||||
if (!empty($errorData->message)) {
|
||||
$errorMsg = $errorData->message;
|
||||
|
||||
if (!empty($errorData->errors) && is_array($errorData->errors)) {
|
||||
$details = [];
|
||||
|
||||
foreach ($errorData->errors as $err) {
|
||||
if (is_object($err)) {
|
||||
$details[] = trim(
|
||||
($err->resource ?? '') . ' ' .
|
||||
($err->field ?? '') . ' ' .
|
||||
($err->code ?? '')
|
||||
);
|
||||
} else {
|
||||
$details[] = (string) $err;
|
||||
}
|
||||
}
|
||||
|
||||
$errorMsg .= ' (' . implode('; ', array_filter($details)) . ')';
|
||||
}
|
||||
|
||||
return $errorMsg;
|
||||
}
|
||||
|
||||
return json_encode($errorData, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
// Fallback to reason phrase or body
|
||||
if (!empty($body))
|
||||
{
|
||||
return $body;
|
||||
}
|
||||
|
||||
return method_exists($response, 'getReasonPhrase')
|
||||
? $response->getReasonPhrase()
|
||||
: 'No error information found in GitHub API response.';
|
||||
}
|
||||
}
|
||||
|
169
libraries/vendor_jcb/VDM.Joomla.Github/src/Utilities/Uri.php
Normal file
169
libraries/vendor_jcb/VDM.Joomla.Github/src/Utilities/Uri.php
Normal 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\Github\Utilities;
|
||||
|
||||
|
||||
use Joomla\Uri\Uri as JoomlaUri;
|
||||
|
||||
|
||||
/**
|
||||
* The Github Uri
|
||||
*
|
||||
* @since 5.1.1
|
||||
*/
|
||||
final class Uri
|
||||
{
|
||||
/**
|
||||
* The api endpoint
|
||||
*
|
||||
* @var string
|
||||
* @since 5.1.1
|
||||
*/
|
||||
private string $endpoint;
|
||||
|
||||
/**
|
||||
* The api version
|
||||
*
|
||||
* @var string
|
||||
* @since 5.1.1
|
||||
*/
|
||||
private string $version;
|
||||
|
||||
/**
|
||||
* The api URL
|
||||
*
|
||||
* @var string
|
||||
* @since 5.1.1
|
||||
*/
|
||||
private string $url;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $url URL to the github system
|
||||
* example: https://api.github.com
|
||||
* @param string $endpoint Endpoint to the gitea system
|
||||
* @param string $version Version to the gitea system
|
||||
*
|
||||
* @since 5.1.1
|
||||
**/
|
||||
public function __construct(
|
||||
string $url = 'https://api.github.com',
|
||||
string $endpoint = '',
|
||||
string $version = 'v3')
|
||||
{
|
||||
// set the API details
|
||||
$this->setUrl($url);
|
||||
//$this->setEndpoint($endpoint);
|
||||
//$this->setVersion($version);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to build and return a full request URL for the request. This method will
|
||||
* add appropriate pagination details if necessary and also prepend the API url
|
||||
* to have a complete URL for the request.
|
||||
*
|
||||
* @param string $path URL to inflect
|
||||
*
|
||||
* @return JoomlaUri
|
||||
* @since 5.1.1
|
||||
**/
|
||||
public function get(string $path): JoomlaUri
|
||||
{
|
||||
// GitHub API does not use version in URL (normally passed in Accept headers)
|
||||
// But we maintain compatibility with existing interface
|
||||
$uri = new JoomlaUri($this->api() . ltrim($path, '/'));
|
||||
|
||||
return $uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the full API URL
|
||||
*
|
||||
* @return string
|
||||
* @since 5.1.1
|
||||
**/
|
||||
public function api(): string
|
||||
{
|
||||
// Ensure trailing slash on base URL
|
||||
return rtrim($this->url, '/') . '/';
|
||||
/**
|
||||
// GitHub typically does not use endpoint/version in URL
|
||||
// But to preserve interface, we include them conditionally
|
||||
$segments = [];
|
||||
|
||||
if (!empty($this->endpoint))
|
||||
{
|
||||
$segments[] = trim($this->endpoint, '/');
|
||||
}
|
||||
|
||||
if (!empty($this->version))
|
||||
{
|
||||
$segments[] = trim($this->version, '/');
|
||||
}
|
||||
|
||||
return $base . (empty($segments) ? '' : implode('/', $segments) . '/');
|
||||
**/
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the URL of the API
|
||||
*
|
||||
* @param string $url URL to your github system
|
||||
* example: https://api.github.com
|
||||
*
|
||||
* @return void
|
||||
* @since 5.1.1
|
||||
**/
|
||||
public function setUrl(string $url)
|
||||
{
|
||||
$this->url = $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL of the API
|
||||
*
|
||||
* @return string|null
|
||||
* @since 5.1.1
|
||||
**/
|
||||
public function getUrl(): ?string
|
||||
{
|
||||
return $this->url ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the endpoint of the API
|
||||
*
|
||||
* @param string $endpoint endpoint to your github API
|
||||
*
|
||||
* @return void
|
||||
* @since 5.1.1
|
||||
private function setEndpoint(string $endpoint)
|
||||
{
|
||||
$this->endpoint = $endpoint;
|
||||
}
|
||||
**/
|
||||
|
||||
/**
|
||||
* Set the version of the API
|
||||
*
|
||||
* @param string $version version to your github API
|
||||
*
|
||||
* @return void
|
||||
* @since 3.2.0
|
||||
private function setVersion($version)
|
||||
{
|
||||
$this->version = $version;
|
||||
}
|
||||
**/
|
||||
}
|
||||
|
@@ -0,0 +1 @@
|
||||
<html><body bgcolor="#FFFFFF"></body></html>
|
@@ -13,8 +13,9 @@ namespace VDM\Joomla\Abstraction;
|
||||
|
||||
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\Database\DatabaseInterface;
|
||||
use Joomla\Database\DatabaseInterface as JoomlaDatabase;
|
||||
use VDM\Joomla\Utilities\Component\Helper;
|
||||
use VDM\Joomla\Database\QuoteTrait;
|
||||
|
||||
|
||||
/**
|
||||
@@ -24,12 +25,28 @@ use VDM\Joomla\Utilities\Component\Helper;
|
||||
*/
|
||||
abstract class Database
|
||||
{
|
||||
/**
|
||||
* Function to quote values
|
||||
*
|
||||
* @since 5.1.1
|
||||
*/
|
||||
use QuoteTrait;
|
||||
|
||||
/**
|
||||
* Database object to query local DB
|
||||
*
|
||||
* @var JoomlaDatabase
|
||||
* @since 3.2.0
|
||||
*/
|
||||
protected $db;
|
||||
protected JoomlaDatabase $db;
|
||||
|
||||
/**
|
||||
* Current component code name
|
||||
*
|
||||
* @var string
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected string $componentCode;
|
||||
|
||||
/**
|
||||
* Core Component Table Name
|
||||
@@ -39,76 +56,18 @@ abstract class Database
|
||||
*/
|
||||
protected string $table;
|
||||
|
||||
/**
|
||||
* Date format to return
|
||||
*
|
||||
* @var string
|
||||
* @since 5.0.2
|
||||
*/
|
||||
protected string $dateFormat = 'Y-m-d H:i:s';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @throws \Exception
|
||||
* @since 3.2.0
|
||||
*/
|
||||
public function __construct()
|
||||
public function __construct(?JoomlaDatabase $db = null)
|
||||
{
|
||||
$this->db = Factory::getContainer()->get(DatabaseInterface::class);
|
||||
$this->db = $db ?: Factory::getContainer()->get(JoomlaDatabase::class);
|
||||
|
||||
// set the component table
|
||||
$this->table = '#__' . Helper::getCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a value based on data type
|
||||
*
|
||||
* @param mixed $value The value to set
|
||||
*
|
||||
* @return mixed
|
||||
* @since 3.2.0
|
||||
**/
|
||||
protected function quote($value)
|
||||
{
|
||||
if ($value === null)
|
||||
{
|
||||
return 'NULL';
|
||||
}
|
||||
|
||||
if (is_numeric($value))
|
||||
{
|
||||
// If the value is a numeric string (e.g., "0123"), treat it as a string to preserve the format
|
||||
if (is_string($value) && ltrim($value, '0') !== $value)
|
||||
{
|
||||
return $this->db->quote($value);
|
||||
}
|
||||
|
||||
if (filter_var($value, FILTER_VALIDATE_INT))
|
||||
{
|
||||
return (int) $value;
|
||||
}
|
||||
|
||||
if (filter_var($value, FILTER_VALIDATE_FLOAT))
|
||||
{
|
||||
return (float) $value;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle boolean values
|
||||
if (is_bool($value))
|
||||
{
|
||||
return $value ? 'TRUE' : 'FALSE';
|
||||
}
|
||||
|
||||
// For date and datetime values
|
||||
if ($value instanceof \DateTime)
|
||||
{
|
||||
return $this->db->quote($value->format($this->getDateFormat()));
|
||||
}
|
||||
|
||||
// For other types of values, quote as string
|
||||
return $this->db->quote($value);
|
||||
$this->componentCode = Helper::getCode();
|
||||
$this->table = '#__' . $this->componentCode;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -128,17 +87,6 @@ abstract class Database
|
||||
}
|
||||
|
||||
return $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the date format to return in the quote
|
||||
*
|
||||
* @return string
|
||||
* @since 5.0.2
|
||||
**/
|
||||
protected function getDateFormat(): string
|
||||
{
|
||||
return $this->dateFormat;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -14,11 +14,13 @@ namespace VDM\Joomla\Abstraction;
|
||||
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\CMS\Filesystem\Folder;
|
||||
use Joomla\Filesystem\Folder;
|
||||
use Joomla\CMS\Application\CMSApplication;
|
||||
use VDM\Joomla\Gitea\Repository\Contents;
|
||||
use VDM\Joomla\Interfaces\Git\ApiInterface as Api;
|
||||
use VDM\Joomla\Interfaces\Remote\ConfigInterface as Config;
|
||||
use VDM\Joomla\Interfaces\Git\Repository\ContentsInterface as Contents;
|
||||
use VDM\Joomla\Componentbuilder\Network\Resolve;
|
||||
use VDM\Joomla\Componentbuilder\Package\Dependency\Tracker;
|
||||
use VDM\Joomla\Interfaces\Git\ApiInterface as Api;
|
||||
use VDM\Joomla\Utilities\FileHelper;
|
||||
use VDM\Joomla\Utilities\JsonHelper;
|
||||
use VDM\Joomla\Interfaces\GrepInterface;
|
||||
@@ -27,9 +29,9 @@ use VDM\Joomla\Interfaces\GrepInterface;
|
||||
/**
|
||||
* Global Resource Empowerment Platform
|
||||
*
|
||||
* 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 [algorithm:cascading]
|
||||
* The Grep feature will try to find your power in the repositories
|
||||
* linked to this [area], and if it can't be found there will try the global core
|
||||
* 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
|
||||
@@ -40,7 +42,7 @@ abstract class Grep implements GrepInterface
|
||||
* The local path
|
||||
*
|
||||
* @var string|null
|
||||
* @since 3.2.0
|
||||
* @since 3.2.0
|
||||
**/
|
||||
public ?string $path;
|
||||
|
||||
@@ -48,7 +50,7 @@ abstract class Grep implements GrepInterface
|
||||
* All approved paths
|
||||
*
|
||||
* @var array|null
|
||||
* @since 3.2.0
|
||||
* @since 3.2.0
|
||||
**/
|
||||
public ?array $paths;
|
||||
|
||||
@@ -61,50 +63,58 @@ abstract class Grep implements GrepInterface
|
||||
protected ?string $target = null;
|
||||
|
||||
/**
|
||||
* Order of global search
|
||||
* The Grep target [entity]
|
||||
*
|
||||
* @var array
|
||||
* @since 3.2.1
|
||||
* @var string
|
||||
* @since 5.0.4
|
||||
**/
|
||||
protected array $order = ['local', 'remote'];
|
||||
protected string $entity;
|
||||
|
||||
/**
|
||||
* The target branch field name ['read_branch', 'write_branch']
|
||||
*
|
||||
* @var string
|
||||
* @since 3.2.2
|
||||
* @since 3.2.2
|
||||
**/
|
||||
protected string $branch_field = 'read_branch';
|
||||
|
||||
/**
|
||||
* Order of global search
|
||||
*
|
||||
* @var array
|
||||
* @since 3.2.1
|
||||
**/
|
||||
protected array $order = ['local', 'remote'];
|
||||
|
||||
/**
|
||||
* The target default branch name
|
||||
*
|
||||
* @var string|null
|
||||
* @since 3.2.2
|
||||
* @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';
|
||||
|
||||
/**
|
||||
* The VDM global API base
|
||||
*
|
||||
* @var string
|
||||
* @since 5.0.4
|
||||
* @since 5.0.4
|
||||
**/
|
||||
protected string $api_base = '//git.vdm.dev/';
|
||||
|
||||
/**
|
||||
* The Config Class.
|
||||
*
|
||||
* @var Config
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected Config $config;
|
||||
|
||||
/**
|
||||
* Gitea Repository Contents
|
||||
*
|
||||
* @var Contents
|
||||
* @since 3.2.0
|
||||
* @since 3.2.0
|
||||
**/
|
||||
protected Contents $contents;
|
||||
|
||||
@@ -116,35 +126,48 @@ abstract class Grep implements GrepInterface
|
||||
*/
|
||||
protected Resolve $resolve;
|
||||
|
||||
/**
|
||||
* The Tracker Class.
|
||||
*
|
||||
* @var Tracker
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected Tracker $tracker;
|
||||
|
||||
/**
|
||||
* Joomla Application object
|
||||
*
|
||||
* @var CMSApplication
|
||||
* @since 3.2.0
|
||||
* @since 3.2.0
|
||||
**/
|
||||
protected CMSApplication $app;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Contents $contents The Gitea Repository Contents object.
|
||||
* @param Resolve $resolve The Resolve Class.
|
||||
* @param array $paths The approved paths
|
||||
* @param string|null $path The local path
|
||||
* @param CMSApplication|null $app The CMS Application object.
|
||||
* @param Config $config The Config Class.
|
||||
* @param Contents $contents The Contents Class.
|
||||
* @param Resolve $resolve The Resolve Class.
|
||||
* @param Tracker $tracker The Tracker Class.
|
||||
* @param array $paths The approved paths
|
||||
* @param string|null $path The local path
|
||||
* @param CMSApplication|null $app The Application Class.
|
||||
*
|
||||
* @throws \Exception
|
||||
* @since 3.2.0
|
||||
* @since 3.2.1
|
||||
*/
|
||||
public function __construct(
|
||||
Contents $contents, Resolve $resolve,
|
||||
array $paths, ?string $path = null,
|
||||
?CMSApplication $app = null)
|
||||
public function __construct(Config $config, Contents $contents,
|
||||
Resolve $resolve, Tracker $tracker, array $paths,
|
||||
?string $path = null, ?CMSApplication $app = null)
|
||||
{
|
||||
$this->entity = $config->getTable();
|
||||
$this->config = $config;
|
||||
$this->contents = $contents;
|
||||
$this->resolve = $resolve;
|
||||
$this->tracker = $tracker;
|
||||
|
||||
$this->paths = $paths;
|
||||
$this->path = $path;
|
||||
|
||||
$this->app = $app ?: Factory::getApplication();
|
||||
|
||||
$this->initializeInstances();
|
||||
@@ -158,7 +181,7 @@ abstract class Grep implements GrepInterface
|
||||
* @param object|null $repo The repository object to search. If null, all repos will be searched.
|
||||
*
|
||||
* @return object|null
|
||||
* @since 3.2.2
|
||||
* @since 3.2.2
|
||||
*/
|
||||
public function get(string $guid, ?array $order = null, ?object $repo = null): ?object
|
||||
{
|
||||
@@ -173,34 +196,56 @@ abstract class Grep implements GrepInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an item exists in any repo or in a specific repo.
|
||||
* Get the path/repo object
|
||||
*
|
||||
* @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.
|
||||
* @param string $guid The target repository guid.
|
||||
*
|
||||
* @return bool True if the item exists, false otherwise.
|
||||
* @since 3.2.2
|
||||
* @return object|null
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function exists(string $guid, ?object $repo = null, ?array $order = null): bool
|
||||
public function getPath(string $guid): ?object
|
||||
{
|
||||
$order = $order ?? $this->order;
|
||||
|
||||
if ($repo !== null)
|
||||
if (!is_array($this->paths) || $this->paths === [] || empty($guid))
|
||||
{
|
||||
return $this->itemExistsInRepo($guid, $repo, $order);
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->itemExistsInAllRepos($guid, $order);
|
||||
foreach ($this->paths as $path)
|
||||
{
|
||||
if (!isset($path->guid) || $guid !== $path->guid)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all remote GUID's
|
||||
* Get all the available repos
|
||||
*
|
||||
* @return array|null
|
||||
* @since 3.2.0
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function getRemoteGuid(): ?array
|
||||
public function getPaths(): ?array
|
||||
{
|
||||
if (!is_array($this->paths) || $this->paths === [])
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->paths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all paths + indexes (the active set)
|
||||
*
|
||||
* @return array|null
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function getPathsIndexes(): ?array
|
||||
{
|
||||
if (!is_array($this->paths) || $this->paths === [])
|
||||
{
|
||||
@@ -213,52 +258,47 @@ abstract class Grep implements GrepInterface
|
||||
// Get remote index
|
||||
$this->indexRemote($path);
|
||||
|
||||
if (isset($path->index) && is_object($path->index))
|
||||
if (is_array($path->index ?? null) && is_object($path->index[$this->entity] ?? null))
|
||||
{
|
||||
$powers = array_merge($powers, array_keys((array) $path->index));
|
||||
$powers[] = $path;
|
||||
}
|
||||
}
|
||||
|
||||
return empty($powers) ? null : array_unique($powers);
|
||||
return $powers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the branch field
|
||||
* Get the a path + indexes
|
||||
*
|
||||
* @param string $field The field to use to get the branch name from the data set
|
||||
* @param string $guid The unique identifier for the repo.
|
||||
*
|
||||
* @return void
|
||||
* @since 3.2.2
|
||||
* @return object|null
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function setBranchField(string $field): void
|
||||
public function getPathIndexes(string $guid): ?object
|
||||
{
|
||||
$this->branch_field = $field;
|
||||
}
|
||||
if (!is_array($this->paths) || $this->paths === [] || empty($guid))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the DEFAULT branch name (only used if branch field is not found)
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
foreach ($this->paths as $path)
|
||||
{
|
||||
if (!isset($path->guid) || $guid !== $path->guid)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 remote index
|
||||
$this->indexRemote($path);
|
||||
|
||||
if (is_array($path->index ?? null) && is_object($path->index[$this->entity] ?? null))
|
||||
{
|
||||
return $path;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -267,7 +307,7 @@ abstract class Grep implements GrepInterface
|
||||
* @param string $guid The unique identifier for the repo.
|
||||
*
|
||||
* @return object|null
|
||||
* @since 3.2.2
|
||||
* @since 3.2.2
|
||||
*/
|
||||
public function getRemoteIndex(string $guid): ?object
|
||||
{
|
||||
@@ -286,15 +326,87 @@ abstract class Grep implements GrepInterface
|
||||
// Get remote index
|
||||
$this->indexRemote($path);
|
||||
|
||||
if (isset($path->index) && is_object($path->index))
|
||||
if (is_array($path->index ?? null) && is_object($path->index[$this->entity] ?? null))
|
||||
{
|
||||
return $path->index;
|
||||
return $path->index[$this->entity];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the network target name
|
||||
*
|
||||
* @return string|null
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function getNetworkTarget(): ?string
|
||||
{
|
||||
return $this->target ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the branch field
|
||||
*
|
||||
* @param string $field The field to use to get the branch name from the data set
|
||||
*
|
||||
* @return void
|
||||
* @since 3.2.2
|
||||
*/
|
||||
public function setBranchField(string $field): void
|
||||
{
|
||||
$this->branch_field = $field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the DEFAULT branch name (only used if branch field is not found)
|
||||
*
|
||||
* @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->config->setIndexPath($indexPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads API config using the provided base URL and token.
|
||||
*
|
||||
@@ -307,14 +419,18 @@ abstract class Grep implements GrepInterface
|
||||
* @param string|null $token The token for authentication (can be null).
|
||||
*
|
||||
* @return void
|
||||
* @since 5.0.4
|
||||
* @since 5.0.4
|
||||
*/
|
||||
public function loadApi(Api $api, ?string $base, ?string $token): void
|
||||
{
|
||||
// Determine the token to use based on the base URL
|
||||
if ($base && strpos($base, $this->api_base) !== false)
|
||||
// If we have global tokens for a base system we must not reset on an empty token
|
||||
if ($base && (
|
||||
strpos($base. '/', $this->api_base) !== false ||
|
||||
strpos($base, 'api.github.com') !== false
|
||||
))
|
||||
{
|
||||
// If base contains $this->api_base = https://git.vdm.dev/, use the token as is
|
||||
// If base contains api.github.com, use the token as is
|
||||
$tokenToUse = $token;
|
||||
}
|
||||
else
|
||||
@@ -337,10 +453,55 @@ abstract class Grep implements GrepInterface
|
||||
* @param string|null $base Base URL
|
||||
*
|
||||
* @return void
|
||||
* @since 3.2.0
|
||||
* @since 3.2.0
|
||||
*/
|
||||
abstract protected function setRemoteIndexMessage(string $message, string $path, string $repository, string $organisation, ?string $base): void;
|
||||
|
||||
/**
|
||||
* Injects metadata SHA into the power params object.
|
||||
*
|
||||
* @param object $power The object to modify
|
||||
* @param object $path The repository path
|
||||
* @param string $targetPath The target path inside the repo
|
||||
* @param string $branch The branch to use
|
||||
* @param string $sourceKey The key to set inside params->source
|
||||
*
|
||||
* @return void
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected function setRepoItemSha(object &$power, object $path,
|
||||
string $targetPath, string $branch, string $sourceKey): void
|
||||
{
|
||||
try {
|
||||
$meta = $this->contents->metadata(
|
||||
$path->organisation,
|
||||
$path->repository,
|
||||
$targetPath,
|
||||
$branch
|
||||
);
|
||||
} catch (\Throwable $e) {
|
||||
$meta = null;
|
||||
}
|
||||
|
||||
if ($meta === null || !isset($meta->sha))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isset($power->params) || !is_object($power->params))
|
||||
{
|
||||
$power->params = (object) ['source' => [$sourceKey => $meta->sha]];
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isset($power->params->source) || !is_array($power->params->source))
|
||||
{
|
||||
$power->params->source = [];
|
||||
}
|
||||
|
||||
$power->params->source[$sourceKey] = $meta->sha;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get function name
|
||||
*
|
||||
@@ -348,7 +509,7 @@ abstract class Grep implements GrepInterface
|
||||
* @param string $type The type of function name
|
||||
*
|
||||
* @return string|null
|
||||
* @since 3.2.0
|
||||
* @since 3.2.0
|
||||
*/
|
||||
protected function getFunctionName(string $name, string $type = 'search'): ?string
|
||||
{
|
||||
@@ -365,7 +526,7 @@ abstract class Grep implements GrepInterface
|
||||
* @param object $repo The repository object to check against.
|
||||
*
|
||||
* @return object|null
|
||||
* @since 3.2.2
|
||||
* @since 3.2.2
|
||||
*/
|
||||
protected function searchSingleRepo(string $guid, array $order, object $repo): ?object
|
||||
{
|
||||
@@ -391,7 +552,7 @@ abstract class Grep implements GrepInterface
|
||||
* @param object $repo The repository object to check against.
|
||||
*
|
||||
* @return object|null
|
||||
* @since 3.2.2
|
||||
* @since 3.2.2
|
||||
*/
|
||||
protected function searchAllRepos(string $guid, array $order): ?object
|
||||
{
|
||||
@@ -418,7 +579,7 @@ abstract class Grep implements GrepInterface
|
||||
* @param array $order The order of the targets to check.
|
||||
*
|
||||
* @return bool True if the item exists, false otherwise.
|
||||
* @since 3.2.2
|
||||
* @since 3.2.2
|
||||
*/
|
||||
protected function itemExistsInRepo(string $guid, object $repo, array $order): bool
|
||||
{
|
||||
@@ -439,7 +600,7 @@ abstract class Grep implements GrepInterface
|
||||
* @param array $order The order of the targets to check.
|
||||
*
|
||||
* @return bool True if the item exists, false otherwise.
|
||||
* @since 3.2.2
|
||||
* @since 3.2.2
|
||||
*/
|
||||
protected function itemExistsInAllRepos(string $guid, array $order): bool
|
||||
{
|
||||
@@ -464,7 +625,7 @@ abstract class Grep implements GrepInterface
|
||||
* Get the branch field
|
||||
*
|
||||
* @return string
|
||||
* @since 3.2.2
|
||||
* @since 3.2.2
|
||||
*/
|
||||
protected function getBranchField(): string
|
||||
{
|
||||
@@ -475,7 +636,7 @@ abstract class Grep implements GrepInterface
|
||||
* Get the branch default name
|
||||
*
|
||||
* @return string|null
|
||||
* @since 3.2.2
|
||||
* @since 3.2.2
|
||||
*/
|
||||
protected function getBranchDefaultName(): ?string
|
||||
{
|
||||
@@ -488,7 +649,7 @@ abstract class Grep implements GrepInterface
|
||||
* @param object $item The item path
|
||||
*
|
||||
* @return string|null
|
||||
* @since 3.2.2
|
||||
* @since 3.2.2
|
||||
*/
|
||||
protected function getBranchName(object $item): ?string
|
||||
{
|
||||
@@ -502,11 +663,55 @@ abstract class Grep implements GrepInterface
|
||||
* Get the index path
|
||||
*
|
||||
* @return string
|
||||
* @since 3.2.2
|
||||
* @since 3.2.2
|
||||
*/
|
||||
protected function getIndexPath(): string
|
||||
{
|
||||
return $this->index_path;
|
||||
return $this->config->getIndexPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the settings name
|
||||
*
|
||||
* @return string
|
||||
* @since 3.2.2
|
||||
*/
|
||||
protected function getSettingsName(): string
|
||||
{
|
||||
return $this->config->getSettingsName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get GUID field
|
||||
*
|
||||
* @return string
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected function getGuidField(): string
|
||||
{
|
||||
return $this->config->getGuidField();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get GUID field
|
||||
*
|
||||
* @return string
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected function getItemReadmeName(): string
|
||||
{
|
||||
return $this->config->getItemReadmeName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Has item readme
|
||||
*
|
||||
* @return bool
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected function hasItemReadme(): bool
|
||||
{
|
||||
return $this->config->hasItemReadme();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -517,7 +722,7 @@ abstract class Grep implements GrepInterface
|
||||
* @param string $target The target to check within the repo.
|
||||
*
|
||||
* @return bool True if the item exists, false otherwise.
|
||||
* @since 3.2.2
|
||||
* @since 3.2.2
|
||||
*/
|
||||
protected function itemExists(string $guid, object &$repo, string $target): bool
|
||||
{
|
||||
@@ -541,7 +746,7 @@ abstract class Grep implements GrepInterface
|
||||
* @param string $guid The global unique id of the item
|
||||
*
|
||||
* @return object|null return path object
|
||||
* @since 3.2.2
|
||||
* @since 3.2.2
|
||||
*/
|
||||
protected function existsLocally(string $guid): ?object
|
||||
{
|
||||
@@ -569,7 +774,7 @@ abstract class Grep implements GrepInterface
|
||||
* @param string $guid The global unique id of the item
|
||||
*
|
||||
* @return object|null return path object
|
||||
* @since 3.2.2
|
||||
* @since 3.2.2
|
||||
*/
|
||||
protected function existsRemotely(string $guid): ?object
|
||||
{
|
||||
@@ -598,11 +803,12 @@ abstract class Grep implements GrepInterface
|
||||
* @param object $path The path object
|
||||
*
|
||||
* @return bool true if it exists
|
||||
* @since 3.2.2
|
||||
* @since 3.2.2
|
||||
*/
|
||||
protected function existsLocal(string $guid, object $path): bool
|
||||
{
|
||||
if (!empty($path->local) && isset($path->local->{$guid}))
|
||||
if (is_array($path->local ?? null) && is_object($path->local[$this->entity] ?? null) &&
|
||||
isset($path->local[$this->entity]->{$guid}))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -617,11 +823,12 @@ abstract class Grep implements GrepInterface
|
||||
* @param object $path The path object
|
||||
*
|
||||
* @return bool true if it exists
|
||||
* @since 3.2.2
|
||||
* @since 3.2.2
|
||||
*/
|
||||
protected function existsRemote(string $guid, object $path): bool
|
||||
{
|
||||
if (!empty($path->index) && isset($path->index->{$guid}))
|
||||
if (is_array($path->index ?? null) && is_object($path->index[$this->entity] ?? null) &&
|
||||
isset($path->index[$this->entity]->{$guid}))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -635,25 +842,44 @@ abstract class Grep implements GrepInterface
|
||||
* @param object $path The repository path details
|
||||
*
|
||||
* @return void
|
||||
* @since 3.2.0
|
||||
* @since 3.2.0
|
||||
*/
|
||||
protected function indexRemote(object &$path): void
|
||||
{
|
||||
if (isset($path->index))
|
||||
if (is_array($path->index ?? null) && isset($path->index[$this->entity]))
|
||||
{
|
||||
return; // already set
|
||||
}
|
||||
|
||||
if (!is_array($path->index ?? null))
|
||||
{
|
||||
$path->index = [];
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// set the target system
|
||||
$target = $path->target ?? 'gitea';
|
||||
$this->contents->setTarget($target);
|
||||
|
||||
// load the base and token if set
|
||||
$this->loadApi($this->contents, $path->base ?? null, $path->token ?? null);
|
||||
$path->index = $this->contents->get($path->organisation, $path->repository, $this->getIndexPath(), $this->getBranchName($path));
|
||||
$this->loadApi(
|
||||
$this->contents,
|
||||
$path->base ?? null,
|
||||
$path->token ?? null
|
||||
);
|
||||
|
||||
$path->index[$this->entity] = $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);
|
||||
$path->index[$this->entity] = null;
|
||||
|
||||
// only when searching (read_branch) do we show this error message
|
||||
if ($this->getBranchField() === 'read_branch')
|
||||
{
|
||||
$this->setRemoteIndexMessage($e->getMessage(), $path->path, $path->repository, $path->organisation, $path->base ?? null);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -668,45 +894,53 @@ abstract class Grep implements GrepInterface
|
||||
* @param object $path The repository path details
|
||||
*
|
||||
* @return void
|
||||
* @since 3.2.0
|
||||
* @since 3.2.0
|
||||
*/
|
||||
protected function indexLocal(object &$path): void
|
||||
{
|
||||
if (isset($path->local) || !isset($path->full_path))
|
||||
if (is_array($path->local ?? null) && isset($path->local[$this->entity]))
|
||||
{
|
||||
return;
|
||||
return; // already set
|
||||
}
|
||||
|
||||
if (!is_array($path->local ?? null))
|
||||
{
|
||||
$path->local = [];
|
||||
}
|
||||
|
||||
if (($content = FileHelper::getContent($path->full_path . '/' . $this->getIndexPath(), null)) !== null &&
|
||||
JsonHelper::check($content))
|
||||
{
|
||||
$path->local = json_decode($content);
|
||||
$path->local[$this->entity] = json_decode($content);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$path->local = null;
|
||||
$path->local[$this->entity] = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set path details
|
||||
*
|
||||
* @return void
|
||||
* @since 3.2.0
|
||||
* @since 3.2.0
|
||||
*/
|
||||
protected function initializeInstances(): void
|
||||
{
|
||||
if (is_array($this->paths) && $this->paths !== [])
|
||||
{
|
||||
$network_target = $this->getNetworkTarget();
|
||||
foreach ($this->paths as $n => &$path)
|
||||
{
|
||||
if (isset($path->organisation) && strlen($path->organisation) > 1 &&
|
||||
isset($path->repository) && strlen($path->repository) > 1)
|
||||
{
|
||||
// resolve API if needed
|
||||
if (!empty($path->base))
|
||||
$target = $path->target ?? 'gitea';
|
||||
|
||||
// resolve API if a gitea (core) endpoint
|
||||
if (!empty($path->base) && $target === 'gitea')
|
||||
{
|
||||
$this->resolve->api($this->target ?? $path->repository, $path->base, $path->organisation, $path->repository);
|
||||
$this->resolve->api($network_target ?? $path->repository, $path->base, $path->organisation, $path->repository);
|
||||
}
|
||||
|
||||
// build the path
|
||||
@@ -724,7 +958,7 @@ abstract class Grep implements GrepInterface
|
||||
}
|
||||
|
||||
// set local path
|
||||
if ($this->path && Folder::exists($this->path . '/' . $path->path))
|
||||
if ($this->path && is_dir($this->path . '/' . $path->path))
|
||||
{
|
||||
$path->full_path = $this->path . '/' . $path->path;
|
||||
}
|
||||
@@ -746,7 +980,7 @@ abstract class Grep implements GrepInterface
|
||||
* @param string|null $branch The repository branch name
|
||||
*
|
||||
* @return mixed
|
||||
* @since 3.2.0
|
||||
* @since 3.2.0
|
||||
*/
|
||||
protected function loadRemoteFile(string $organisation, string $repository, string $path, ?string $branch)
|
||||
{
|
||||
@@ -757,7 +991,7 @@ abstract class Grep implements GrepInterface
|
||||
catch (\Exception $e)
|
||||
{
|
||||
$this->app->enqueueMessage(
|
||||
Text::sprintf('COM_COMPONENTBUILDER_PFILE_AT_BSSB_GAVE_THE_FOLLOWING_ERRORBR_SP', $this->contents->api(), $path, $e->getMessage()),
|
||||
Text::sprintf('COM_COMPONENTBUILDER_PFILE_AT_BSSSSB_GAVE_THE_FOLLOWING_ERRORBR_SP', $this->contents->api(), $organisation, $repository, $path, $e->getMessage()),
|
||||
'Error'
|
||||
);
|
||||
|
||||
|
@@ -62,11 +62,11 @@ abstract class Model implements ModelInterface
|
||||
*
|
||||
* @param Table $table The search table object.
|
||||
* @param string|null $tableName The table
|
||||
* @param bool|null $allowEmpty The switch to control the behaviour of empty values (default true)
|
||||
* @param bool|null $allowEmpty The switch to control the behaviour of empty values (default true)
|
||||
*
|
||||
* @since 3.2.0
|
||||
*/
|
||||
public function __construct(Table $table, ?string $tableName = null, bool $allowEmpty = null)
|
||||
public function __construct(Table $table, ?string $tableName = null, ?bool $allowEmpty = null)
|
||||
{
|
||||
$this->table = $table;
|
||||
if ($tableName !== null)
|
||||
|
493
libraries/vendor_jcb/VDM.Joomla/src/Abstraction/Remote/Base.php
Normal file
493
libraries/vendor_jcb/VDM.Joomla/src/Abstraction/Remote/Base.php
Normal file
@@ -0,0 +1,493 @@
|
||||
<?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\Remote\ConfigInterface as Config;
|
||||
use VDM\Joomla\Interfaces\Remote\BaseInterface;
|
||||
|
||||
|
||||
/**
|
||||
* Remote Base Shared by get and set methods
|
||||
*
|
||||
* @since 5.1.1
|
||||
*/
|
||||
abstract class Base implements BaseInterface
|
||||
{
|
||||
/**
|
||||
* The Base Configure Class.
|
||||
*
|
||||
* @var Config
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected Config $config;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Config $config The configure class.
|
||||
*
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function __construct(Config $config)
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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->config->table($table);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current active table
|
||||
*
|
||||
* @return string
|
||||
* @since 3.2.2
|
||||
*/
|
||||
public function getTable(): string
|
||||
{
|
||||
return $this->config->getTable();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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->config->area($area);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current active area
|
||||
*
|
||||
* @return string|null
|
||||
* @since 3.2.2
|
||||
*/
|
||||
public function getArea(): ?string
|
||||
{
|
||||
return $this->config->getArea();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the settings file name
|
||||
*
|
||||
* @param string $settingsName The repository settings name
|
||||
*
|
||||
* @return self
|
||||
* @since 3.2.2
|
||||
*/
|
||||
public function setSettingsName(string $settingsName): self
|
||||
{
|
||||
$this->config->setSettingsName($settingsName);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the settings file name
|
||||
*
|
||||
* @return string
|
||||
* @since 3.2.2
|
||||
*/
|
||||
public function getSettingsName(): string
|
||||
{
|
||||
return $this->config->getSettingsName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the index path
|
||||
*
|
||||
* @param string $indexPath The repository index path
|
||||
*
|
||||
* @return void
|
||||
* @since 3.2.2
|
||||
*/
|
||||
public function setIndexPath(string $indexPath): void
|
||||
{
|
||||
$this->config->setIndexPath($indexPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the index path
|
||||
*
|
||||
* @return string
|
||||
* @since 3.2.2
|
||||
*/
|
||||
public function getIndexPath(): string
|
||||
{
|
||||
return $this->config->getIndexPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get core placeholders
|
||||
*
|
||||
* @return array
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function getPlaceholders(): array
|
||||
{
|
||||
return $this->config->getPlaceholders();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get index map
|
||||
*
|
||||
* @return array
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function getIndexMap(): array
|
||||
{
|
||||
return $this->config->getIndexMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get index header
|
||||
*
|
||||
* @return array
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function getIndexHeader(): array
|
||||
{
|
||||
return $this->config->getIndexHeader();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get src path
|
||||
*
|
||||
* @return string
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function getSrcPath(): string
|
||||
{
|
||||
return $this->config->getSrcPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the field names of the files in the entity
|
||||
*
|
||||
* @return array
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function getFiles(): array
|
||||
{
|
||||
return $this->config->getFiles();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the field names of the folders in the entity
|
||||
*
|
||||
* @return array
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function getFolders(): array
|
||||
{
|
||||
return $this->config->getFolders();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get map
|
||||
*
|
||||
* @return array
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function getMap(): array
|
||||
{
|
||||
return $this->config->getMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the [direct] entities/children of this entity
|
||||
*
|
||||
* @return array
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function getChildren(): array
|
||||
{
|
||||
return $this->config->getChildren();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the table title name field
|
||||
*
|
||||
* @return string
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function getTitleName(): string
|
||||
{
|
||||
return $this->config->getTitleName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get GUID field
|
||||
*
|
||||
* @return string
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function getGuidField(): string
|
||||
{
|
||||
return $this->config->getGuidField();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get main readme path
|
||||
*
|
||||
* @return string
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function getMainReadmePath(): string
|
||||
{
|
||||
return $this->config->getMainReadmePath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Has main readme
|
||||
*
|
||||
* @return bool
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function hasMainReadme(): bool
|
||||
{
|
||||
return $this->config->hasMainReadme();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get item readme path
|
||||
*
|
||||
* @return string
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function getItemReadmeName(): string
|
||||
{
|
||||
return $this->config->getItemReadmeName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Has item readme
|
||||
*
|
||||
* @return bool
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function hasItemReadme(): bool
|
||||
{
|
||||
return $this->config->hasItemReadme();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Prefix Key
|
||||
*
|
||||
* @return string
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function getPrefixKey(): string
|
||||
{
|
||||
return $this->config->getPrefixKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Suffix Key
|
||||
*
|
||||
* @return string
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function getSuffixKey(): string
|
||||
{
|
||||
return $this->config->getSuffixKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a single item to its properties
|
||||
*
|
||||
* @param object $item The item to be mapped
|
||||
*
|
||||
* @return object
|
||||
* @since 3.2.2
|
||||
*/
|
||||
public function mapItem(object $item): object
|
||||
{
|
||||
$power = [];
|
||||
$mapper = $this->getMap();
|
||||
foreach ($mapper as $key => $map)
|
||||
{
|
||||
$methodName = "mapItemValue_{$key}";
|
||||
if (method_exists($this, $methodName))
|
||||
{
|
||||
$this->{$methodName}($item, $power);
|
||||
}
|
||||
elseif (!isset($power[$key]))
|
||||
{
|
||||
$power[$key] = $item->{$map} ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
return (object) $power;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get index values
|
||||
*
|
||||
* @param object $item The item
|
||||
*
|
||||
* @return array|null
|
||||
* @since 3.2.2
|
||||
*/
|
||||
public function getIndexItem(object $item): ?array
|
||||
{
|
||||
$index_map = $this->getIndexMap();
|
||||
if (empty($index_map))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
$index_item = [];
|
||||
foreach ($index_map as $key => $function_name)
|
||||
{
|
||||
if (method_exists($this, $function_name))
|
||||
{
|
||||
$index_item[$key] = $this->{$function_name}($item);
|
||||
}
|
||||
}
|
||||
|
||||
return $index_item ?? null;
|
||||
}
|
||||
|
||||
//// 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
|
||||
{
|
||||
$field = $this->getTitleName();
|
||||
return $item->{$field} ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the item Short Description for the index values
|
||||
*
|
||||
* @param object $item
|
||||
*
|
||||
* @return string|null
|
||||
* @since 5.0.3
|
||||
*/
|
||||
protected function index_map_ShortDescription(object $item): ?string
|
||||
{
|
||||
return $item->short_description ?? $item->description ?? 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
|
||||
{
|
||||
$index_path = $this->index_map_IndexPath($item);
|
||||
$settings_name = $this->getSettingsName();
|
||||
|
||||
return !empty($settings_name)
|
||||
? "{$index_path}/{$settings_name}"
|
||||
: $index_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
$src_path = $this->getSrcPath();
|
||||
$key = $this->index_map_IndexGUID($item);
|
||||
return "{$src_path}/{$key}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the item readme path for the index values
|
||||
*
|
||||
* @param object $item
|
||||
*
|
||||
* @return string
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected function index_map_IndexReadmePath(object $item): string
|
||||
{
|
||||
$index_path = $this->index_map_IndexPath($item);
|
||||
$readme = $this->getItemReadmeName();
|
||||
|
||||
return !empty($readme)
|
||||
? "{$index_path}/{$readme}"
|
||||
: "{$index_path}.md";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the item [POWER KEY] for the index values
|
||||
*
|
||||
* @param object $item
|
||||
*
|
||||
* @return string
|
||||
* @since 3.2.2
|
||||
*/
|
||||
protected function index_map_IndexKey(object $item): string
|
||||
{
|
||||
$prefix_key = $this->getPrefixKey();
|
||||
$suffix_key = $this->getSuffixKey();
|
||||
$key = $this->index_map_IndexGUID($item);
|
||||
$key = str_replace('-', '_', $key);
|
||||
|
||||
return "{$prefix_key}{$key}{$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
|
||||
{
|
||||
$guid_field = $this->getGuidField();
|
||||
|
||||
return $item->{$guid_field} ?? $item->guid ?? 'missing-guid';
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,521 @@
|
||||
<?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\Componentbuilder\Power\Interfaces\TableInterface as Table;
|
||||
use VDM\Joomla\Interfaces\Remote\ConfigInterface;
|
||||
|
||||
|
||||
/**
|
||||
* Remote Base Config Shared by get and set methods
|
||||
*
|
||||
* @since 5.1.1
|
||||
*/
|
||||
abstract class Config implements ConfigInterface
|
||||
{
|
||||
/**
|
||||
* The Table Class.
|
||||
*
|
||||
* @var Table
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected Table $core;
|
||||
|
||||
/**
|
||||
* Table Name
|
||||
*
|
||||
* @var string
|
||||
* @since 3.2.1
|
||||
*/
|
||||
protected string $table;
|
||||
|
||||
/**
|
||||
* Area Name
|
||||
*
|
||||
* @var string|null
|
||||
* @since 3.2.2
|
||||
*/
|
||||
protected ?string $area = null;
|
||||
|
||||
/**
|
||||
* Prefix Key
|
||||
*
|
||||
* @var string
|
||||
* @since 3.2.2
|
||||
*/
|
||||
protected string $prefix_key = '';
|
||||
|
||||
/**
|
||||
* Suffix Key
|
||||
*
|
||||
* @var string
|
||||
* @since 3.2.2
|
||||
*/
|
||||
protected string $suffix_key = '';
|
||||
|
||||
/**
|
||||
* The main readme file path
|
||||
*
|
||||
* @var string
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected string $main_readme_path = 'README.md';
|
||||
|
||||
/**
|
||||
* The item readme file name
|
||||
*
|
||||
* @var string
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected string $item_readme_name = 'README.md';
|
||||
|
||||
/**
|
||||
* The index file path (index of all items)
|
||||
*
|
||||
* @var string
|
||||
* @since 3.2.2
|
||||
*/
|
||||
protected string $index_path = 'index.json';
|
||||
|
||||
/**
|
||||
* The item (files) source path
|
||||
*
|
||||
* @var string
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected string $src_path = 'src';
|
||||
|
||||
/**
|
||||
* The item settings file name (item data)
|
||||
*
|
||||
* @var string
|
||||
* @since 3.2.2
|
||||
*/
|
||||
protected string $settings_name = 'item.json';
|
||||
|
||||
/**
|
||||
* The item guid=unique field
|
||||
*
|
||||
* @var string
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected string $guid_field = 'guid';
|
||||
|
||||
/**
|
||||
* The ignore fields
|
||||
*
|
||||
* @var array
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected array $ignore = ['access'];
|
||||
|
||||
/**
|
||||
* The files (to map target files to move in an entity)
|
||||
*
|
||||
* Use a pipe in the name to denote
|
||||
* subform location of the value
|
||||
* format: [field_name, field_name|subfrom_key]
|
||||
*
|
||||
* @var array
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected array $files = [];
|
||||
|
||||
/**
|
||||
* The folders (to map target folders to move in an entity)
|
||||
*
|
||||
* Use a pipe in the name to denote
|
||||
* subform location of the value
|
||||
* format: [field_name, field_name|subfrom_key]
|
||||
*
|
||||
* @var array
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected array $folders = [];
|
||||
|
||||
/**
|
||||
* The item map
|
||||
*
|
||||
* @var array
|
||||
* @since 3.2.2
|
||||
*/
|
||||
protected array $map = [];
|
||||
|
||||
/**
|
||||
* The direct entities/children of this entity
|
||||
*
|
||||
* @var array
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected array $children = [];
|
||||
|
||||
/**
|
||||
* The index map
|
||||
* must always have: [name,path,settings,guid]
|
||||
* you can add more
|
||||
*
|
||||
* @var array
|
||||
* @since 5.0.3
|
||||
*/
|
||||
protected array $index_map = [
|
||||
'name' => 'index_map_IndexName',
|
||||
'path' => 'index_map_IndexPath',
|
||||
'settings' => 'index_map_IndexSettingsPath',
|
||||
'guid' => 'index_map_IndexGUID'
|
||||
];
|
||||
|
||||
/**
|
||||
* The index header
|
||||
* mapping the index map to a table
|
||||
* must always have: [name,path,settings,guid,local]
|
||||
* with [name] always first
|
||||
* with [path,settings,guid,local] always last
|
||||
* you can add more in between
|
||||
*
|
||||
* @var array
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected array $index_header = [
|
||||
'name',
|
||||
// from here you can add more
|
||||
'path',
|
||||
'settings',
|
||||
'guid',
|
||||
'local'
|
||||
];
|
||||
|
||||
/**
|
||||
* Core Placeholders
|
||||
*
|
||||
* @var array
|
||||
* @since 5.0.3
|
||||
*/
|
||||
protected array $placeholders = [];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Table $core The Core Table Class.
|
||||
*
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function __construct(Table $core)
|
||||
{
|
||||
$this->core = $core;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get core placeholders
|
||||
*
|
||||
* @return array
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function getPlaceholders(): array
|
||||
{
|
||||
return $this->placeholders;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current active table
|
||||
*
|
||||
* @return string
|
||||
* @since 3.2.2
|
||||
*/
|
||||
public function getTable(): string
|
||||
{
|
||||
return $this->table;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current active area
|
||||
*
|
||||
* @return string|null
|
||||
* @since 3.2.2
|
||||
*/
|
||||
public function getArea(): ?string
|
||||
{
|
||||
return $this->area;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the settings file name
|
||||
*
|
||||
* @param string $settingsName The repository settings path
|
||||
*
|
||||
* @return self
|
||||
* @since 3.2.2
|
||||
*/
|
||||
public function setSettingsName(string $settingsName): self
|
||||
{
|
||||
$this->settings_name = $settingsName;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the settings file name
|
||||
*
|
||||
* @return string
|
||||
* @since 3.2.2
|
||||
*/
|
||||
public function getSettingsName(): string
|
||||
{
|
||||
return $this->settings_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 path
|
||||
*
|
||||
* @return string
|
||||
* @since 3.2.2
|
||||
*/
|
||||
public function getIndexPath(): string
|
||||
{
|
||||
return $this->index_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get index map
|
||||
*
|
||||
* @return array
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function getIndexMap(): array
|
||||
{
|
||||
return $this->index_map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get index header
|
||||
*
|
||||
* @return array
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function getIndexHeader(): array
|
||||
{
|
||||
return $this->index_header;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get src path
|
||||
*
|
||||
* @return string
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function getSrcPath(): string
|
||||
{
|
||||
return $this->src_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get main readme path
|
||||
*
|
||||
* @return string
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function getMainReadmePath(): string
|
||||
{
|
||||
return $this->main_readme_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Has main readme
|
||||
*
|
||||
* @return bool
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function hasMainReadme(): bool
|
||||
{
|
||||
return !empty($this->main_readme_path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get item readme path
|
||||
*
|
||||
* @return string
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function getItemReadmeName(): string
|
||||
{
|
||||
return $this->item_readme_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Has item readme
|
||||
*
|
||||
* @return bool
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function hasItemReadme(): bool
|
||||
{
|
||||
return !empty($this->item_readme_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the field names of the files in the entity
|
||||
*
|
||||
* @return array
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function getFiles(): array
|
||||
{
|
||||
return $this->files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the field names of the folders in the entity
|
||||
*
|
||||
* @return array
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function getFolders(): array
|
||||
{
|
||||
return $this->folders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get map
|
||||
*
|
||||
* Builds (and caches) an associative map of the table’s field names,
|
||||
* automatically removing any fields defined in $this->ignore.
|
||||
*
|
||||
* @return array Associative array in the form ['field' => 'field']
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function getMap(): array
|
||||
{
|
||||
// Only build once – cached in $this->map.
|
||||
if (empty($this->map))
|
||||
{
|
||||
// Fetch raw field list from the core service.
|
||||
$map = $this->core->fields($this->getTable());
|
||||
|
||||
if ($map)
|
||||
{
|
||||
// Ensure $this->ignore is an array; default to empty otherwise.
|
||||
$ignore = is_array($this->ignore ?? null) ? $this->ignore : [];
|
||||
|
||||
// Remove ignored fields, preserving the original order.
|
||||
$map = array_values(array_diff($map, $ignore));
|
||||
|
||||
// Convert to the required ['field' => 'field'] structure.
|
||||
$this->map = array_combine($map, $map);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->map = [];
|
||||
}
|
||||
}
|
||||
|
||||
return $this->map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the direct entities/children of this entity
|
||||
*
|
||||
* @return array
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function getChildren(): array
|
||||
{
|
||||
return $this->children;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the table title name field
|
||||
*
|
||||
* @return string
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function getTitleName(): string
|
||||
{
|
||||
return $this->core->titleName($this->getTable());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get GUID field
|
||||
*
|
||||
* @return string
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function getGuidField(): string
|
||||
{
|
||||
return $this->guid_field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Prefix Key
|
||||
*
|
||||
* @return string
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function getPrefixKey(): string
|
||||
{
|
||||
return $this->prefix_key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Suffix Key
|
||||
*
|
||||
* @return string
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function getSuffixKey(): string
|
||||
{
|
||||
return $this->suffix_key;
|
||||
}
|
||||
}
|
||||
|
@@ -12,9 +12,14 @@
|
||||
namespace VDM\Joomla\Abstraction\Remote;
|
||||
|
||||
|
||||
use Joomla\CMS\Language\Text;
|
||||
use VDM\Joomla\Interfaces\Remote\ConfigInterface as Config;
|
||||
use VDM\Joomla\Interfaces\GrepInterface as Grep;
|
||||
use VDM\Joomla\Interfaces\Data\ItemInterface as Item;
|
||||
use VDM\Joomla\Componentbuilder\Package\Dependency\Tracker;
|
||||
use VDM\Joomla\Componentbuilder\Package\MessageBus;
|
||||
use VDM\Joomla\Interfaces\Remote\GetInterface;
|
||||
use VDM\Joomla\Abstraction\Remote\Base;
|
||||
|
||||
|
||||
/**
|
||||
@@ -22,13 +27,13 @@ use VDM\Joomla\Interfaces\Remote\GetInterface;
|
||||
*
|
||||
* @since 3.2.0
|
||||
*/
|
||||
abstract class Get implements GetInterface
|
||||
abstract class Get extends Base implements GetInterface
|
||||
{
|
||||
/**
|
||||
* The Grep Class.
|
||||
*
|
||||
* @var Grep
|
||||
* @since 3.2.0
|
||||
* @since 3.2.2
|
||||
*/
|
||||
protected Grep $grep;
|
||||
|
||||
@@ -41,70 +46,202 @@ abstract class Get implements GetInterface
|
||||
protected Item $item;
|
||||
|
||||
/**
|
||||
* Table Name
|
||||
* The Tracker Class.
|
||||
*
|
||||
* @var string
|
||||
* @since 3.2.1
|
||||
* @var Tracker
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected string $table;
|
||||
protected Tracker $tracker;
|
||||
|
||||
/**
|
||||
* The MessageBus Class.
|
||||
*
|
||||
* @var MessageBus
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected MessageBus $messages;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Grep $grep The GrepInterface Class.
|
||||
* @param Item $item The ItemInterface Class.
|
||||
* @param string|null $table The table name.
|
||||
* @param Config $config The Config Class.
|
||||
* @param Grep $grep The Grep Class.
|
||||
* @param Item $item The ItemInterface Class.
|
||||
* @param Tracker $tracker The Tracker Class.
|
||||
* @param MessageBus $messages The MessageBus Class.
|
||||
* @param string|null $table The table name.
|
||||
*
|
||||
* @since 3.2.0
|
||||
*/
|
||||
public function __construct(Grep $grep, Item $item, ?string $table = null)
|
||||
public function __construct(Config $config, Grep $grep, Item $item,
|
||||
Tracker $tracker, MessageBus $messages, ?string $table = null)
|
||||
{
|
||||
parent::__construct($config);
|
||||
|
||||
$this->grep = $grep;
|
||||
$this->item = $item;
|
||||
$this->tracker = $tracker;
|
||||
$this->messages = $messages;
|
||||
|
||||
if ($table !== null)
|
||||
{
|
||||
$this->table = $table;
|
||||
$this->table($table);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current active table
|
||||
* Initializes and categorizes items by checking their existence in the local database
|
||||
* and optionally retrieving them from a remote repository if not found locally.
|
||||
*
|
||||
* @param string $table The table that should be active
|
||||
* This method processes an array of unique identifiers (`$items`) and checks each item:
|
||||
* - If found in the local database: categorized under 'local'.
|
||||
* - If not found locally and not available remotely: categorized under 'not_found'.
|
||||
* - If retrieved from the remote repository: categorized under 'added' and stored locally.
|
||||
*
|
||||
* @return self
|
||||
* @since 3.2.2
|
||||
* @param array $items An array of item identifiers (GUIDs) to initialize and validate.
|
||||
* @param object|null $repo The repository object to search. If null, all repos will be searched.
|
||||
* @param bool $force Force a local update (if item exist locally).
|
||||
*
|
||||
* @return array{
|
||||
* local: array<string, string>,
|
||||
* not_found: array<string, string>,
|
||||
* added: array<string, string>
|
||||
* } Associative arrays indexed by GUIDs indicating the status of each item:
|
||||
* - 'local': Items already present in the local database.
|
||||
* - 'not_found': Items not found locally or remotely.
|
||||
* - 'added': Items successfully retrieved from the remote repository and stored.
|
||||
*
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function table(string $table): self
|
||||
public function init(array $items, ?object $repo = null, bool $force = false): array
|
||||
{
|
||||
$this->table = $table;
|
||||
$logger = [
|
||||
'local' => [],
|
||||
'not_found' => [],
|
||||
'added' => []
|
||||
];
|
||||
|
||||
return $this;
|
||||
}
|
||||
$guid_field = $this->getGuidField();
|
||||
$table = $this->getTable();
|
||||
$this->grep->setBranchField('read_branch');
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
foreach($items as $guid)
|
||||
if ($this->tracker->exists("save.{$table}.{$guid_field}|{$guid}"))
|
||||
{
|
||||
if ($this->item->table($this->getTable())->value($guid) === null &&
|
||||
($item = $this->grep->get($guid, ['remote'])) !== null)
|
||||
{
|
||||
$this->item->set($item);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
$this->tracker->set("save.{$table}.{$guid_field}|{$guid}", true);
|
||||
|
||||
// Check if item exists in the local database
|
||||
if ($force === false && $this->item->table($table)->value($guid, $guid_field) !== null)
|
||||
{
|
||||
$logger['local'][$guid] = $guid;
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
// Attempt to fetch the item from the remote repository
|
||||
$item = $this->grep->get($guid, ['remote'], $repo);
|
||||
|
||||
if ($item === null)
|
||||
{
|
||||
$logger['not_found'][$guid] = $guid;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Store the retrieved remote item into the local structure
|
||||
$this->item->table($table)->set($item, $guid_field);
|
||||
|
||||
$logger['added'][$guid] = $guid;
|
||||
}
|
||||
|
||||
return false;
|
||||
return $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path/repo object
|
||||
*
|
||||
* @param string $guid The target repository guid.
|
||||
*
|
||||
* @return object|null
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function path(string $guid): ?object
|
||||
{
|
||||
$this->grep->setBranchField('read_branch');
|
||||
return $this->grep->getPath($guid);
|
||||
}
|
||||
|
||||
/**
|
||||
* get all the available paths for this area
|
||||
*
|
||||
* @return array|null
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function paths(): ?array
|
||||
{
|
||||
$this->grep->setBranchField('read_branch');
|
||||
return $this->grep->getPaths();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all available items for the given repository, or all repositories if none specified.
|
||||
*
|
||||
* @param string|null $repo The target repository to list (optional).
|
||||
*
|
||||
* @return array|null An array of indexed path objects or null if not found.
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function list(?string $repo = null): ?array
|
||||
{
|
||||
$guid_field = $this->getGuidField();
|
||||
$entity = $this->getTable();
|
||||
$table = $this->item->table($entity);
|
||||
$this->grep->setBranchField('read_branch');
|
||||
|
||||
if ($repo === null)
|
||||
{
|
||||
$paths = $this->grep->getPathsIndexes();
|
||||
}
|
||||
else
|
||||
{
|
||||
$singlePath = $this->grep->getPathIndexes($repo);
|
||||
$paths = $singlePath !== null ? [$singlePath] : null;
|
||||
}
|
||||
|
||||
if ($paths === null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
$list = [];
|
||||
foreach ($paths as $path)
|
||||
{
|
||||
if (!is_object($path->index[$entity] ?? null))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$repo = clone $path;
|
||||
$repo->index = $path->index[$entity];
|
||||
|
||||
foreach ($repo->index as $key => $item)
|
||||
{
|
||||
$guid = $item->guid ?? $item->{$guid_field} ?? null;
|
||||
if (!isset($guid))
|
||||
{
|
||||
unset($repo->index->{$key});
|
||||
continue;
|
||||
}
|
||||
$item->local = $table->value($guid, $guid_field) !== null;
|
||||
}
|
||||
|
||||
$this->normalizeObjectIndexHeader($repo->index);
|
||||
|
||||
$list[] = $repo;
|
||||
}
|
||||
|
||||
return $list ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -113,7 +250,7 @@ abstract class Get implements GetInterface
|
||||
* @param array $items The global unique ids of the items
|
||||
*
|
||||
* @return bool
|
||||
* @since 3.2.0
|
||||
* @since 3.2.0
|
||||
*/
|
||||
public function reset(array $items): bool
|
||||
{
|
||||
@@ -123,47 +260,97 @@ abstract class Get implements GetInterface
|
||||
}
|
||||
|
||||
$success = true;
|
||||
$area = $this->getArea();
|
||||
|
||||
foreach($items as $guid)
|
||||
{
|
||||
if (!$this->item($guid, ['remote']))
|
||||
{
|
||||
$success = false;
|
||||
$this->messages->add('warning', Text::sprintf('COM_COMPONENTBUILDER_THE_S_ITEMS_DID_NOT_RESET', strtolower($area), $guid));
|
||||
}
|
||||
}
|
||||
|
||||
if ($success)
|
||||
{
|
||||
$this->messages->add('success', Text::sprintf('COM_COMPONENTBUILDER_THE_S_ITEMS_WAS_RESET', strtolower($area)));
|
||||
}
|
||||
|
||||
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
|
||||
* @param string $guid The global unique id of the item
|
||||
* @param array $order The search order
|
||||
* @param object|null $repo The repository object to search. If null, all repos will be searched.
|
||||
*
|
||||
* @return bool
|
||||
* @since 3.2.0
|
||||
* @since 3.2.0
|
||||
* @since 5.1.1 We added the repo object
|
||||
*/
|
||||
public function item(string $guid, array $order = ['remote', 'local'], ?string $action = null): bool
|
||||
public function item(string $guid, array $order = ['remote', 'local'], ?object $repo = null): bool
|
||||
{
|
||||
if (($item = $this->grep->get($guid, $order)) !== null)
|
||||
$guid_field = $this->getGuidField();
|
||||
$table = $this->getTable();
|
||||
$this->grep->setBranchField('read_branch');
|
||||
$result = false;
|
||||
|
||||
if ($this->tracker->exists("save.{$table}.{$guid_field}|{$guid}"))
|
||||
{
|
||||
return $this->item->table($this->getTable())->set($item);
|
||||
return $this->tracker->get("save.{$table}.{$guid_field}|{$guid}");
|
||||
}
|
||||
|
||||
return false;
|
||||
if (($item = $this->grep->get($guid, $order, $repo)) !== null)
|
||||
{
|
||||
// pass item to the model to set the direct children
|
||||
$result = $this->item->table($table)->set($item, $guid_field);
|
||||
}
|
||||
|
||||
$this->tracker->set("save.{$table}.{$guid_field}|{$guid}", $result);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current active table
|
||||
* Normalize an object of objects (indexed by GUID):
|
||||
* - Each sub-object is normalized to have all keys in the order from getIndexHeader().
|
||||
* - Missing keys are filled with an empty string.
|
||||
* - The outer GUID keys are preserved.
|
||||
*
|
||||
* @return string
|
||||
* @since 3.2.2
|
||||
* @param object &$items Object of stdClass sub-objects, passed by reference
|
||||
*
|
||||
* @return void
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function getTable(): string
|
||||
public function normalizeObjectIndexHeader(object &$items): void
|
||||
{
|
||||
return $this->table;
|
||||
$canonicalKeys = $this->getIndexHeader();
|
||||
|
||||
$expectedCount = count($canonicalKeys);
|
||||
$template = array_fill_keys($canonicalKeys, '');
|
||||
|
||||
foreach ($items as $guid => &$subObject)
|
||||
{
|
||||
if (!is_object($subObject))
|
||||
{
|
||||
continue; // skip if not an object
|
||||
}
|
||||
|
||||
$vars = get_object_vars($subObject);
|
||||
|
||||
// Skip normalization if already compliant
|
||||
if (count($vars) === $expectedCount && array_keys($vars) === $canonicalKeys)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Normalize: enforce key order and fill missing values
|
||||
$subObject = (object) array_merge($template, $vars);
|
||||
}
|
||||
|
||||
unset($subObject); // break reference
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -12,13 +12,19 @@
|
||||
namespace VDM\Joomla\Abstraction\Remote;
|
||||
|
||||
|
||||
use Joomla\CMS\Language\Text;
|
||||
use VDM\Joomla\Interfaces\Remote\ConfigInterface as Config;
|
||||
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\Package\Dependency\Tracker;
|
||||
use VDM\Joomla\Componentbuilder\Package\MessageBus;
|
||||
use VDM\Joomla\Interfaces\Remote\Dependency\ResolverInterface as Resolver;
|
||||
use VDM\Joomla\Utilities\ObjectHelper;
|
||||
use VDM\Joomla\Interfaces\Remote\SetInterface;
|
||||
use VDM\Joomla\Abstraction\Remote\Base;
|
||||
|
||||
|
||||
/**
|
||||
@@ -26,7 +32,7 @@ use VDM\Joomla\Interfaces\Remote\SetInterface;
|
||||
*
|
||||
* @since 3.2.2
|
||||
*/
|
||||
abstract class Set implements SetInterface
|
||||
abstract class Set extends Base implements SetInterface
|
||||
{
|
||||
/**
|
||||
* The Grep Class.
|
||||
@@ -68,6 +74,33 @@ abstract class Set implements SetInterface
|
||||
*/
|
||||
protected Git $git;
|
||||
|
||||
/**
|
||||
* The Tracker Class.
|
||||
*
|
||||
* @var Tracker
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected Tracker $tracker;
|
||||
|
||||
/**
|
||||
* The Message Bus Class.
|
||||
*
|
||||
* @var MessageBus
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected MessageBus $messages;
|
||||
|
||||
/**
|
||||
* The Resolver Class
|
||||
*
|
||||
* Only set in some child classes
|
||||
* that has dependencies
|
||||
*
|
||||
* @var Resolver
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected ?Resolver $resolver = null;
|
||||
|
||||
/**
|
||||
* All active repos
|
||||
*
|
||||
@@ -76,90 +109,13 @@ abstract class Set implements SetInterface
|
||||
**/
|
||||
public array $repos;
|
||||
|
||||
/**
|
||||
* Table Name
|
||||
*
|
||||
* @var string
|
||||
* @since 3.2.2
|
||||
*/
|
||||
protected string $table;
|
||||
|
||||
/**
|
||||
* Area Name
|
||||
*
|
||||
* @var string
|
||||
* @since 3.2.2
|
||||
*/
|
||||
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';
|
||||
|
||||
/**
|
||||
* Core Placeholders
|
||||
*
|
||||
* @var array
|
||||
* @since 5.0.3
|
||||
*/
|
||||
protected array $placeholders = [
|
||||
'[['.'[NamespacePrefix]]]' => 'VDM',
|
||||
'[['.'[ComponentNamespace]]]' => 'Componentbuilder',
|
||||
'[['.'[Component]]]' => 'Componentbuilder',
|
||||
'[['.'[component]]]' => 'componentbuilder'
|
||||
];
|
||||
protected array $settings = [];
|
||||
|
||||
/**
|
||||
* Repo Placeholders
|
||||
@@ -172,142 +128,98 @@ abstract class Set implements SetInterface
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $repos The active repos
|
||||
* @param Config $config The Config Class.
|
||||
* @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 Tracker $tracker The Tracker Class.
|
||||
* @param MessageBus $messages The MessageBus Class.
|
||||
* @param array $repos The active repos.
|
||||
* @param string|null $table The table name.
|
||||
* @param string|null $settingsPath The settings path.
|
||||
* @param string|null $settingsIndexPath The index settings path.
|
||||
* @param string|null $settingsName The settings name.
|
||||
* @param string|null $indexPath The index 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)
|
||||
public function __construct(Config $config, Grep $grep, Items $items, ItemReadme $itemReadme,
|
||||
MainReadme $mainReadme, Git $git, Tracker $tracker, MessageBus $messages,
|
||||
array $repos, ?string $table = null, ?string $settingsName = null, ?string $indexPath = null)
|
||||
{
|
||||
$this->repos = $repos;
|
||||
parent::__construct($config);
|
||||
|
||||
$this->grep = $grep;
|
||||
$this->items = $items;
|
||||
$this->itemReadme = $itemReadme;
|
||||
$this->mainReadme = $mainReadme;
|
||||
$this->git = $git;
|
||||
$this->tracker = $tracker;
|
||||
$this->messages = $messages;
|
||||
$this->repos = $repos;
|
||||
|
||||
if ($table !== null)
|
||||
{
|
||||
$this->table = $table;
|
||||
$this->table($table);
|
||||
}
|
||||
|
||||
if ($settingsPath !== null)
|
||||
if ($settingsName !== null)
|
||||
{
|
||||
$this->settings_path = $settingsPath;
|
||||
$this->setSettingsName($settingsName);
|
||||
}
|
||||
|
||||
if ($settingsIndexPath !== null)
|
||||
if ($indexPath !== null)
|
||||
{
|
||||
$this->setIndexSettingsPath($settingsIndexPath);
|
||||
$this->setIndexPath($indexPath);
|
||||
}
|
||||
|
||||
if (empty($this->area))
|
||||
if ($this->getArea() === null)
|
||||
{
|
||||
$this->area = ucfirst(str_replace('_', ' ', $this->table));
|
||||
$this->area(ucfirst(str_replace('_', ' ', $this->getTable())));
|
||||
}
|
||||
|
||||
// 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())
|
||||
if (empty($guids))
|
||||
{
|
||||
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.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// we reset the index settings
|
||||
$this->settings = [];
|
||||
$this->grep->setBranchField('write_branch');
|
||||
$area = $this->getArea();
|
||||
$table = $this->getTable();
|
||||
|
||||
if (!$this->canWrite())
|
||||
{
|
||||
$target_network = $this->grep->getNetworkTarget() ?? $this->getArea();
|
||||
$this->messages->add('error', Text::sprintf('COM_COMPONENTBUILDER_AT_LEAST_ONE_S_CONTENT_REPOSITORY_MUST_BE_CONFIGURED_WITH_A_WRITE_BRANCH_VALUE_IN_THE_REPOSITORIES_AREA_FOR_THE_PUSH_FUNCTION_TO_OPERATE_CORRECTLY', $target_network));
|
||||
return false;
|
||||
}
|
||||
|
||||
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.");
|
||||
$this->messages->add('warning', Text::sprintf('COM_COMPONENTBUILDER_THE_S_ITEMS_COULD_NOT_BE_FOUND', strtolower($area)));
|
||||
return false;
|
||||
}
|
||||
|
||||
$counter = 0;
|
||||
foreach ($items as $item)
|
||||
{
|
||||
$this->save($item);
|
||||
if ($this->save($item))
|
||||
{
|
||||
$counter++;
|
||||
}
|
||||
}
|
||||
|
||||
// update the repos main readme and index settings
|
||||
@@ -319,7 +231,15 @@ abstract class Set implements SetInterface
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
// add a message per area once
|
||||
if ($counter > 0 && !$this->tracker->exists("message.{$table}"))
|
||||
{
|
||||
$this->tracker->set("message.{$table}", true);
|
||||
$item_name = $counter == 1 ? 'item has' : 'items have';
|
||||
$this->messages->add('success', Text::sprintf('COM_COMPONENTBUILDER_S_S_BEEN_PUSHED_SUCCESSFULLY', $area, $item_name));
|
||||
}
|
||||
|
||||
return $counter === count($items);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -340,10 +260,10 @@ abstract class Set implements SetInterface
|
||||
* @param object $item
|
||||
* @param object $repo
|
||||
*
|
||||
* @return void
|
||||
* @return bool
|
||||
* @since 3.2.2
|
||||
*/
|
||||
abstract protected function createItem(object $item, object $repo): void;
|
||||
abstract protected function createItem(object $item, object $repo): bool;
|
||||
|
||||
/**
|
||||
* update an existing item readme
|
||||
@@ -368,28 +288,6 @@ abstract class Set implements SetInterface
|
||||
*/
|
||||
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
|
||||
*
|
||||
@@ -416,23 +314,42 @@ abstract class Set implements SetInterface
|
||||
|
||||
$settings = $this->mergeIndexSettings($repoGuid, $settings);
|
||||
|
||||
$this->grep->loadApi($this->git, $repo->base ?? null, $repo->token ?? null);
|
||||
// set the target system
|
||||
$target = $repo->target ?? 'gitea';
|
||||
$this->git->setTarget($target);
|
||||
|
||||
$this->updateIndexMainFile(
|
||||
$repo,
|
||||
$this->getIndexSettingsPath(),
|
||||
json_encode($settings, JSON_PRETTY_PRINT),
|
||||
'Update main index file'
|
||||
// load the base and token if set
|
||||
$this->grep->loadApi(
|
||||
$this->git,
|
||||
$repo->base ?? null,
|
||||
$repo->token ?? null
|
||||
);
|
||||
|
||||
$this->updateIndexMainFile(
|
||||
$repo,
|
||||
'README.md',
|
||||
$this->mainReadme->get($settings),
|
||||
'Update main readme file'
|
||||
);
|
||||
|
||||
$this->git->reset_();
|
||||
try {
|
||||
$indexPath = $this->getIndexPath();
|
||||
if (!empty($indexPath))
|
||||
{
|
||||
$this->setMainRepoFile(
|
||||
$repo,
|
||||
$indexPath,
|
||||
json_encode($settings, JSON_PRETTY_PRINT),
|
||||
'Update main index file', 'Create main index file'
|
||||
);
|
||||
}
|
||||
if ($this->hasMainReadme())
|
||||
{
|
||||
$this->setMainRepoFile(
|
||||
$repo,
|
||||
$this->getMainReadmePath(),
|
||||
$this->mainReadme->get($settings),
|
||||
'Update main readme file', 'Create main readme file'
|
||||
);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$this->messages->add('error', $e->getMessage());
|
||||
} finally {
|
||||
$this->git->reset_();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -487,32 +404,69 @@ abstract class Set implements SetInterface
|
||||
* @param object $repo
|
||||
* @param string $path
|
||||
* @param string $content
|
||||
* @param string $message
|
||||
* @param string $updateMessage
|
||||
* @param string $createMessage
|
||||
*
|
||||
* @return void
|
||||
* @since 3.2.2
|
||||
*/
|
||||
protected function updateIndexMainFile(object $repo, string $path,
|
||||
string $content, string $message): void
|
||||
protected function setMainRepoFile(object $repo, string $path,
|
||||
string $content, string $updateMessage, string $createMessage): void
|
||||
{
|
||||
$meta = $this->git->metadata(
|
||||
$repo->organisation,
|
||||
$repo->repository,
|
||||
$path,
|
||||
$repo->write_branch
|
||||
);
|
||||
|
||||
if ($meta !== null && isset($meta->sha))
|
||||
{
|
||||
$this->git->update(
|
||||
try {
|
||||
$meta = $this->git->metadata(
|
||||
$repo->organisation,
|
||||
$repo->repository,
|
||||
$path,
|
||||
$content,
|
||||
$message,
|
||||
$meta->sha,
|
||||
$repo->write_branch
|
||||
);
|
||||
} catch (\Throwable $e) {
|
||||
$meta = null;
|
||||
}
|
||||
|
||||
if ($meta !== null && isset($meta->sha))
|
||||
{
|
||||
// Calculate the new SHA from the current content
|
||||
$newSha = sha1("blob " . strlen($content) . "\0" . $content);
|
||||
|
||||
// Check if the new SHA matches the existing SHA
|
||||
if ($meta->sha === $newSha)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->git->update(
|
||||
$repo->organisation, // The owner name.
|
||||
$repo->repository, // The repository name.
|
||||
$path, // The file path.
|
||||
$content, // The file content.
|
||||
$updateMessage, // The commit message.
|
||||
$meta->sha, // The previous sha value.
|
||||
$repo->write_branch, // The branch name.
|
||||
$repo->author_name, // The author name.
|
||||
$repo->author_email // The author name.
|
||||
);
|
||||
} catch (\Throwable $e) {
|
||||
$this->messages->add('error', $e->getMessage());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try {
|
||||
$this->git->create(
|
||||
$repo->organisation, // The owner name.
|
||||
$repo->repository, // The repository name.
|
||||
$path, // The file path.
|
||||
$content, // The file content.
|
||||
$createMessage, // The commit message.
|
||||
$repo->write_branch, // The branch name.
|
||||
$repo->author_name, // The author name.
|
||||
$repo->author_email // The author name.
|
||||
);
|
||||
} catch (\Throwable $e) {
|
||||
$this->messages->add('error', $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -526,93 +480,126 @@ abstract class Set implements SetInterface
|
||||
*/
|
||||
protected function getLocalItems(array $guids): ?array
|
||||
{
|
||||
return $this->items->table($this->getTable())->get($guids);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$methodName = "mapItemValue_{$key}";
|
||||
if (method_exists($this, $methodName))
|
||||
{
|
||||
$this->{$methodName}($item, $power);
|
||||
}
|
||||
else
|
||||
{
|
||||
$power[$key] = $item->{$map} ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
return (object) $power;
|
||||
$guid_field = $this->getGuidField();
|
||||
return $this->items->table($this->getTable())->get($guids, $guid_field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save an item remotely
|
||||
*
|
||||
* @param object $item The item to save
|
||||
* @param object $rawItem The item to save
|
||||
*
|
||||
* @return void
|
||||
* @return bool
|
||||
* @since 3.2.2
|
||||
*/
|
||||
protected function save(object $item): void
|
||||
protected function save(object $rawItem): bool
|
||||
{
|
||||
if (empty($item->guid))
|
||||
$index_item = null;
|
||||
$item = $this->mapItem($rawItem);
|
||||
$area = $this->getArea();
|
||||
$item_id = $rawItem->id ?? '_no_id_found_';
|
||||
$item_name = $this->index_map_IndexName($item);
|
||||
|
||||
$guid_field = $this->getGuidField();
|
||||
if (empty($item->{$guid_field}))
|
||||
{
|
||||
return;
|
||||
$this->messages->add('error', Text::sprintf('COM_COMPONENTBUILDER_S_ITEM_S_ID_S_MISSING_THE_S_KEY_VALUE', $area, $item_name, $item_id, $guid_field));
|
||||
return false;
|
||||
}
|
||||
$table = $this->getTable();
|
||||
$guid = $item->{$guid_field};
|
||||
|
||||
// check if we have saved this entity already
|
||||
if ($this->tracker->exists("save.{$table}.{$guid_field}|{$guid}"))
|
||||
{
|
||||
return $this->tracker->get("save.{$table}.{$guid_field}|{$guid}");
|
||||
}
|
||||
|
||||
$index_item = null;
|
||||
// pass item to the inspector/resolver to get all dependencies
|
||||
$dependencies = $this->getDependencies($item);
|
||||
if ($dependencies !== null)
|
||||
{
|
||||
foreach ($dependencies as $key => $dependency)
|
||||
{
|
||||
$item->{$key} = $dependency;
|
||||
}
|
||||
}
|
||||
|
||||
$at_least_once = false;
|
||||
$not_approved = true;
|
||||
foreach ($this->repos as $key => $repo)
|
||||
{
|
||||
if (empty($repo->write_branch) || $repo->write_branch === 'default' || !$this->targetRepo($item, $repo))
|
||||
if (empty($repo->write_branch) || $repo->write_branch === 'default' || !$this->targetRepo($rawItem, $repo))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$item = $this->mapItem($item);
|
||||
$not_approved = false;
|
||||
|
||||
$this->setRepoPlaceholders($repo);
|
||||
|
||||
$this->grep->loadApi($this->git, $repo->base ?? null, $repo->token ?? null);
|
||||
// set the target system
|
||||
$target_system = $repo->target ?? 'gitea';
|
||||
$this->git->setTarget($target_system);
|
||||
|
||||
if (($existing = $this->grep->get($item->guid, ['remote'], $repo)) !== null)
|
||||
{
|
||||
if ($this->updateItem($item, $existing, $repo))
|
||||
// load the base and token if set
|
||||
$this->grep->loadApi(
|
||||
$this->git,
|
||||
$repo->base ?? null,
|
||||
$repo->token ?? null
|
||||
);
|
||||
|
||||
try {
|
||||
if (($existing = $this->grep->get($guid, ['remote'], $repo)) !== null)
|
||||
{
|
||||
$this->updateItemReadme($item, $existing, $repo);
|
||||
if ($this->updateItem($item, $existing, $repo))
|
||||
{
|
||||
$this->updateItemReadme($item, $existing, $repo);
|
||||
$at_least_once = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->createItem($item, $repo);
|
||||
|
||||
$this->createItemReadme($item, $repo);
|
||||
|
||||
$index_item ??= $this->getIndexItem($item);
|
||||
|
||||
if (!isset($this->settings[$key]))
|
||||
elseif ($this->createItem($item, $repo))
|
||||
{
|
||||
$this->settings[$key] = ['repo' => $repo, 'items' => [$item->guid => $index_item]];
|
||||
$this->createItemReadme($item, $repo);
|
||||
|
||||
$index_item ??= $this->getIndexItem($item);
|
||||
|
||||
$at_least_once = true;
|
||||
|
||||
if (!isset($this->settings[$key]))
|
||||
{
|
||||
$this->settings[$key] = ['repo' => $repo, 'items' => [$guid => $index_item]];
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->settings[$key]['items'][$guid] = $index_item;
|
||||
$this->settings[$key]['repo'] = $repo;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->settings[$key]['items'][$item->guid] = $index_item;
|
||||
$repo_name = $this->getRepoName($repo);
|
||||
$this->messages->add('error',
|
||||
Text::sprintf('COM_COMPONENTBUILDER_S_ITEM_S_ID_S_COULD_NOT_BE_CREATED_OR_FOUND_IN_REPOS',
|
||||
$area, $item_name, $item_id, $repo_name));
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$repo_name = $this->getRepoName($repo);
|
||||
$this->messages->add('error',
|
||||
Text::sprintf('COM_COMPONENTBUILDER_S_ITEM_S_ID_S_ENCOUNTERED_AN_ERROR_IN_REPOSBRERROR_MESSAGEBRS',
|
||||
$area, $item_name, $item_id, $repo_name, $e->getMessage()));
|
||||
} finally {
|
||||
$this->git->reset_();
|
||||
}
|
||||
|
||||
$this->git->reset_();
|
||||
}
|
||||
|
||||
if (!$at_least_once && $not_approved)
|
||||
{
|
||||
$this->messages->add('warning', Text::sprintf('COM_COMPONENTBUILDER_S_ITEM_S_ID_S_IS_NOT_APPROVED_AND_THEREFORE_NOT_LINKED_TO_ANY_REPOSITORY', $area, $item_name, $item_id));
|
||||
}
|
||||
|
||||
$this->tracker->set("save.{$table}.{$guid_field}|{$guid}", $at_least_once);
|
||||
|
||||
return $at_least_once;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -625,7 +612,7 @@ abstract class Set implements SetInterface
|
||||
*/
|
||||
protected function setRepoPlaceholders(object $repo): void
|
||||
{
|
||||
$this->repoPlaceholders = $this->placeholders;
|
||||
$this->repoPlaceholders = $this->getPlaceholders();
|
||||
if (!empty($repo->placeholders) && is_array($repo->placeholders))
|
||||
{
|
||||
foreach ($repo->placeholders as $key => $value)
|
||||
@@ -635,6 +622,23 @@ abstract class Set implements SetInterface
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the dependencies of this item
|
||||
*
|
||||
* @param object $item The item to resolve the dependencies
|
||||
*
|
||||
* @return array|null The array of relationships or null for none
|
||||
* @since 3.2.2
|
||||
*/
|
||||
protected function getDependencies($item): ?array
|
||||
{
|
||||
if ($this->resolver instanceof Resolver)
|
||||
{
|
||||
return $this->resolver->extract($item);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update Placeholders in String
|
||||
*
|
||||
@@ -652,33 +656,6 @@ abstract class Set implements SetInterface
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get index values
|
||||
*
|
||||
* @param object $item The item
|
||||
*
|
||||
* @return array|null
|
||||
* @since 3.2.2
|
||||
*/
|
||||
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
|
||||
*
|
||||
@@ -712,6 +689,23 @@ abstract class Set implements SetInterface
|
||||
return true; // for more control in children classes
|
||||
}
|
||||
|
||||
/**
|
||||
* get the name of the repo
|
||||
*
|
||||
* @param object $repo The current repo
|
||||
*
|
||||
* @return string
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected function getRepoName(object $repo): string
|
||||
{
|
||||
$base = $repo->base ?? '[core]';
|
||||
$organisation = $repo->organisation ?? '[organisation]';
|
||||
$repository = $repo->repository ?? '[repository]';
|
||||
|
||||
return "{$base}/{$organisation}/{$repository}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if two objects are equal by comparing their properties and values.
|
||||
*
|
||||
@@ -728,96 +722,7 @@ abstract class Set implements SetInterface
|
||||
*/
|
||||
protected function areObjectsEqual(?object $obj1, ?object $obj2): bool
|
||||
{
|
||||
return ObjectHelper::equal($obj1, $obj2); // basic comparison
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
return ObjectHelper::equal($obj1, $obj2, ['@dependencies']); // basic comparison
|
||||
}
|
||||
}
|
||||
|
||||
|
370
libraries/vendor_jcb/VDM.Joomla/src/Abstraction/Versioning.php
Normal file
370
libraries/vendor_jcb/VDM.Joomla/src/Abstraction/Versioning.php
Normal file
@@ -0,0 +1,370 @@
|
||||
<?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\Application\CMSApplicationInterface as CMSApplication;
|
||||
use Joomla\CMS\Component\ComponentHelper;
|
||||
use Joomla\CMS\Date\Date;
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Table\ContentHistory;
|
||||
use Joomla\CMS\Table\ContentType;
|
||||
use Joomla\CMS\Table\TableInterface;
|
||||
use Joomla\CMS\User\User;
|
||||
use Joomla\Database\DatabaseInterface as JoomlaDatabase;
|
||||
use Joomla\Registry\Registry;
|
||||
use VDM\Joomla\Utilities\Component\Helper;
|
||||
use VDM\Joomla\Interfaces\Database\VersioningInterface;
|
||||
use VDM\Joomla\Abstraction\Database;
|
||||
|
||||
|
||||
/**
|
||||
* Versioning
|
||||
*
|
||||
* @since 5.1.1
|
||||
*/
|
||||
abstract class Versioning extends Database implements VersioningInterface
|
||||
{
|
||||
/**
|
||||
* CMS Application
|
||||
*
|
||||
* @var CMSApplication
|
||||
* @since 5.1.1
|
||||
**/
|
||||
protected CMSApplication $app;
|
||||
|
||||
/**
|
||||
* Joomla History Class
|
||||
*
|
||||
* @var ContentHistory
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected ContentHistory $contentHistory;
|
||||
|
||||
/**
|
||||
* Joomla Content Type Class
|
||||
*
|
||||
* @var ContentType
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected ContentType $typeTable;
|
||||
|
||||
/**
|
||||
* Current component params
|
||||
*
|
||||
* @var Registry
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected Registry $params;
|
||||
|
||||
/**
|
||||
* Current user ID
|
||||
*
|
||||
* @var int
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected int $userId;
|
||||
|
||||
/**
|
||||
* Current component code name
|
||||
*
|
||||
* @var string
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected string $componentNamespace;
|
||||
|
||||
/**
|
||||
* The current entity
|
||||
*
|
||||
* @var string|null
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected ?string $entity;
|
||||
|
||||
/**
|
||||
* Switch to set the history
|
||||
*
|
||||
* @var int
|
||||
* @since 5.1.1
|
||||
**/
|
||||
protected int $history;
|
||||
|
||||
/**
|
||||
* Number of max item versions to store in history
|
||||
*
|
||||
* @var int
|
||||
* @since 5.1.1
|
||||
**/
|
||||
protected int $maxVersions;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* Initializes the component context by setting the application, database,
|
||||
* content history tracking, and content type table instances. Also loads
|
||||
* component-specific parameters like history tracking and version limits.
|
||||
*
|
||||
* @param JoomlaDatabase|null $db Optional database object. Defaults to Joomla's factory DB.
|
||||
* @param CMSApplication|null $app Optional application object. Defaults to Factory::getApplication().
|
||||
* @param ContentHistory|null $history Optional content history table instance. Defaults to new ContentHistory.
|
||||
* @param ContentType|null $typeTable Optional content type table instance. Defaults to new ContentType.
|
||||
*
|
||||
* @throws \Exception If the parent constructor or any dependency throws.
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function __construct(?JoomlaDatabase $db = null, ?CMSApplication $app = null,
|
||||
?ContentHistory $history = null, ?ContentType $typeTable = null)
|
||||
{
|
||||
parent::__construct($db);
|
||||
|
||||
$this->app = $app ?: Factory::getApplication();
|
||||
$this->contentHistory = $history ?: new ContentHistory($this->db);
|
||||
$this->typeTable = $typeTable ?: new ContentType($this->db);
|
||||
|
||||
$user = $this->app->getIdentity();
|
||||
$this->userId = $user instanceof User ? (int) $user->id : 0;
|
||||
|
||||
// set the component details
|
||||
$this->componentNamespace = Helper::getNamespace();
|
||||
$this->params = Helper::getParams();
|
||||
$this->history = $this->params->get('save_history', 0);
|
||||
$this->maxVersions = $this->params->get('history_limit', 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch to prevent/allow history from being set.
|
||||
*
|
||||
* @param int|null $trigger toggle the history (0 = no, 1 = yes, null = default)
|
||||
*
|
||||
* @return self
|
||||
* @since 5.1.1
|
||||
**/
|
||||
public function history(?int $trigger = null): self
|
||||
{
|
||||
$this->history = $trigger !== null ? $trigger : $this->params->get('save_history', 0);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a history record for a stored item.
|
||||
*
|
||||
* @param int $id The ID of the record
|
||||
*
|
||||
* @return bool True if saved, false if skipped or failed
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected function setHistory(int $id): bool
|
||||
{
|
||||
$tableClass = $this->getTableClass();
|
||||
|
||||
if ($tableClass === null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @var TableInterface $table */
|
||||
$table = new $tableClass($this->db);
|
||||
|
||||
if (!$table->load($id))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// set the type alias
|
||||
$type_alias = 'com_' . $this->componentCode . '.' . $this->entity;
|
||||
|
||||
$item = (object) $table->getProperties();
|
||||
unset($item->typeAlias, $item->tagsHelper);
|
||||
|
||||
// Required: item_id, version_data, editor_user_id
|
||||
$this->contentHistory->reset();
|
||||
$this->contentHistory->version_id = null;
|
||||
$this->contentHistory->item_id = $type_alias . '.' . $id;
|
||||
$this->contentHistory->version_note = '';
|
||||
$this->contentHistory->version_data = json_encode($item);
|
||||
$this->contentHistory->editor_user_id = $this->userId;
|
||||
$this->contentHistory->save_date = (new Date())->toSql();
|
||||
|
||||
// Don't save if hash already exists and same version note
|
||||
$this->typeTable->load(['type_alias' => $type_alias]);
|
||||
$this->contentHistory->sha1_hash = $this->contentHistory->getSha1($item, $this->typeTable);
|
||||
|
||||
if ($this->contentHistory->getHashMatch())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
$result = $this->contentHistory->store();
|
||||
|
||||
$max_versions_context = $this->params->get('history_limit_' . $this->entity, 0);
|
||||
|
||||
if ($max_versions_context)
|
||||
{
|
||||
$this->contentHistory->deleteOldVersions($max_versions_context);
|
||||
}
|
||||
elseif ($this->maxVersions)
|
||||
{
|
||||
$this->contentHistory->deleteOldVersions($this->maxVersions);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save multiple version records for already stored items.
|
||||
*
|
||||
* @param int[] $ids Array of IDs
|
||||
* @param string $entity Table entity name
|
||||
*
|
||||
* @return int Number of successful version saves
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected function setMultipleHistory(array $ids): int
|
||||
{
|
||||
$tableClass = $this->getTableClass();
|
||||
|
||||
if ($tableClass === null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @var TableInterface $table */
|
||||
$table = new $tableClass($this->db);
|
||||
|
||||
// set some var needed in loop
|
||||
$date = (new Date())->toSql();
|
||||
$max_versions_context = $this->params->get('history_limit_' . $this->entity, 0);
|
||||
$type_alias = 'com_' . $this->componentCode . '.' . $this->entity;
|
||||
$this->typeTable->load(['type_alias' => $type_alias]);
|
||||
$count = 0;
|
||||
|
||||
foreach ($ids as $id)
|
||||
{
|
||||
$id = (int) $id;
|
||||
if ($id <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$table->load($id))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$item = (object) $table->getProperties();
|
||||
unset($item->typeAlias, $item->tagsHelper);
|
||||
|
||||
$this->contentHistory->reset();
|
||||
$this->contentHistory->version_id = null;
|
||||
$this->contentHistory->item_id = $type_alias . '.' . $id;
|
||||
$this->contentHistory->version_note = '';
|
||||
$this->contentHistory->version_data = json_encode($item);
|
||||
$this->contentHistory->editor_user_id = $this->userId;
|
||||
$this->contentHistory->save_date = $date;
|
||||
|
||||
// Don't save if hash already exists and same version note
|
||||
$this->contentHistory->sha1_hash = $this->contentHistory->getSha1($item, $this->typeTable);
|
||||
|
||||
if ($this->contentHistory->getHashMatch())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$result = $this->contentHistory->store();
|
||||
|
||||
if ($max_versions_context)
|
||||
{
|
||||
$this->contentHistory->deleteOldVersions($max_versions_context);
|
||||
}
|
||||
elseif ($this->maxVersions)
|
||||
{
|
||||
$this->contentHistory->deleteOldVersions($this->maxVersions);
|
||||
}
|
||||
|
||||
if ($result)
|
||||
{
|
||||
++$count;
|
||||
}
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fully qualified class name for a table if it exists.
|
||||
*
|
||||
* This method first extracts the base table name using `getTableName`.
|
||||
* If the extraction fails (e.g., wrong component prefix), it returns null.
|
||||
* If successful, it constructs the FQCN in the format:
|
||||
* \Namespace\Component\ComponentName\Administrator\Table\TableNameTable
|
||||
*
|
||||
* The table name is converted to PascalCase and suffixed with `Table`.
|
||||
* The constructed class name is verified with `class_exists`.
|
||||
*
|
||||
* @return string|null The fully qualified class name, or null if it does not exist.
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected function getTableClass(): ?string
|
||||
{
|
||||
if (empty($this->entity))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
$tableClass = ucfirst($this->entity) . 'Table';
|
||||
|
||||
$class = $this->componentNamespace . '\\Administrator\\Table\\' . $tableClass;
|
||||
if (!class_exists($class))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the actual table name by removing the component prefix.
|
||||
*
|
||||
* This method checks whether the given table name includes the component-specific prefix,
|
||||
* which usually starts with `#__` followed by the component name and an underscore (e.g., `#__mycomponent_`).
|
||||
* If it matches this instance's component prefix stored in `$this->table`, the prefix is stripped and the short table name is returned.
|
||||
* If the prefix is different (implying a foreign component), `null` is returned.
|
||||
* If no prefix is present, the original value is returned unchanged.
|
||||
*
|
||||
* @param string $table The full or short table name.
|
||||
*
|
||||
* @return string|null The stripped table name, original if no prefix is found, or null if not removable.
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected function getTableEntityName(string $table): ?string
|
||||
{
|
||||
if (strpos($table, '#__') === false)
|
||||
{
|
||||
return $table;
|
||||
}
|
||||
|
||||
if (empty($this->table))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
$prefix = $this->table . '_';
|
||||
if (str_starts_with($table, $prefix))
|
||||
{
|
||||
return substr($table, strlen($prefix));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@@ -315,6 +315,11 @@ class Data
|
||||
*/
|
||||
public function get($view): ?object
|
||||
{
|
||||
if (empty($view))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isset($this->index[$view]))
|
||||
{
|
||||
$id = $this->index[$view];
|
||||
@@ -478,6 +483,13 @@ class Data
|
||||
'jcb_ce_onBeforeModelViewData', [&$view]
|
||||
);
|
||||
|
||||
// should we add sql?
|
||||
if ($view->add_sql !== 1)
|
||||
{
|
||||
unset($view->addtables);
|
||||
unset($view->sql);
|
||||
}
|
||||
|
||||
// add the tables
|
||||
$view->addtables = (isset($view->addtables) && JsonHelper::check($view->addtables))
|
||||
? json_decode((string) $view->addtables, true)
|
||||
|
@@ -12,7 +12,7 @@
|
||||
namespace VDM\Joomla\Componentbuilder\Compiler\Architecture\JoomlaFive\Plugin;
|
||||
|
||||
|
||||
use Joomla\CMS\Filesystem\Folder;
|
||||
use Joomla\Filesystem\Folder;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Config;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Language;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Language\Set;
|
||||
@@ -542,7 +542,7 @@ final class MainXML implements MainXMLInterface
|
||||
|
||||
$path = "{$plugin->folder_path}/language/{$tag}/";
|
||||
|
||||
if (!Folder::exists($path))
|
||||
if (!is_dir($path))
|
||||
{
|
||||
Folder::create($path);
|
||||
$this->counter->folder++;
|
||||
|
@@ -12,7 +12,7 @@
|
||||
namespace VDM\Joomla\Componentbuilder\Compiler\Architecture\JoomlaFour\Plugin;
|
||||
|
||||
|
||||
use Joomla\CMS\Filesystem\Folder;
|
||||
use Joomla\Filesystem\Folder;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Config;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Language;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Language\Set;
|
||||
@@ -542,7 +542,7 @@ final class MainXML implements MainXMLInterface
|
||||
|
||||
$path = "{$plugin->folder_path}/language/{$tag}/";
|
||||
|
||||
if (!Folder::exists($path))
|
||||
if (!is_dir($path))
|
||||
{
|
||||
Folder::create($path);
|
||||
$this->counter->folder++;
|
||||
|
@@ -12,7 +12,7 @@
|
||||
namespace VDM\Joomla\Componentbuilder\Compiler\Architecture\JoomlaThree\Plugin;
|
||||
|
||||
|
||||
use Joomla\CMS\Filesystem\Folder;
|
||||
use Joomla\Filesystem\Folder;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Config;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Language;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Language\Set;
|
||||
@@ -543,7 +543,7 @@ final class MainXML implements MainXMLInterface
|
||||
|
||||
$path = "{$plugin->folder_path}/language/{$tag}/";
|
||||
|
||||
if (!Folder::exists($path))
|
||||
if (!is_dir($path))
|
||||
{
|
||||
Folder::create($path);
|
||||
$this->counter->folder++;
|
||||
|
@@ -44,21 +44,25 @@ final class PermissionDashboard extends Registry implements Registryinterface
|
||||
use VarExport;
|
||||
|
||||
/**
|
||||
* Get the build permission dashboard code
|
||||
* Get the build permission dashboard code.
|
||||
*
|
||||
* @return string
|
||||
* @since 3.2.0
|
||||
* @return string
|
||||
* @since 3.2.0
|
||||
* @since 5.1.1 Changed to class property.
|
||||
*/
|
||||
public function build(): string
|
||||
{
|
||||
if ($this->isActive())
|
||||
{
|
||||
return PHP_EOL . Indent::_(2) . "//" . Line::_(__Line__, __Class__)
|
||||
. " view access array" . PHP_EOL . Indent::_(2)
|
||||
. "\$viewAccess = " . $this->varExport() . ';';
|
||||
}
|
||||
$indent = Indent::_(1);
|
||||
$docBlock = PHP_EOL . $indent . '/**'
|
||||
. PHP_EOL . $indent . ' *' . Line::_(__LINE__, __CLASS__) . ' View access array.'
|
||||
. PHP_EOL . $indent . ' *'
|
||||
. PHP_EOL . $indent . ' * @var array<string, string>'
|
||||
. PHP_EOL . $indent . ' * @since 5.1.1'
|
||||
. PHP_EOL . $indent . ' */';
|
||||
|
||||
return '';
|
||||
$value = $this->isActive() ? $this->varExport(null, 1) : '[]';
|
||||
|
||||
return $docBlock . PHP_EOL . $indent . 'protected array $viewAccess = ' . $value . ';' . PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -15,7 +15,6 @@ namespace VDM\Joomla\Componentbuilder\Compiler\Component;
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\CMS\Application\CMSApplication;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Factory as Compiler;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Registry;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Component;
|
||||
use VDM\Joomla\Utilities\StringHelper;
|
||||
@@ -30,9 +29,9 @@ use VDM\Joomla\Utilities\ArrayHelper;
|
||||
final class Dashboard
|
||||
{
|
||||
/**
|
||||
* The compiler registry
|
||||
* Compiler Registry
|
||||
*
|
||||
* @var Registry
|
||||
* @var Registry
|
||||
* @since 3.2.0
|
||||
*/
|
||||
protected Registry $registry;
|
||||
@@ -40,34 +39,54 @@ final class Dashboard
|
||||
/**
|
||||
* Compiler Component
|
||||
*
|
||||
* @var Component
|
||||
* @var Component
|
||||
* @since 3.2.0
|
||||
**/
|
||||
*/
|
||||
protected Component $component;
|
||||
|
||||
/**
|
||||
* Application object.
|
||||
* Joomla Application
|
||||
*
|
||||
* @var CMSApplication
|
||||
* @var CMSApplication
|
||||
* @since 3.2.0
|
||||
**/
|
||||
*/
|
||||
protected CMSApplication $app;
|
||||
|
||||
/**
|
||||
* Supported Dashboard Target Types
|
||||
*
|
||||
* @var array<string, array<string, string>>
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected const DASHBOARD_TARGETS = [
|
||||
'A' => [
|
||||
'viewsKey' => 'admin_views',
|
||||
'typeKey' => 'adminview',
|
||||
'name' => 'admin view',
|
||||
'settingsKey' => 'name_list',
|
||||
],
|
||||
'C' => [
|
||||
'viewsKey' => 'custom_admin_views',
|
||||
'typeKey' => 'customadminview',
|
||||
'name' => 'custom admin view',
|
||||
'settingsKey' => 'code',
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param Registry|null $registry The compiler registry object.
|
||||
* @param Component|null $component The component class.
|
||||
* @param CMSApplication|null $app The app object.
|
||||
* @param Registry $registry The registry instance.
|
||||
* @param Component $component The component instance.
|
||||
* @param CMSApplication|null $app The application instance.
|
||||
*
|
||||
* @since 3.2.0
|
||||
*/
|
||||
public function __construct(?Registry $registry = null, ?Component $component = null,
|
||||
?CMSApplication $app = null)
|
||||
public function __construct(Registry $registry, Component $component, ?CMSApplication $app = null)
|
||||
{
|
||||
$this->registry = $registry ?: Compiler::_('Registry');
|
||||
$this->component = $component ?: Compiler::_('Component');
|
||||
$this->app = $app ?: Factory::getApplication();
|
||||
$this->registry = $registry;
|
||||
$this->component = $component;
|
||||
$this->app = $app ?? Factory::getApplication();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,137 +95,157 @@ final class Dashboard
|
||||
* @return void
|
||||
* @since 3.2.0
|
||||
*/
|
||||
public function set()
|
||||
public function set(): void
|
||||
{
|
||||
// only add the dynamic dashboard if all checks out
|
||||
if ($this->component->get('dashboard_type', 0) == 2
|
||||
&& ($dashboard_ = $this->component->get('dashboard')) !== null
|
||||
&& StringHelper::check($dashboard_)
|
||||
&& strpos((string) $dashboard_, '_') !== false)
|
||||
if (!$this->isDynamicDashboardEnabled())
|
||||
{
|
||||
// set the default view
|
||||
$getter = explode('_', (string) $dashboard_);
|
||||
if (count((array) $getter) == 2 && is_numeric($getter[1]))
|
||||
return;
|
||||
}
|
||||
|
||||
$rawDashboard = (string) $this->component->get('dashboard');
|
||||
|
||||
if (!$this->isValidDashboardFormat($rawDashboard))
|
||||
{
|
||||
$this->notifyInvalidDashboard($rawDashboard);
|
||||
return;
|
||||
}
|
||||
|
||||
[$targetKey, $identifier] = explode('_', $rawDashboard, 2);
|
||||
$targetKey = strtoupper($targetKey);
|
||||
|
||||
if (!isset(self::DASHBOARD_TARGETS[$targetKey]))
|
||||
{
|
||||
$this->notifyInvalidDashboard($rawDashboard);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->handleDashboardTarget($targetKey, $identifier, $rawDashboard);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if dynamic dashboard is enabled.
|
||||
*
|
||||
* @return bool
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected function isDynamicDashboardEnabled(): bool
|
||||
{
|
||||
return (int) $this->component->get('dashboard_type', 0) === 2
|
||||
&& $this->component->get('dashboard') !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the dashboard format (e.g., "A_23" or "C_abcd1234").
|
||||
*
|
||||
* @param string $dashboard The dashboard value.
|
||||
*
|
||||
* @return bool
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected function isValidDashboardFormat(string $dashboard): bool
|
||||
{
|
||||
return StringHelper::check($dashboard) && strpos($dashboard, '_') !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the dashboard target and attempt to set it in the registry.
|
||||
*
|
||||
* @param string $key The dashboard type key (e.g. A or C).
|
||||
* @param string $identifier The numeric ID or GUID string.
|
||||
* @param string $rawDashboard The original dashboard string value.
|
||||
*
|
||||
* @return void
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected function handleDashboardTarget(string $key, string $identifier, string $rawDashboard): void
|
||||
{
|
||||
$target = self::DASHBOARD_TARGETS[$key];
|
||||
|
||||
$views = $this->component->get($target['viewsKey']);
|
||||
|
||||
if (!ArrayHelper::check($views))
|
||||
{
|
||||
$this->notifyDashboardNotFound($target['name'], $rawDashboard, $target['viewsKey']);
|
||||
return;
|
||||
}
|
||||
|
||||
$matchedView = $this->findViewByIdentifier((array) $views, $target['typeKey'], $identifier);
|
||||
|
||||
if ($matchedView !== null && isset($matchedView['settings']->{$target['settingsKey']}))
|
||||
{
|
||||
$this->registry->set('build.dashboard', StringHelper::safe($matchedView['settings']->{$target['settingsKey']}));
|
||||
$this->registry->set('build.dashboard.type', $target['viewsKey']);
|
||||
|
||||
$this->component->remove('dashboard_tab');
|
||||
$this->component->remove('php_dashboard_methods');
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->notifyDashboardNotFound($target['name'], $rawDashboard, $target['viewsKey']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a matching view by numeric ID or GUID.
|
||||
*
|
||||
* @param array<string, mixed> $views List of views to search.
|
||||
* @param string $field Field name to match (e.g. adminview or customadminview).
|
||||
* @param string $identifier ID or GUID string.
|
||||
*
|
||||
* @return array<string, mixed>|null
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected function findViewByIdentifier(array $views, string $field, string $identifier): ?array
|
||||
{
|
||||
foreach ($views as $view)
|
||||
{
|
||||
if (isset($view[$field]) && (string) $view[$field] === $identifier)
|
||||
{
|
||||
// the pointers
|
||||
$t = StringHelper::safe($getter[0], 'U');
|
||||
$id = (int) $getter[1];
|
||||
|
||||
// the dynamic stuff
|
||||
$targets = array('A' => 'admin_views',
|
||||
'C' => 'custom_admin_views');
|
||||
$names = array('A' => 'admin view',
|
||||
'C' => 'custom admin view');
|
||||
$types = array('A' => 'adminview', 'C' => 'customadminview');
|
||||
$keys = array('A' => 'name_list', 'C' => 'code');
|
||||
|
||||
// check the target values
|
||||
if (isset($targets[$t]) && $id > 0)
|
||||
{
|
||||
// set the type name
|
||||
$type_names = StringHelper::safe(
|
||||
$targets[$t], 'w'
|
||||
);
|
||||
// set the dynamic dash
|
||||
if (($target_ = $this->component->get($targets[$t])) !== null
|
||||
&& ArrayHelper::check($target_))
|
||||
{
|
||||
// search the target views
|
||||
$dashboard = (array) array_filter(
|
||||
$target_,
|
||||
function ($view) use ($id, $t, $types) {
|
||||
if (isset($view[$types[$t]])
|
||||
&& $id == $view[$types[$t]])
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
);
|
||||
|
||||
// set dashboard
|
||||
if (ArrayHelper::check($dashboard))
|
||||
{
|
||||
$dashboard = array_values($dashboard)[0];
|
||||
}
|
||||
|
||||
// check if view was found (this should be true)
|
||||
if (isset($dashboard['settings'])
|
||||
&& isset($dashboard['settings']->{$keys[$t]}))
|
||||
{
|
||||
$this->registry->set('build.dashboard',
|
||||
StringHelper::safe(
|
||||
$dashboard['settings']->{$keys[$t]}
|
||||
)
|
||||
);
|
||||
$this->registry->set('build.dashboard.type',
|
||||
$targets[$t]
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
// set massage that something is wrong
|
||||
$this->app->enqueueMessage(
|
||||
Text::_('COM_COMPONENTBUILDER_HR_HTHREEDASHBOARD_ERRORHTHREE'),
|
||||
'Error'
|
||||
);
|
||||
$this->app->enqueueMessage(
|
||||
Text::sprintf('COM_COMPONENTBUILDER_THE_BSB_BSB_IS_NOT_AVAILABLE_IN_YOUR_COMPONENT_PLEASE_INSURE_TO_ONLY_USED_S_FOR_A_DYNAMIC_DASHBOARD_THAT_ARE_STILL_LINKED_TO_YOUR_COMPONENT',
|
||||
$names[$t], $dashboard_,
|
||||
$type_names
|
||||
), 'Error'
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// set massage that something is wrong
|
||||
$this->app->enqueueMessage(
|
||||
Text::_('COM_COMPONENTBUILDER_HR_HTHREEDASHBOARD_ERRORHTHREE'), 'Error'
|
||||
);
|
||||
$this->app->enqueueMessage(
|
||||
Text::sprintf('COM_COMPONENTBUILDER_THE_BSB_BSB_IS_NOT_AVAILABLE_IN_YOUR_COMPONENT_PLEASE_INSURE_TO_ONLY_USED_S_FOR_A_DYNAMIC_DASHBOARD_THAT_ARE_STILL_LINKED_TO_YOUR_COMPONENT',
|
||||
$names[$t], $dashboard_,
|
||||
$type_names
|
||||
), 'Error'
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// the target value is wrong
|
||||
$this->app->enqueueMessage(
|
||||
Text::_('COM_COMPONENTBUILDER_HR_HTHREEDASHBOARD_ERRORHTHREE'), 'Error'
|
||||
);
|
||||
$this->app->enqueueMessage(
|
||||
Text::sprintf('COM_COMPONENTBUILDER_THE_BSB_VALUE_FOR_THE_DYNAMIC_DASHBOARD_IS_INVALID',
|
||||
$dashboard_
|
||||
), 'Error'
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// the target value is wrong
|
||||
$this->app->enqueueMessage(
|
||||
Text::_('COM_COMPONENTBUILDER_HR_HTHREEDASHBOARD_ERRORHTHREE'), 'Error'
|
||||
);
|
||||
$this->app->enqueueMessage(
|
||||
Text::sprintf('COM_COMPONENTBUILDER_THE_BSB_VALUE_FOR_THE_DYNAMIC_DASHBOARD_IS_INVALID',
|
||||
$dashboard_
|
||||
), 'Error'
|
||||
);
|
||||
}
|
||||
|
||||
// if default was changed to dynamic dashboard the remove default tab and methods
|
||||
if ($this->registry->get('build.dashboard'))
|
||||
{
|
||||
// dynamic dashboard is used
|
||||
$this->component->remove('dashboard_tab');
|
||||
$this->component->remove('php_dashboard_methods');
|
||||
return $view;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify that the dashboard configuration is invalid.
|
||||
*
|
||||
* @param string $dashboard The invalid dashboard value.
|
||||
*
|
||||
* @return void
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected function notifyInvalidDashboard(string $dashboard): void
|
||||
{
|
||||
$this->app->enqueueMessage(Text::_('COM_COMPONENTBUILDER_HR_HTHREEDASHBOARD_ERRORHTHREE'), 'Error');
|
||||
$this->app->enqueueMessage(
|
||||
Text::sprintf('COM_COMPONENTBUILDER_THE_BSB_VALUE_FOR_THE_DYNAMIC_DASHBOARD_IS_INVALID', $dashboard),
|
||||
'Error'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify that the configured dashboard target is not found.
|
||||
*
|
||||
* @param string $name Name of the dashboard type.
|
||||
* @param string $dashboard Dashboard value.
|
||||
* @param string $typeKey The internal registry key (e.g. admin_views).
|
||||
*
|
||||
* @return void
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected function notifyDashboardNotFound(string $name, string $dashboard, string $typeKey): void
|
||||
{
|
||||
$this->app->enqueueMessage(Text::_('COM_COMPONENTBUILDER_HR_HTHREEDASHBOARD_ERRORHTHREE'), 'Error');
|
||||
$this->app->enqueueMessage(
|
||||
Text::sprintf('COM_COMPONENTBUILDER_THE_BSB_BSB_IS_NOT_AVAILABLE_IN_YOUR_COMPONENT_PLEASE_INSURE_TO_ONLY_USED_S_FOR_A_DYNAMIC_DASHBOARD_THAT_ARE_STILL_LINKED_TO_YOUR_COMPONENT',
|
||||
$name,
|
||||
$dashboard,
|
||||
StringHelper::safe($typeKey, 'w')
|
||||
),
|
||||
'Error'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1021,7 +1021,7 @@ final class Data
|
||||
if (StringHelper::check($component->bom))
|
||||
{
|
||||
$this->config->set('bom_path',
|
||||
$this->config->get('compiler_path', JPATH_COMPONENT_ADMINISTRATOR . '/compiler') . '/' . $component->bom
|
||||
$this->config->get('compiler_path', JPATH_ADMINISTRATOR . '/components/com_componentbuilder/compiler') . '/' . $component->bom
|
||||
);
|
||||
}
|
||||
unset($component->bom);
|
||||
|
@@ -12,7 +12,6 @@
|
||||
namespace VDM\Joomla\Componentbuilder\Compiler\Component;
|
||||
|
||||
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Factory as Compiler;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Config;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Registry;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Interfaces\Component\SettingsInterface as Settings;
|
||||
@@ -88,30 +87,29 @@ final class Structuremultiple
|
||||
protected Structure $structure;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* Constructor.
|
||||
*
|
||||
* @param Config|null $config The compiler config object.
|
||||
* @param Registry|null $registry The compiler registry object.
|
||||
* @param Settings|null $settings The compiler component Joomla version settings object.
|
||||
* @param Component|null $component The component class.
|
||||
* @param Createdate|null $createdate The compiler model to get create date class.
|
||||
* @param Modifieddate|null $modifieddate The compiler model to get modified date class.
|
||||
* @param Structure|null $structure The compiler structure to build dynamic folder and files class.
|
||||
* @param Config $config The Config Class.
|
||||
* @param Registry $registry The Registry Class.
|
||||
* @param Settings $settings The SettingsInterface Class.
|
||||
* @param Component $component The Component Class.
|
||||
* @param Createdate $createdate The Createdate Class.
|
||||
* @param Modifieddate $modifieddate The Modifieddate Class.
|
||||
* @param Structure $structure The Structure Class.
|
||||
*
|
||||
* @since 3.2.0
|
||||
*/
|
||||
public function __construct(?Config $config = null, ?Registry $registry = null,
|
||||
?Settings $settings = null, ?Component $component = null,
|
||||
?Createdate $createdate = null, ?Modifieddate $modifieddate = null,
|
||||
?Structure $structure = null)
|
||||
public function __construct(Config $config, Registry $registry, Settings $settings,
|
||||
Component $component, Createdate $createdate,
|
||||
Modifieddate $modifieddate, Structure $structure)
|
||||
{
|
||||
$this->config = $config ?: Compiler::_('Config');
|
||||
$this->registry = $registry ?: Compiler::_('Registry');
|
||||
$this->settings = $settings ?: Compiler::_('Component.Settings');
|
||||
$this->component = $component ?: Compiler::_('Component');
|
||||
$this->createdate = $createdate ?: Compiler::_('Model.Createdate');
|
||||
$this->modifieddate = $modifieddate ?: Compiler::_('Model.Modifieddate');
|
||||
$this->structure = $structure ?: Compiler::_('Utilities.Structure');
|
||||
$this->config = $config;
|
||||
$this->registry = $registry;
|
||||
$this->settings = $settings;
|
||||
$this->component = $component;
|
||||
$this->createdate = $createdate;
|
||||
$this->modifieddate = $modifieddate;
|
||||
$this->structure = $structure;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -274,6 +272,25 @@ final class Structuremultiple
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the view has an Modal
|
||||
*
|
||||
* @param array $view
|
||||
*
|
||||
* @return int
|
||||
* @since 5.1.1
|
||||
*/
|
||||
private function hasModal(array $view): int
|
||||
{
|
||||
// only for Joomla 5 and above
|
||||
if ($this->config->get('joomla_version', 3) > 4)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the view has an API
|
||||
*
|
||||
@@ -336,12 +353,19 @@ final class Structuremultiple
|
||||
*/
|
||||
private function buildAdminView(array $view, array $config)
|
||||
{
|
||||
$addModal = $this->hasModal($view);
|
||||
|
||||
// build the admin edit view
|
||||
if ($view['settings']->name_single != 'null')
|
||||
{
|
||||
$target = ['admin' => $view['settings']->name_single];
|
||||
$this->structure->build($target, 'single', false, $config);
|
||||
|
||||
if ($addModal)
|
||||
{
|
||||
$this->structure->build($target, 'single_modal', false, $config);
|
||||
}
|
||||
|
||||
// build the site edit view (of this admin view)
|
||||
if (isset($view['edit_create_site_view'])
|
||||
&& is_numeric($view['edit_create_site_view'])
|
||||
@@ -350,6 +374,11 @@ final class Structuremultiple
|
||||
// setup the front site edit-view files
|
||||
$target = ['site' => $view['settings']->name_single];
|
||||
$this->structure->build($target, 'edit', false, $config);
|
||||
|
||||
if ($addModal)
|
||||
{
|
||||
$this->structure->build($target, 'edit_modal', false, $config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -358,6 +387,11 @@ final class Structuremultiple
|
||||
{
|
||||
$target = ['admin' => $view['settings']->name_list];
|
||||
$this->structure->build($target, 'list', false, $config);
|
||||
|
||||
if ($addModal)
|
||||
{
|
||||
$this->structure->build($target, 'list_modal', false, $config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -15,8 +15,8 @@ namespace VDM\Joomla\Componentbuilder\Compiler\Component;
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Application\CMSApplication;
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\CMS\Filesystem\Folder;
|
||||
use Joomla\CMS\Filesystem\File;
|
||||
use Joomla\Filesystem\Folder;
|
||||
use Joomla\Filesystem\File;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Config;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Registry;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Placeholder;
|
||||
@@ -397,7 +397,7 @@ final class Structuresingle
|
||||
return false;
|
||||
}
|
||||
// take action based on type
|
||||
elseif ($details->type === 'file' && !File::exists($this->currentFullPath))
|
||||
elseif ($details->type === 'file' && !is_file($this->currentFullPath))
|
||||
{
|
||||
$this->app->enqueueMessage(
|
||||
Text::_('COM_COMPONENTBUILDER_HR_HTHREEFILE_PATH_ERRORHTHREE'), 'Error'
|
||||
@@ -410,7 +410,7 @@ final class Structuresingle
|
||||
|
||||
return false;
|
||||
}
|
||||
elseif ($details->type === 'folder' && !Folder::exists($this->currentFullPath))
|
||||
elseif ($details->type === 'folder' && !is_dir($this->currentFullPath))
|
||||
{
|
||||
$this->app->enqueueMessage(
|
||||
Text::_('COM_COMPONENTBUILDER_HR_HTHREEFOLDER_PATH_ERRORHTHREE'),
|
||||
@@ -474,7 +474,7 @@ final class Structuresingle
|
||||
);
|
||||
|
||||
// check if path exist, if not creat it
|
||||
if (!Folder::exists($packageFullPath0nly))
|
||||
if (!is_dir($packageFullPath0nly))
|
||||
{
|
||||
Folder::create($packageFullPath0nly);
|
||||
}
|
||||
|
@@ -674,6 +674,7 @@ class Config extends ComponentConfig
|
||||
// these strings are used to search for language strings in all content
|
||||
return [
|
||||
'jjt' => 'Joomla' . '.JText._(',
|
||||
'jjtn' => 'Joomla' . '.Text._(',
|
||||
'js' => 'Text:' . ':script(',
|
||||
't' => 'Text:' . ':_(', // namespace and J version will be found
|
||||
'ts' => 'Text:' . ':sprintf(', // namespace and J version will be found
|
||||
@@ -734,7 +735,7 @@ class Config extends ComponentConfig
|
||||
// get the compiler path
|
||||
return $this->params->get(
|
||||
'compiler_folder_path',
|
||||
JPATH_COMPONENT_ADMINISTRATOR . '/compiler'
|
||||
JPATH_ADMINISTRATOR . '/components/com_componentbuilder/compiler'
|
||||
);
|
||||
}
|
||||
|
||||
@@ -946,7 +947,7 @@ class Config extends ComponentConfig
|
||||
// get the custom folder path
|
||||
return $this->params->get(
|
||||
'custom_folder_path',
|
||||
JPATH_COMPONENT_ADMINISTRATOR . '/custom'
|
||||
JPATH_ADMINISTRATOR . '/components/com_componentbuilder/custom'
|
||||
);
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -15,6 +15,7 @@ namespace VDM\Joomla\Componentbuilder\Compiler\Creator;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Field\Name;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Field\TypeName;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Field\Attributes;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Field\ModalSelect;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Field\Groups;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Builder\FieldNames;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Interfaces\Creator\Fieldtypeinterface as Field;
|
||||
@@ -56,6 +57,14 @@ final class FieldDynamic implements Fielddynamicinterface
|
||||
*/
|
||||
protected Attributes $attributes;
|
||||
|
||||
/**
|
||||
* The ModalSelect Class.
|
||||
*
|
||||
* @var ModalSelect
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected ModalSelect $modalselect;
|
||||
|
||||
/**
|
||||
* The Groups Class.
|
||||
*
|
||||
@@ -102,6 +111,7 @@ final class FieldDynamic implements Fielddynamicinterface
|
||||
* @param Name $name The Name Class.
|
||||
* @param TypeName $typename The TypeName Class.
|
||||
* @param Attributes $attributes The Attributes Class.
|
||||
* @param ModalSelect $modalselect The ModalSelect Class.
|
||||
* @param Groups $groups The Groups Class.
|
||||
* @param FieldNames $fieldnames The FieldNames Class.
|
||||
* @param Field $field The Fieldtypeinterface Class.
|
||||
@@ -111,12 +121,14 @@ final class FieldDynamic implements Fielddynamicinterface
|
||||
* @since 3.2.0
|
||||
*/
|
||||
public function __construct(Name $name, TypeName $typename,
|
||||
Attributes $attributes, Groups $groups, FieldNames $fieldnames,
|
||||
Attributes $attributes, ModalSelect $modalselect,
|
||||
Groups $groups, FieldNames $fieldnames,
|
||||
Field $field, Builders $builders, Layout $layout)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->typename = $typename;
|
||||
$this->attributes = $attributes;
|
||||
$this->modalselect = $modalselect;
|
||||
$this->groups = $groups;
|
||||
$this->fieldnames = $fieldnames;
|
||||
$this->field = $field;
|
||||
@@ -172,6 +184,12 @@ final class FieldDynamic implements Fielddynamicinterface
|
||||
// set options as null
|
||||
$optionArray = null;
|
||||
|
||||
// special treatment for Modal Select
|
||||
if ($typeName === 'ModalSelect')
|
||||
{
|
||||
$fieldAttributes['custom'] = $this->modalselect->extract($fieldAttributes);
|
||||
}
|
||||
|
||||
if ($this->groups->check($typeName, 'option'))
|
||||
{
|
||||
// set options array
|
||||
@@ -253,6 +271,7 @@ final class FieldDynamic implements Fielddynamicinterface
|
||||
// set the custom array
|
||||
$custom = $fieldAttributes['custom'];
|
||||
unset($fieldAttributes['custom']);
|
||||
|
||||
// set db key
|
||||
$custom['db'] = $dbkey;
|
||||
// increment the db key
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -14,7 +14,7 @@ namespace VDM\Joomla\Componentbuilder\Compiler\Customcode;
|
||||
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\User\User;
|
||||
use Joomla\CMS\Filesystem\Folder;
|
||||
use Joomla\Filesystem\Folder;
|
||||
use Joomla\CMS\Application\CMSApplication;
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\CMS\Version;
|
||||
|
@@ -13,7 +13,7 @@ namespace VDM\Joomla\Componentbuilder\Compiler\Customcode\Extractor;
|
||||
|
||||
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Filesystem\Folder;
|
||||
use Joomla\Filesystem\Folder;
|
||||
use VDM\Joomla\Utilities\ArrayHelper;
|
||||
use VDM\Joomla\Utilities\JsonHelper;
|
||||
use VDM\Joomla\Utilities\GetHelper;
|
||||
@@ -178,7 +178,7 @@ class Paths
|
||||
// check if the local install is found
|
||||
foreach ($local_paths as $key => $localPath)
|
||||
{
|
||||
if (!Folder::exists($localPath))
|
||||
if (!is_dir($localPath))
|
||||
{
|
||||
unset($local_paths[$key]);
|
||||
}
|
||||
|
@@ -231,7 +231,7 @@ final class InstallScript implements GetScriptInterface
|
||||
$script = PHP_EOL . 'use Joomla\CMS\Factory;';
|
||||
$script .= PHP_EOL . 'use Joomla\CMS\Language\Text;';
|
||||
$script .= PHP_EOL . 'use Joomla\CMS\Filesystem\File;';
|
||||
$script .= PHP_EOL . 'use Joomla\CMS\Filesystem\Folder;' . PHP_EOL;
|
||||
$script .= PHP_EOL . 'use Joomla\Filesystem\Folder;' . PHP_EOL;
|
||||
$script .= PHP_EOL . '/**';
|
||||
$script .= PHP_EOL . ' * ' . $extension->official_name
|
||||
. ' script file.';
|
||||
|
@@ -231,7 +231,7 @@ final class InstallScript implements GetScriptInterface
|
||||
$script = PHP_EOL . 'use Joomla\CMS\Factory;';
|
||||
$script .= PHP_EOL . 'use Joomla\CMS\Language\Text;';
|
||||
$script .= PHP_EOL . 'use Joomla\CMS\Filesystem\File;';
|
||||
$script .= PHP_EOL . 'use Joomla\CMS\Filesystem\Folder;' . PHP_EOL;
|
||||
$script .= PHP_EOL . 'use Joomla\Filesystem\Folder;' . PHP_EOL;
|
||||
$script .= PHP_EOL . '/**';
|
||||
$script .= PHP_EOL . ' * ' . $extension->official_name
|
||||
. ' script file.';
|
||||
|
@@ -48,6 +48,9 @@ use VDM\Joomla\Componentbuilder\Compiler\Service\ArchitectureComHelperClass;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Service\ArchitectureController;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Service\ArchitectureModel;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Service\ArchitecturePlugin;
|
||||
use VDM\Joomla\Componentbuilder\Power\Service\Git;
|
||||
use VDM\Joomla\Componentbuilder\Power\Service\Github;
|
||||
use VDM\Joomla\Github\Service\Utilities as GithubUtilities;
|
||||
use VDM\Joomla\Componentbuilder\Service\Gitea;
|
||||
use VDM\Joomla\Gitea\Service\Utilities as GiteaUtilities;
|
||||
use VDM\Joomla\Gitea\Service\Settings as GiteaSettings;
|
||||
@@ -159,6 +162,9 @@ abstract class Factory extends ExtendingFactory implements FactoryInterface
|
||||
->registerServiceProvider(new ArchitectureController())
|
||||
->registerServiceProvider(new ArchitectureModel())
|
||||
->registerServiceProvider(new ArchitecturePlugin())
|
||||
->registerServiceProvider(new Git())
|
||||
->registerServiceProvider(new Github())
|
||||
->registerServiceProvider(new GithubUtilities())
|
||||
->registerServiceProvider(new Gitea())
|
||||
->registerServiceProvider(new GiteaUtilities())
|
||||
->registerServiceProvider(new GiteaSettings())
|
||||
|
@@ -551,6 +551,11 @@ final class Attributes
|
||||
return $this->removeRequired();
|
||||
}
|
||||
|
||||
if ($name === 'sql_title_key' && 'ModalSelect' === $this->typeName)
|
||||
{
|
||||
return $this->getSqlTitleKey();
|
||||
}
|
||||
|
||||
if ($this->viewType == 2 && in_array($name, ['readonly', 'disabled']))
|
||||
{
|
||||
return $this->setReadonly($name);
|
||||
@@ -967,7 +972,7 @@ final class Attributes
|
||||
{
|
||||
// load the text key
|
||||
$this->attributes['custom']['text']
|
||||
= StringHelper::safe(
|
||||
= FieldHelper::safe(
|
||||
GetHelper::between(
|
||||
$this->settings->xml, 'value_field="', '"'
|
||||
)
|
||||
@@ -986,7 +991,7 @@ final class Attributes
|
||||
{
|
||||
// load the id key
|
||||
$this->attributes['custom']['id']
|
||||
= StringHelper::safe(
|
||||
= FieldHelper::safe(
|
||||
GetHelper::between(
|
||||
$this->settings->xml, 'key_field="', '"'
|
||||
)
|
||||
@@ -1045,6 +1050,30 @@ final class Attributes
|
||||
return 'false';
|
||||
}
|
||||
|
||||
/**
|
||||
* get sql title key value
|
||||
* and set the [data-key-name] if the key is not an [id]
|
||||
*
|
||||
* @return string|null
|
||||
* @since 5.1.1
|
||||
*/
|
||||
private function getSqlTitleKey(): ?string
|
||||
{
|
||||
// load the id key
|
||||
$key = FieldHelper::safe(
|
||||
GetHelper::between(
|
||||
$this->settings->xml, 'sql_title_key="', '"'
|
||||
)
|
||||
);
|
||||
|
||||
if ($key !== 'id')
|
||||
{
|
||||
$this->attributes['data-key-name'] = $key;
|
||||
}
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the readonly switch
|
||||
*
|
||||
|
@@ -151,6 +151,11 @@ class Data
|
||||
*/
|
||||
public function get($field, ?string $singleViewName = null, ?string $listViewName = null): ?object
|
||||
{
|
||||
if (empty($field))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isset($this->index[$field]))
|
||||
{
|
||||
$id = $this->index[$field];
|
||||
|
@@ -12,7 +12,7 @@
|
||||
namespace VDM\Joomla\Componentbuilder\Compiler\Field\JoomlaFive;
|
||||
|
||||
|
||||
use Joomla\CMS\Filesystem\Folder;
|
||||
use Joomla\Filesystem\Folder;
|
||||
use VDM\Joomla\Utilities\ArrayHelper;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Interfaces\Field\CoreFieldInterface;
|
||||
|
||||
@@ -99,7 +99,7 @@ final class CoreField implements CoreFieldInterface
|
||||
private function set(string $path): void
|
||||
{
|
||||
// Check if the path exists
|
||||
if (!Folder::exists($path))
|
||||
if (!is_dir($path))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@@ -12,7 +12,7 @@
|
||||
namespace VDM\Joomla\Componentbuilder\Compiler\Field\JoomlaFive;
|
||||
|
||||
|
||||
use Joomla\CMS\Filesystem\Folder;
|
||||
use Joomla\Filesystem\Folder;
|
||||
use VDM\Joomla\Utilities\ArrayHelper;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Interfaces\Field\CoreRuleInterface;
|
||||
|
||||
@@ -95,7 +95,7 @@ final class CoreRule implements CoreRuleInterface
|
||||
private function set(string $path): void
|
||||
{
|
||||
// Check if the path exists
|
||||
if (!Folder::exists($path))
|
||||
if (!is_dir($path))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@@ -12,7 +12,7 @@
|
||||
namespace VDM\Joomla\Componentbuilder\Compiler\Field\JoomlaFour;
|
||||
|
||||
|
||||
use Joomla\CMS\Filesystem\Folder;
|
||||
use Joomla\Filesystem\Folder;
|
||||
use VDM\Joomla\Utilities\ArrayHelper;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Interfaces\Field\CoreFieldInterface;
|
||||
|
||||
@@ -99,7 +99,7 @@ final class CoreField implements CoreFieldInterface
|
||||
private function set(string $path): void
|
||||
{
|
||||
// Check if the path exists
|
||||
if (!Folder::exists($path))
|
||||
if (!is_dir($path))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@@ -12,7 +12,7 @@
|
||||
namespace VDM\Joomla\Componentbuilder\Compiler\Field\JoomlaFour;
|
||||
|
||||
|
||||
use Joomla\CMS\Filesystem\Folder;
|
||||
use Joomla\Filesystem\Folder;
|
||||
use VDM\Joomla\Utilities\ArrayHelper;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Interfaces\Field\CoreRuleInterface;
|
||||
|
||||
@@ -95,7 +95,7 @@ final class CoreRule implements CoreRuleInterface
|
||||
private function set(string $path): void
|
||||
{
|
||||
// Check if the path exists
|
||||
if (!Folder::exists($path))
|
||||
if (!is_dir($path))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@@ -12,7 +12,7 @@
|
||||
namespace VDM\Joomla\Componentbuilder\Compiler\Field\JoomlaThree;
|
||||
|
||||
|
||||
use Joomla\CMS\Filesystem\Folder;
|
||||
use Joomla\Filesystem\Folder;
|
||||
use VDM\Joomla\Utilities\ArrayHelper;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Interfaces\Field\CoreFieldInterface;
|
||||
|
||||
@@ -100,7 +100,7 @@ final class CoreField implements CoreFieldInterface
|
||||
private function set(string $path): void
|
||||
{
|
||||
// Check if the path exists
|
||||
if (!Folder::exists($path))
|
||||
if (!is_dir($path))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@@ -12,7 +12,7 @@
|
||||
namespace VDM\Joomla\Componentbuilder\Compiler\Field\JoomlaThree;
|
||||
|
||||
|
||||
use Joomla\CMS\Filesystem\Folder;
|
||||
use Joomla\Filesystem\Folder;
|
||||
use VDM\Joomla\Utilities\ArrayHelper;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Interfaces\Field\CoreRuleInterface;
|
||||
|
||||
@@ -95,7 +95,7 @@ final class CoreRule implements CoreRuleInterface
|
||||
private function set(string $path): void
|
||||
{
|
||||
// Check if the path exists
|
||||
if (!Folder::exists($path))
|
||||
if (!is_dir($path))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@@ -0,0 +1,160 @@
|
||||
<?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\Compiler\Field;
|
||||
|
||||
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Utilities\Structure;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Builder\ContentMulti;
|
||||
|
||||
|
||||
/**
|
||||
* Compiler Field Modal Select
|
||||
*
|
||||
* @since 5.1.1
|
||||
*/
|
||||
final class ModalSelect
|
||||
{
|
||||
/**
|
||||
* The Structure Class.
|
||||
*
|
||||
* @var Structure
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected Structure $structure;
|
||||
|
||||
/**
|
||||
* The ContentMulti Class.
|
||||
*
|
||||
* @var ContentMulti
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected ContentMulti $contentmulti;
|
||||
|
||||
/**
|
||||
* The switch to ensure the fix is just added once
|
||||
*
|
||||
* @var bool
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected bool $addedFix = false;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Structure $structure The Structure Class.
|
||||
* @param ContentMulti $contentmulti The ContentMulti Class.
|
||||
*
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function __construct(Structure $structure, ContentMulti $contentmulti)
|
||||
{
|
||||
$this->structure = $structure;
|
||||
$this->contentmulti = $contentmulti;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts component and view details from field attributes for a Modal Select field.
|
||||
*
|
||||
* @param array $fieldAttributes The field attributes containing URLs and SQL table details.
|
||||
*
|
||||
* @return array An associative array with extracted component, view, views, table, id, and text.
|
||||
* @since 5.1.2
|
||||
*/
|
||||
public function extract(array $fieldAttributes): array
|
||||
{
|
||||
$component = null;
|
||||
$views = null;
|
||||
$view = null;
|
||||
|
||||
// Extract parameters from the given URL
|
||||
$extractParams = function ($url) {
|
||||
if (empty($url)) {
|
||||
return ['option' => null, 'view' => null];
|
||||
}
|
||||
$query = parse_url($url, PHP_URL_QUERY);
|
||||
parse_str($query, $params);
|
||||
return [
|
||||
'option' => $params['option'] ?? $params['amp;option'] ?? null,
|
||||
'view' => $params['view'] ?? $params['amp;view'] ?? null,
|
||||
];
|
||||
};
|
||||
|
||||
// Process URL attributes
|
||||
foreach (['urlSelect', 'urlEdit', 'urlNew'] as $urlKey)
|
||||
{
|
||||
$params = $extractParams($fieldAttributes[$urlKey] ?? '');
|
||||
$component ??= $params['option'];
|
||||
if ($urlKey === 'urlSelect')
|
||||
{
|
||||
$views ??= $params['view'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$view ??= $params['view'];
|
||||
}
|
||||
}
|
||||
|
||||
// Determine the target table and extract the view name
|
||||
$field_target_table = $fieldAttributes['sql_title_table'] ?? '';
|
||||
if (!$view && !empty($field_target_table))
|
||||
{
|
||||
$clean_table = str_replace('__', '', $field_target_table);
|
||||
$view = substr($clean_table, strpos($clean_table, '_') + 1);
|
||||
}
|
||||
|
||||
$sql_title_key = $fieldAttributes['sql_title_key'] ?? 'id';
|
||||
$sql_title_column = $fieldAttributes['sql_title_column'] ?? 'id';
|
||||
|
||||
// if one field is not id, we add an override to the ModalSelectField as a FIX
|
||||
if (!$this->addedFix && $sql_title_key !== 'id')
|
||||
{
|
||||
$this->structure->build(
|
||||
['admin' => 'fieldmodalselect_override'],
|
||||
'fieldmodalselect_override'
|
||||
);
|
||||
$this->structure->build(
|
||||
['site' => 'fieldmodalselect_override'],
|
||||
'fieldmodalselect_override'
|
||||
);
|
||||
// to make sure the file is updated TODO
|
||||
$this->contentmulti->set('fieldmodalselect_override|BLABLA', 'blabla');
|
||||
|
||||
$this->addedFix = true;
|
||||
}
|
||||
|
||||
// make sure we have the target view code name
|
||||
$view ??= 'error';
|
||||
|
||||
// add the Title Key for the Modal
|
||||
$this->contentmulti->set($view . '|SQL_TITLE_KEY', $sql_title_key);
|
||||
|
||||
// add the Title Column for the Modal
|
||||
$this->contentmulti->set($view . '|SQL_TITLE_COLUMN', $sql_title_column);
|
||||
|
||||
return [
|
||||
'modal_select' => true,
|
||||
'urlSelect' => $fieldAttributes['urlSelect'] ?? '',
|
||||
'hint' => $fieldAttributes['hint'] ?? '',
|
||||
'titleSelect' => $fieldAttributes['titleSelect'] ?? '',
|
||||
'iconSelect' => $fieldAttributes['iconSelect'] ?? '',
|
||||
'table' => $field_target_table,
|
||||
'id' => $sql_title_key,
|
||||
'text' => $sql_title_column,
|
||||
'component' => $component ?? 'error',
|
||||
'view' => $view,
|
||||
'views' => $views ?? 'error',
|
||||
'button' => false,
|
||||
'extends' => ''
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@@ -14,8 +14,8 @@ namespace VDM\Joomla\Componentbuilder\Compiler\Helper;
|
||||
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\CMS\Filesystem\File;
|
||||
use Joomla\CMS\Filesystem\Folder;
|
||||
use Joomla\Filesystem\File;
|
||||
use Joomla\Filesystem\Folder;
|
||||
use VDM\Component\Componentbuilder\Administrator\Helper\ComponentbuilderHelper;
|
||||
use VDM\Joomla\Utilities\StringHelper;
|
||||
use VDM\Joomla\Utilities\ArrayHelper;
|
||||
@@ -356,7 +356,7 @@ class Compiler extends Infusion
|
||||
. StringHelper::safe($string, 'U');
|
||||
$this->app->enqueueMessage(
|
||||
Text::sprintf(
|
||||
'The <b>Joomla.JText._('%s')</b> language constant for <b>%s</b> does not have a corresponding <code>Text::script('%s')</code> decalaration, please add it.',
|
||||
'The <b>Joomla.Text._('%s')</b> language constant for <b>%s</b> does not have a corresponding <code>Text::script('%s')</code> decalaration, please add it.',
|
||||
$constant, $string, $string
|
||||
), 'Warning'
|
||||
);
|
||||
@@ -418,7 +418,7 @@ class Compiler extends Infusion
|
||||
// first we do the static files
|
||||
foreach (CFactory::_('Utilities.Files')->get('static') as $static)
|
||||
{
|
||||
if (File::exists($static['path']))
|
||||
if (is_file($static['path']))
|
||||
{
|
||||
$this->setFileContent(
|
||||
$static['name'], $static['path'], $bom
|
||||
@@ -434,7 +434,7 @@ class Compiler extends Infusion
|
||||
{
|
||||
if ($file['view'] == $view)
|
||||
{
|
||||
if (File::exists($file['path']))
|
||||
if (is_file($file['path']))
|
||||
{
|
||||
$this->setFileContent(
|
||||
$file['name'], $file['path'], $bom,
|
||||
@@ -513,7 +513,7 @@ class Compiler extends Infusion
|
||||
// update the module files
|
||||
foreach (CFactory::_('Utilities.Files')->get($module->key) as $module_file)
|
||||
{
|
||||
if (File::exists($module_file['path']))
|
||||
if (is_file($module_file['path']))
|
||||
{
|
||||
$this->setFileContent(
|
||||
$module_file['name'], $module_file['path'],
|
||||
@@ -591,7 +591,7 @@ class Compiler extends Infusion
|
||||
// update the plugin files
|
||||
foreach (CFactory::_('Utilities.Files')->get($plugin->key) as $plugin_file)
|
||||
{
|
||||
if (File::exists($plugin_file['path']))
|
||||
if (is_file($plugin_file['path']))
|
||||
{
|
||||
$this->setFileContent(
|
||||
$plugin_file['name'], $plugin_file['path'],
|
||||
@@ -625,7 +625,7 @@ class Compiler extends Infusion
|
||||
// update the power files
|
||||
foreach (CFactory::_('Utilities.Files')->get($power->key) as $power_file)
|
||||
{
|
||||
if (File::exists($power_file['path']))
|
||||
if (is_file($power_file['path']))
|
||||
{
|
||||
$this->setFileContent(
|
||||
$power_file['name'], $power_file['path'],
|
||||
@@ -650,7 +650,7 @@ class Compiler extends Infusion
|
||||
// update the power files
|
||||
foreach (CFactory::_('Utilities.Files')->get($key) as $power_file)
|
||||
{
|
||||
if (File::exists($power_file['path']))
|
||||
if (is_file($power_file['path']))
|
||||
{
|
||||
$this->setFileContent(
|
||||
$power_file['name'], $power_file['path'],
|
||||
@@ -775,7 +775,7 @@ class Compiler extends Infusion
|
||||
$update_server_xml_path = CFactory::_('Utilities.Paths')->component_path . '/'
|
||||
. $this->updateServerFileName . '.xml';
|
||||
// make sure we have the correct file
|
||||
if (File::exists($update_server_xml_path)
|
||||
if (is_file($update_server_xml_path)
|
||||
&& ($update_server = CFactory::_('Component')->get('update_server')) !== null)
|
||||
{
|
||||
// move to server
|
||||
@@ -811,7 +811,7 @@ class Compiler extends Infusion
|
||||
&& is_numeric($module->update_server)
|
||||
&& $module->update_server > 0
|
||||
&& isset($module->update_server_xml_path)
|
||||
&& File::exists($module->update_server_xml_path)
|
||||
&& is_file($module->update_server_xml_path)
|
||||
&& isset($module->update_server_xml_file_name)
|
||||
&& StringHelper::check(
|
||||
$module->update_server_xml_file_name
|
||||
@@ -852,7 +852,7 @@ class Compiler extends Infusion
|
||||
&& is_numeric($plugin->update_server)
|
||||
&& $plugin->update_server > 0
|
||||
&& isset($plugin->update_server_xml_path)
|
||||
&& File::exists($plugin->update_server_xml_path)
|
||||
&& is_file($plugin->update_server_xml_path)
|
||||
&& isset($plugin->update_server_xml_file_name)
|
||||
&& StringHelper::check(
|
||||
$plugin->update_server_xml_file_name
|
||||
@@ -952,7 +952,7 @@ class Compiler extends Infusion
|
||||
if (('README.md' === $static['name']
|
||||
|| 'README.txt' === $static['name'])
|
||||
&& CFactory::_('Component')->get('addreadme')
|
||||
&& File::exists($static['path']))
|
||||
&& is_file($static['path']))
|
||||
{
|
||||
$this->setReadMe($static['path']);
|
||||
$two++;
|
||||
@@ -1467,7 +1467,7 @@ class Compiler extends Infusion
|
||||
}
|
||||
$counter = 0;
|
||||
// check if file exist
|
||||
if (File::exists($file))
|
||||
if (is_file($file))
|
||||
{
|
||||
foreach (
|
||||
new \SplFileObject($file) as $lineNumber => $lineContent
|
||||
|
@@ -1326,6 +1326,30 @@ class Fields extends Structure
|
||||
. $filter['type'] . '"';
|
||||
// set css classname of this field
|
||||
$filter_class = ucfirst((string) $filter['type']);
|
||||
|
||||
// if this is a modal_select field
|
||||
$modal_select = $filter['custom']['modal_select'] ?? null;
|
||||
if ($modal_select)
|
||||
{
|
||||
$field_filter_sets[] = Indent::_(3) . 'sql_title_table="'
|
||||
. $filter['custom']['table'] . '"';
|
||||
$field_filter_sets[] = Indent::_(3) . 'sql_title_column="'
|
||||
. $filter['custom']['text'] . '"';
|
||||
$field_filter_sets[] = Indent::_(3) . 'sql_title_key="'
|
||||
. $filter['custom']['id'] . '"';
|
||||
$field_filter_sets[] = Indent::_(3) . 'urlSelect="'
|
||||
. $filter['custom']['urlSelect'] . '"';
|
||||
$field_filter_sets[] = Indent::_(3) . 'hint="'
|
||||
. $filter['custom']['hint'] . '"';
|
||||
$field_filter_sets[] = Indent::_(3) . 'titleSelect="'
|
||||
. $filter['custom']['titleSelect'] . '"';
|
||||
$field_filter_sets[] = Indent::_(3) . 'iconSelect="'
|
||||
. $filter['custom']['iconSelect'] . '"';
|
||||
$field_filter_sets[] = Indent::_(3) . 'select="true"';
|
||||
$field_filter_sets[] = Indent::_(3) . 'edit="false"';
|
||||
$field_filter_sets[] = Indent::_(3) . 'clear="true"';
|
||||
$field_filter_sets[] = Indent::_(3) . 'onchange="form.submit()"';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@@ -454,6 +454,7 @@ class Get
|
||||
public $langStringTargets
|
||||
= array(
|
||||
'Joomla' . '.JText._(',
|
||||
'Joomla' . '.Text._(',
|
||||
'JText:' . ':script(',
|
||||
'Text:' . ':_(', // namespace and J version will be found
|
||||
'Text:' . ':sprintf(', // namespace and J version will be found
|
||||
@@ -1070,7 +1071,7 @@ class Get
|
||||
}
|
||||
CFactory::_('Config')->set('field_builder_type', $this->fieldBuilderType);
|
||||
// load the compiler path @deprecated
|
||||
$this->compilerPath = CFactory::_('Config')->get('compiler_path', JPATH_COMPONENT_ADMINISTRATOR . '/compiler');
|
||||
$this->compilerPath = CFactory::_('Config')->get('compiler_path', JPATH_ADMINISTRATOR . '/components/com_componentbuilder/compiler');
|
||||
// load the jcb powers path @deprecated
|
||||
$this->jcbPowersPath = CFactory::_('Config')->get('jcb_powers_path', 'libraries/jcb_powers');
|
||||
// set the component ID @deprecated
|
||||
|
@@ -13,8 +13,8 @@ namespace VDM\Joomla\Componentbuilder\Compiler\Helper;
|
||||
|
||||
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Filesystem\File;
|
||||
use Joomla\CMS\Filesystem\Folder;
|
||||
use Joomla\Filesystem\File;
|
||||
use Joomla\Filesystem\Folder;
|
||||
use Joomla\Filter\OutputFilter;
|
||||
use VDM\Component\Componentbuilder\Administrator\Helper\ComponentbuilderHelper;
|
||||
use VDM\Joomla\Utilities\StringHelper;
|
||||
@@ -55,6 +55,8 @@ class Infusion extends Interpretation
|
||||
*/
|
||||
public $removeSiteEditFolder = true;
|
||||
|
||||
public $secondRunAdmin;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
@@ -420,6 +422,15 @@ class Infusion extends Interpretation
|
||||
'jcb_ce_onBeforeBuildAdminEditViewContent', [&$view, &$nameSingleCode, &$nameListCode]
|
||||
);
|
||||
|
||||
// Here we set defaults
|
||||
// The real values are set in ModalSelect(4fc020dc-3137-478d-8d42-0571a75b77b5)
|
||||
|
||||
// add the Title Key for the Modal
|
||||
CFactory::_('Compiler.Builder.Content.Multi')->set($nameSingleCode . '|SQL_TITLE_KEY', 'id');
|
||||
|
||||
// add the Title Column for the Modal
|
||||
CFactory::_('Compiler.Builder.Content.Multi')->set($nameSingleCode . '|SQL_TITLE_COLUMN', 'name');
|
||||
|
||||
// FIELDSETS <<<DYNAMIC>>>
|
||||
CFactory::_('Compiler.Builder.Content.Multi')->set($nameSingleCode . '|FIELDSETS',
|
||||
CFactory::_('Compiler.Creator.Fieldset')->get(
|
||||
@@ -445,6 +456,11 @@ class Infusion extends Interpretation
|
||||
$this->setAddToolBar($view)
|
||||
);
|
||||
|
||||
// ADDMODALTOOLBAR <<<DYNAMIC>>>
|
||||
CFactory::_('Compiler.Builder.Content.Multi')->set($nameSingleCode . '|ADDMODALTOOLBAR',
|
||||
$this->setAddModalToolBar($view)
|
||||
);
|
||||
|
||||
// set the script for this view
|
||||
$this->buildTheViewScript($view);
|
||||
|
||||
@@ -945,6 +961,14 @@ class Infusion extends Interpretation
|
||||
)
|
||||
);
|
||||
|
||||
// VIEWS_MODAL_BODY <<<DYNAMIC>>>
|
||||
CFactory::_('Compiler.Builder.Content.Multi')->set($nameListCode . '|VIEWS_MODAL_BODY',
|
||||
$this->setModalViewsBody(
|
||||
$nameSingleCode,
|
||||
$nameListCode
|
||||
)
|
||||
);
|
||||
|
||||
// LISTHEAD <<<DYNAMIC>>>
|
||||
CFactory::_('Compiler.Builder.Content.Multi')->set($nameListCode . '|LISTHEAD',
|
||||
$this->setListHead(
|
||||
@@ -1046,6 +1070,13 @@ class Infusion extends Interpretation
|
||||
)
|
||||
);
|
||||
|
||||
// ADMIN_VIEWS_MODAL_HEADER <<<DYNAMIC>>> add the header details for the views
|
||||
CFactory::_('Compiler.Builder.Content.Multi')->set($nameListCode . '|ADMIN_VIEWS_MODAL_HEADER',
|
||||
CFactory::_('Header')->get(
|
||||
'admin.views.modal', $nameListCode
|
||||
)
|
||||
);
|
||||
|
||||
// API_VIEWS_CONTROLLER_HEADER <<<DYNAMIC>>> add the header details for the controller
|
||||
CFactory::_('Compiler.Builder.Content.Multi')->set($nameListCode . '|API_VIEWS_CONTROLLER_HEADER',
|
||||
CFactory::_('Header')->get(
|
||||
@@ -2495,7 +2526,7 @@ class Infusion extends Interpretation
|
||||
// build the path to place the lang file
|
||||
$path = CFactory::_('Utilities.Paths')->component_path . '/' . $p . '/language/'
|
||||
. $tag . '/';
|
||||
if (!Folder::exists($path))
|
||||
if (!is_dir($path))
|
||||
{
|
||||
Folder::create($path);
|
||||
// count the folder created
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -12,7 +12,7 @@
|
||||
namespace VDM\Joomla\Componentbuilder\Compiler\Helper;
|
||||
|
||||
|
||||
use Joomla\CMS\Filesystem\File;
|
||||
use Joomla\Filesystem\File;
|
||||
use Joomla\CMS\Language\Text;
|
||||
use VDM\Joomla\Utilities\ArrayHelper;
|
||||
use VDM\Joomla\Utilities\GetHelper;
|
||||
@@ -679,7 +679,7 @@ class Structure extends Get
|
||||
!isset($this->extentionTrackingFilesMoved[$check]))
|
||||
{
|
||||
// check files exist
|
||||
if (File::exists(
|
||||
if (is_file(
|
||||
CFactory::_('Utilities.Paths')->component_path . '/admin/models/fields/'
|
||||
. $field['type_name'] . '.php'
|
||||
))
|
||||
@@ -707,7 +707,7 @@ class Structure extends Get
|
||||
!isset($this->extentionTrackingFilesMoved[$check]))
|
||||
{
|
||||
// check files exist
|
||||
if (File::exists(
|
||||
if (is_file(
|
||||
CFactory::_('Utilities.Paths')->component_path . '/admin/models/rules/'
|
||||
. CFactory::_('Registry')->get('validation.linked.' . $field['field'])
|
||||
. '.php'
|
||||
|
@@ -36,10 +36,10 @@ interface Fieldtypeinterface
|
||||
* @param string $taber The tabs to add in layout
|
||||
*
|
||||
* @return mixed The field (two return types based of field_builder_type selected Object->xml or String)
|
||||
* @since 3.2.0
|
||||
* @since 3.2.0
|
||||
*/
|
||||
public function get(string $setType, array &$fieldAttributes, string &$name,
|
||||
string &$typeName, string &$langView, string &$nameSingleCode, string &$nameListCode,
|
||||
public function get(string $setType, array &$fieldAttributes, string $name,
|
||||
string $typeName, string $langView, string $nameSingleCode, string $nameListCode,
|
||||
array $placeholders, ?array &$optionArray, ?array $custom = null, string $taber = '');
|
||||
}
|
||||
|
||||
|
@@ -426,6 +426,8 @@ final class Header implements HeaderInterface
|
||||
$headers[] = 'use Joomla\Input\Input;';
|
||||
break;
|
||||
|
||||
case 'admin.views.modal':
|
||||
$headers[] = 'use Joomla\CMS\Session\Session;';
|
||||
case 'admin.views':
|
||||
$headers[] = 'use Joomla\CMS\HTML\HTMLHelper as Html;';
|
||||
$headers[] = 'use Joomla\CMS\Layout\LayoutHelper;';
|
||||
@@ -504,7 +506,7 @@ final class Header implements HeaderInterface
|
||||
case 'import.model':
|
||||
$headers[] = 'use Joomla\Filesystem\File;';
|
||||
$headers[] = 'use Joomla\Filesystem\Folder;';
|
||||
$headers[] = 'use Joomla\CMS\Filesystem\Path;';
|
||||
$headers[] = 'use Joomla\Filesystem\Path;';
|
||||
$headers[] = 'use Joomla\CMS\Filter\OutputFilter;';
|
||||
$headers[] = 'use Joomla\CMS\Installer\InstallerHelper;';
|
||||
$headers[] = 'use Joomla\CMS\MVC\Model\BaseDatabaseModel;';
|
||||
|
@@ -200,7 +200,7 @@ final class Header implements HeaderInterface
|
||||
$headers = $this->getHeaders($context);
|
||||
|
||||
// add to all except the helper classes
|
||||
if ('admin.helper' !== $context && 'site.helper' !== $context)
|
||||
if ('admin.helper' !== $context && 'site.helper' !== $context && 'plugin.extension.header' !== $context && 'plugin.provider.header' !== $context)
|
||||
{
|
||||
$target = 'Administrator';
|
||||
if ($this->config->get('build_target', 'admin') === 'site')
|
||||
@@ -426,6 +426,8 @@ final class Header implements HeaderInterface
|
||||
$headers[] = 'use Joomla\Input\Input;';
|
||||
break;
|
||||
|
||||
case 'admin.views.modal':
|
||||
$headers[] = 'use Joomla\CMS\Session\Session;';
|
||||
case 'admin.views':
|
||||
$headers[] = 'use Joomla\CMS\HTML\HTMLHelper as Html;';
|
||||
$headers[] = 'use Joomla\CMS\Layout\LayoutHelper;';
|
||||
@@ -503,7 +505,7 @@ final class Header implements HeaderInterface
|
||||
case 'import.custom.model':
|
||||
case 'import.model':
|
||||
$headers[] = 'use Joomla\CMS\Filesystem\File;';
|
||||
$headers[] = 'use Joomla\CMS\Filesystem\Folder;';
|
||||
$headers[] = 'use Joomla\Filesystem\Folder;';
|
||||
$headers[] = 'use Joomla\CMS\Filesystem\Path;';
|
||||
$headers[] = 'use Joomla\CMS\Filter\OutputFilter;';
|
||||
$headers[] = 'use Joomla\CMS\Installer\InstallerHelper;';
|
||||
|
@@ -19,7 +19,7 @@ use VDM\Joomla\Componentbuilder\Compiler\Config;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Placeholder;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Customcode;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Customcode\Gui;
|
||||
use VDM\Joomla\Componentbuilder\JoomlaPower\Remote\Get as SuperPower;
|
||||
use VDM\Joomla\Interfaces\Remote\GetInterface as SuperPower;
|
||||
use VDM\Joomla\Utilities\ArrayHelper;
|
||||
use VDM\Joomla\Utilities\JsonHelper;
|
||||
use VDM\Joomla\Utilities\GuidHelper;
|
||||
@@ -379,7 +379,7 @@ final class JoomlaPower implements PowerInterface
|
||||
*/
|
||||
private function handlePowerNotFound(string $guid): bool
|
||||
{
|
||||
if (empty($this->retry[$guid]) && $this->superpower->item($guid, ['remote', 'local']))
|
||||
if (empty($this->retry[$guid]) && $this->setSuperPowers($guid))
|
||||
{
|
||||
// Retry loading the power
|
||||
unset($this->state[$guid]);
|
||||
@@ -529,16 +529,16 @@ final class JoomlaPower implements PowerInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the super powers of this power
|
||||
* Set the super power of this power
|
||||
*
|
||||
* @param string $guid The global unique id of the power
|
||||
*
|
||||
* @return void
|
||||
* @since 3.2.1
|
||||
* @return bool
|
||||
* @since 5.1.1
|
||||
*/
|
||||
private function setSuperPowers(string $guid): void
|
||||
private function setSuperPowers(string $guid): bool
|
||||
{
|
||||
// soon
|
||||
return $this->superpower->item($guid, ['remote', 'local']);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -388,7 +388,7 @@ final class Header implements HeaderInterface
|
||||
case 'import.custom.model':
|
||||
case 'import.model':
|
||||
$headers[] = 'use Joomla\CMS\Filesystem\File;';
|
||||
$headers[] = 'use Joomla\CMS\Filesystem\Folder;';
|
||||
$headers[] = 'use Joomla\Filesystem\Folder;';
|
||||
$headers[] = 'use Joomla\CMS\Filesystem\Path;';
|
||||
$headers[] = 'use Joomla\CMS\Filter\OutputFilter;';
|
||||
$headers[] = 'use Joomla\CMS\Installer\InstallerHelper;';
|
||||
|
@@ -350,7 +350,7 @@ class Structure
|
||||
*/
|
||||
protected function modulePath(object &$module): void
|
||||
{
|
||||
$module->folder_path = $this->config->get('compiler_path', JPATH_COMPONENT_ADMINISTRATOR . '/compiler') . '/'
|
||||
$module->folder_path = $this->config->get('compiler_path', JPATH_ADMINISTRATOR . '/components/com_componentbuilder/compiler') . '/'
|
||||
. $module->folder_name;
|
||||
}
|
||||
|
||||
|
@@ -321,7 +321,7 @@ class Structure implements StructureInterface
|
||||
*/
|
||||
protected function pluginPath(object &$plugin): void
|
||||
{
|
||||
$plugin->folder_path = $this->config->get('compiler_path', JPATH_COMPONENT_ADMINISTRATOR . '/compiler') . '/'
|
||||
$plugin->folder_path = $this->config->get('compiler_path', JPATH_ADMINISTRATOR . '/components/com_componentbuilder/compiler') . '/'
|
||||
. $plugin->folder_name;
|
||||
}
|
||||
|
||||
|
@@ -321,7 +321,7 @@ class Structure implements StructureInterface
|
||||
*/
|
||||
protected function pluginPath(object &$plugin): void
|
||||
{
|
||||
$plugin->folder_path = $this->config->get('compiler_path', JPATH_COMPONENT_ADMINISTRATOR . '/compiler') . '/'
|
||||
$plugin->folder_path = $this->config->get('compiler_path', JPATH_ADMINISTRATOR . '/components/com_componentbuilder/compiler') . '/'
|
||||
. $plugin->folder_name;
|
||||
}
|
||||
|
||||
|
@@ -282,7 +282,7 @@ class Structure implements StructureInterface
|
||||
*/
|
||||
protected function pluginPath(object &$plugin): void
|
||||
{
|
||||
$plugin->folder_path = $this->config->get('compiler_path', JPATH_COMPONENT_ADMINISTRATOR . '/compiler') . '/'
|
||||
$plugin->folder_path = $this->config->get('compiler_path', JPATH_ADMINISTRATOR . '/components/com_componentbuilder/compiler') . '/'
|
||||
. $plugin->folder_name;
|
||||
}
|
||||
|
||||
|
@@ -198,6 +198,7 @@ final class Extractor
|
||||
{
|
||||
// need some special treatment here
|
||||
if ($lang_string_target === 'Joomla' . '.JText._('
|
||||
|| $lang_string_target === 'Joomla' . '.Text._('
|
||||
|| $lang_string_target === 'JText:' . ':script('
|
||||
|| $lang_string_target === 'Text:' . ':script('
|
||||
|| $lang_string_target === 'Joomla__' . '_ba6326ef_cb79_4348_80f4_ab086082e3c5___Power:' . ':script('
|
||||
|
@@ -54,141 +54,143 @@ final class Purge
|
||||
}
|
||||
|
||||
/**
|
||||
* Purge the unused language strings.
|
||||
* Purge unused language strings linked to a component.
|
||||
*
|
||||
* This method removes or updates language strings that are no longer linked
|
||||
* to the specified component. It checks if the strings are linked to other
|
||||
* extensions and either updates, archives, or deletes them based on the
|
||||
* conditions.
|
||||
*
|
||||
* @param array $values The active strings.
|
||||
* @param int $targetGuid The target entity GUID.
|
||||
* @param string $target The target extension type (default is 'components').
|
||||
* @param array $values Active string sources.
|
||||
* @param string $targetGuid The GUID of the target entity.
|
||||
* @param string $target Target extension type. Default: 'components'.
|
||||
*
|
||||
* @return void
|
||||
* @since 5.0.2
|
||||
*/
|
||||
public function execute(array $values, string $targetGuid, string $target = 'components'): void
|
||||
{
|
||||
$target_types = ['components' => 'components', 'modules' => 'modules', 'plugins' => 'plugins'];
|
||||
$validTargets = ['components' => 'components', 'modules' => 'modules', 'plugins' => 'plugins'];
|
||||
|
||||
if (isset($target_types[$target]))
|
||||
if (!isset($validTargets[$target]))
|
||||
{
|
||||
unset($target_types[$target]);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a new query object.
|
||||
$query = $this->db->getQuery(true);
|
||||
$query->from($this->db->quoteName('#__componentbuilder_language_translation', 'a'))
|
||||
->select($this->db->quoteName(['a.id', 'a.translation', 'a.components', 'a.modules', 'a.plugins']))
|
||||
->where($this->db->quoteName('a.source') . ' NOT IN (' . implode(',', array_map(fn($a) => $this->db->quote($a), $values)) . ')')
|
||||
->where($this->db->quoteName('a.published') . ' = 1');
|
||||
$otherTargets = array_diff_key($validTargets, [$target => $target]);
|
||||
|
||||
$this->db->setQuery($query);
|
||||
$this->db->execute();
|
||||
$query = $this->db->getQuery(true)
|
||||
->select($this->db->quoteName(['id', 'translation', 'components', 'modules', 'plugins']))
|
||||
->from($this->db->quoteName('#__componentbuilder_language_translation', 'a'))
|
||||
->where($this->db->quoteName('a.source') . ' NOT IN (' . implode(',', array_map([$this->db, 'quote'], $values)) . ')')
|
||||
->where($this->db->quoteName('a.published') . ' = 1');
|
||||
|
||||
if ($this->db->getNumRows())
|
||||
$this->db->setQuery($query);
|
||||
$this->db->execute();
|
||||
|
||||
if (!$this->db->getNumRows())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$today = Factory::getDate()->toSql();
|
||||
$items = $this->db->loadAssocList();
|
||||
$counterUpdate = 0;
|
||||
|
||||
foreach ($items as $item)
|
||||
{
|
||||
if (!JsonHelper::check($item[$target]))
|
||||
{
|
||||
$counterUpdate = 0;
|
||||
$otherStrings = $this->db->loadAssocList();
|
||||
$today = Factory::getDate()->toSql();
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($otherStrings as $item)
|
||||
{
|
||||
if (JsonHelper::check($item[$target]))
|
||||
{
|
||||
$targets = (array) json_decode((string) $item[$target], true);
|
||||
$targets = (array) json_decode((string) $item[$target], true);
|
||||
|
||||
if (($key = array_search($targetGuid, $targets)) !== false)
|
||||
{
|
||||
unset($targets[$key]);
|
||||
if (($key = array_search($targetGuid, $targets, true)) === false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ArrayHelper::check($targets))
|
||||
{
|
||||
$this->update->set($item['id'], $target, $targets, 1, $today, $counterUpdate);
|
||||
unset($targets[$key]);
|
||||
|
||||
$counterUpdate++;
|
||||
|
||||
$this->update->execute(50);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->handleUnlinkedString($item, $target_types, $targets, $today, $counterUpdate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->update->execute();
|
||||
if (ArrayHelper::check($targets))
|
||||
{
|
||||
$this->update->set($item['id'], $target, $targets, 1, $today, $counterUpdate);
|
||||
$counterUpdate++;
|
||||
$this->update->execute(50);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->handleUnlinkedString($item, $otherTargets, $target, $targets, $today, $counterUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
$this->update->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle strings that are unlinked from the current component.
|
||||
* Handle strings no longer linked to the current component.
|
||||
*
|
||||
* This method checks if a string is linked to other extensions and either updates,
|
||||
* archives, or deletes it based on the conditions.
|
||||
*
|
||||
* @param array $item The language string item.
|
||||
* @param array $targetTypes The target extension types.
|
||||
* @param array $targets The targets to update.
|
||||
* @param string $today The current date.
|
||||
* @param int $counter The update counter.
|
||||
* @param array $item The language string item.
|
||||
* @param array $otherTypes Other extension types.
|
||||
* @param string $target The current target extension type.
|
||||
* @param array $targets Remaining targets to update.
|
||||
* @param string $today Current date in SQL format.
|
||||
* @param int $counter Counter for updates.
|
||||
*
|
||||
* @return void
|
||||
* @since 5.0.2
|
||||
*/
|
||||
protected function handleUnlinkedString(array $item, array $targetTypes, array $targets, string $today, int &$counter): void
|
||||
protected function handleUnlinkedString(array $item, array $otherTypes, string $target,
|
||||
array $targets, string $today, int &$counter): void
|
||||
{
|
||||
// the action (1 = remove, 2 = archive, 0 = do nothing)
|
||||
$action_with_string = 1;
|
||||
$action = 1; // 1 = remove, 2 = archive, 0 = keep
|
||||
|
||||
foreach ($targetTypes as $other_target)
|
||||
foreach ($otherTypes as $type)
|
||||
{
|
||||
if ($action_with_string && JsonHelper::check($item[$other_target]))
|
||||
if (JsonHelper::check($item[$type]))
|
||||
{
|
||||
$other_targets = (array) json_decode((string) $item[$other_target], true);
|
||||
|
||||
if (ArrayHelper::check($other_targets))
|
||||
$linked = json_decode((string) $item[$type], true);
|
||||
if (ArrayHelper::check($linked))
|
||||
{
|
||||
$action_with_string = 0;
|
||||
$action = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($action_with_string && JsonHelper::check($item['translation']))
|
||||
if ($action && JsonHelper::check($item['translation']))
|
||||
{
|
||||
$translation = json_decode((string) $item['translation'], true);
|
||||
|
||||
if (ArrayHelper::check($translation))
|
||||
{
|
||||
$this->update->set($item['id'], $targets, $targets, 2, $today, $counter);
|
||||
$this->update->set($item['id'], $target, $targets, 2, $today, $counter);
|
||||
$counter++;
|
||||
$this->update->execute(50);
|
||||
$action_with_string = 2;
|
||||
$action = 2; // just to show intent :)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ($action_with_string == 1)
|
||||
if ($action === 1)
|
||||
{
|
||||
$this->removeExitingLangString($item['id']);
|
||||
$this->removeLanguageString($item['id']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove existing language translation strings.
|
||||
* Delete a language string by ID.
|
||||
*
|
||||
* This method deletes a language string from the database based on its ID.
|
||||
*
|
||||
* @param int $id The string ID to remove.
|
||||
* @param int $id The ID of the string.
|
||||
*
|
||||
* @return void
|
||||
* @since 5.0.2
|
||||
*/
|
||||
protected function removeExitingLangString(int $id): void
|
||||
protected function removeLanguageString(int $id): void
|
||||
{
|
||||
$query = $this->db->getQuery(true);
|
||||
$query->delete($this->db->quoteName('#__componentbuilder_language_translation'))
|
||||
$query = $this->db->getQuery(true)
|
||||
->delete($this->db->quoteName('#__componentbuilder_language_translation'))
|
||||
->where($this->db->quoteName('id') . ' = ' . (int) $id);
|
||||
|
||||
$this->db->setQuery($query);
|
||||
|
@@ -12,7 +12,7 @@
|
||||
namespace VDM\Joomla\Componentbuilder\Compiler\Library;
|
||||
|
||||
|
||||
use Joomla\CMS\Filesystem\Folder as JoomlaFolder;
|
||||
use Joomla\Filesystem\Folder as JoomlaFolder;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Config;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Registry;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Interfaces\EventInterface as Event;
|
||||
@@ -158,7 +158,7 @@ class Structure
|
||||
);
|
||||
|
||||
// creat the main component folder
|
||||
if (!JoomlaFolder::exists($this->paths->component_path))
|
||||
if (!is_dir($this->paths->component_path))
|
||||
{
|
||||
JoomlaFolder::create($this->paths->component_path);
|
||||
|
||||
|
@@ -35,7 +35,7 @@ class Dynamicget
|
||||
* The joint types
|
||||
*
|
||||
* @var array
|
||||
* @since 3.2.0
|
||||
* @since 3.2.0
|
||||
*/
|
||||
protected array $jointer = [
|
||||
1 => 'LEFT',
|
||||
@@ -49,20 +49,31 @@ class Dynamicget
|
||||
* The operator types
|
||||
*
|
||||
* @var array
|
||||
* @since 3.2.0
|
||||
* @since 3.2.0
|
||||
*/
|
||||
protected array $operator = [
|
||||
1 => '=',
|
||||
2 => '!=',
|
||||
3 => '<>',
|
||||
2 => '!=',
|
||||
3 => '<>',
|
||||
4 => '>',
|
||||
5 => '<',
|
||||
6 => '>=',
|
||||
5 => '<',
|
||||
6 => '>=',
|
||||
7 => '<=',
|
||||
8 => '!<',
|
||||
9 => '!>',
|
||||
8 => '!<',
|
||||
9 => '!>',
|
||||
10 => 'IN',
|
||||
11 => 'NOT IN'
|
||||
11 => 'NOT IN',
|
||||
12 => 'LIKE',
|
||||
13 => 'NOT LIKE',
|
||||
14 => 'IS NULL',
|
||||
15 => 'IS NOT NULL',
|
||||
16 => 'BETWEEN',
|
||||
17 => 'NOT BETWEEN',
|
||||
18 => 'EXISTS',
|
||||
19 => 'NOT EXISTS',
|
||||
20 => 'REGEXP',
|
||||
21 => 'NOT REGEXP',
|
||||
22 => 'SOUNDS LIKE'
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -163,416 +174,373 @@ class Dynamicget
|
||||
/**
|
||||
* Set Dynamic Get
|
||||
*
|
||||
* @param object $item The item data
|
||||
* @param string $view_code The view code name
|
||||
* @param string $context The context for events
|
||||
* @param object $item The item data
|
||||
* @param string $viewCode The view code name
|
||||
* @param string $context The context for events
|
||||
*
|
||||
* @return void
|
||||
* @since 3.2.0
|
||||
* @since 3.2.0
|
||||
*/
|
||||
public function set(object &$item, string $view_code, string $context)
|
||||
public function set(object &$item, string $viewCode, string $context): void
|
||||
{
|
||||
// reset buckets
|
||||
$item->main_get = [];
|
||||
$item->main_get = [];
|
||||
$item->custom_get = [];
|
||||
$addJoins = true;
|
||||
|
||||
// should joined and other tweaks be added
|
||||
$add_tweaks_joints = true;
|
||||
|
||||
// set source data
|
||||
switch ($item->main_source)
|
||||
{
|
||||
case 1:
|
||||
// check if auto sync is set
|
||||
if ($item->select_all == 1)
|
||||
{
|
||||
$item->view_selection = '*';
|
||||
}
|
||||
// set the view data
|
||||
$item->main_get[0]['selection'] = $this->selection->get(
|
||||
$item->key, $view_code,
|
||||
$item->view_selection,
|
||||
$item->view_table_main, 'a', 'view'
|
||||
);
|
||||
$item->main_get[0]['as'] = 'a';
|
||||
$item->main_get[0]['key'] = $item->key;
|
||||
$item->main_get[0]['context'] = $context;
|
||||
unset($item->view_selection);
|
||||
$this->configureViewSource($item, $viewCode, $context);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
// check if auto sync is set
|
||||
if ($item->select_all == 1)
|
||||
{
|
||||
$item->db_selection = '*';
|
||||
}
|
||||
// set the database data
|
||||
$item->main_get[0]['selection'] = $this->selection->get(
|
||||
$item->key, $view_code,
|
||||
$item->db_selection,
|
||||
$item->db_table_main, 'a', 'db'
|
||||
);
|
||||
$item->main_get[0]['as'] = 'a';
|
||||
$item->main_get[0]['key'] = $item->key;
|
||||
$item->main_get[0]['context'] = $context;
|
||||
unset($item->db_selection);
|
||||
$this->configureDbSource($item, $viewCode, $context);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
// set GUI mapper field
|
||||
$this->guiMapper['field'] = 'php_custom_get';
|
||||
// get the custom query
|
||||
$customQueryString
|
||||
= $this->gui->set(
|
||||
$this->customcode->update(
|
||||
base64_decode((string) $item->php_custom_get)
|
||||
),
|
||||
$this->guiMapper
|
||||
);
|
||||
|
||||
// get the table name
|
||||
$_searchQuery
|
||||
= GetHelper::between(
|
||||
$customQueryString, '$query->from(', ')'
|
||||
);
|
||||
|
||||
if (StringHelper::check(
|
||||
$_searchQuery
|
||||
)
|
||||
&& strpos((string) $_searchQuery, '#__') !== false)
|
||||
{
|
||||
$_queryName = GetHelper::between(
|
||||
$_searchQuery, '#__', "'"
|
||||
);
|
||||
|
||||
if (!StringHelper::check(
|
||||
$_queryName
|
||||
))
|
||||
{
|
||||
$_queryName = GetHelper::between(
|
||||
$_searchQuery, '#__', '"'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// set to blank if not found
|
||||
if (!isset($_queryName)
|
||||
|| !StringHelper::check(
|
||||
$_queryName
|
||||
))
|
||||
{
|
||||
$_queryName = '';
|
||||
}
|
||||
|
||||
// set custom script
|
||||
$item->main_get[0]['selection'] = [
|
||||
'select' => $customQueryString,
|
||||
'from' => '', 'table' => '', 'type' => '',
|
||||
'name' => $_queryName];
|
||||
$item->main_get[0]['as'] = 'a';
|
||||
$item->main_get[0]['key'] = $item->key;
|
||||
$item->main_get[0]['context'] = $context;
|
||||
|
||||
// do not add
|
||||
$add_tweaks_joints = false;
|
||||
|
||||
$this->configureCustomSource($item, $viewCode, $context);
|
||||
$addJoins = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// only add if main source is not custom
|
||||
if ($add_tweaks_joints)
|
||||
if ($addJoins)
|
||||
{
|
||||
// set join_view_table details
|
||||
$item->join_view_table = json_decode(
|
||||
(string) $item->join_view_table, true
|
||||
);
|
||||
|
||||
if (ArrayHelper::check(
|
||||
$item->join_view_table
|
||||
))
|
||||
{
|
||||
// start the part of a table bucket
|
||||
$_part_of_a = [];
|
||||
// build relationship
|
||||
$_relationship = array_map(
|
||||
function ($op) use (&$_part_of_a) {
|
||||
$bucket = [];
|
||||
// array(on_field_as, on_field)
|
||||
$bucket['on_field'] = array_map(
|
||||
'trim',
|
||||
explode('.', (string) $op['on_field'])
|
||||
);
|
||||
// array(join_field_as, join_field)
|
||||
$bucket['join_field'] = array_map(
|
||||
'trim',
|
||||
explode('.', (string) $op['join_field'])
|
||||
);
|
||||
// triget filed that has table a relationship
|
||||
if ($op['row_type'] == 1
|
||||
&& ($bucket['on_field'][0] === 'a'
|
||||
|| isset($_part_of_a[$bucket['on_field'][0]])
|
||||
|| isset($_part_of_a[$bucket['join_field'][0]])))
|
||||
{
|
||||
$_part_of_a[$op['as']] = $op['as'];
|
||||
}
|
||||
|
||||
return $bucket;
|
||||
}, $item->join_view_table
|
||||
);
|
||||
|
||||
// loop joints
|
||||
foreach ($item->join_view_table as $nr => &$option)
|
||||
{
|
||||
if (StringHelper::check(
|
||||
$option['selection']
|
||||
))
|
||||
{
|
||||
// convert the type
|
||||
$option['type']
|
||||
= $this->jointer[$option['type']];
|
||||
// convert the operator
|
||||
$option['operator']
|
||||
= $this->operator[$option['operator']];
|
||||
// get the on field values
|
||||
$on_field
|
||||
= $_relationship[$nr]['on_field'];
|
||||
// get the join field values
|
||||
$join_field
|
||||
= $_relationship[$nr]['join_field'];
|
||||
// set selection
|
||||
$option['selection']
|
||||
= $this->selection->get(
|
||||
$item->key,
|
||||
$view_code,
|
||||
$option['selection'],
|
||||
$option['view_table'],
|
||||
$option['as'],
|
||||
'view',
|
||||
$option['row_type']
|
||||
);
|
||||
$option['key'] = $item->key;
|
||||
$option['context'] = $context;
|
||||
// load to the getters
|
||||
if ($option['row_type'] == 1)
|
||||
{
|
||||
$item->main_get[] = $option;
|
||||
if ($on_field[0] === 'a'
|
||||
|| isset($_part_of_a[$join_field[0]])
|
||||
|| isset($_part_of_a[$on_field[0]]))
|
||||
{
|
||||
$this->sitemainget->set(
|
||||
$this->config->build_target . '.' . $view_code . '.' .
|
||||
$option['as'], $option['as']
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->sitedynamicget->set(
|
||||
$this->config->build_target . '.' . $view_code . '.' .
|
||||
$option['as'] . '.' . $join_field[1],
|
||||
$on_field[0]
|
||||
);
|
||||
}
|
||||
}
|
||||
elseif ($option['row_type'] == 2)
|
||||
{
|
||||
$item->custom_get[] = $option;
|
||||
if ($on_field[0] != 'a')
|
||||
{
|
||||
$this->sitedynamicget->set(
|
||||
$this->config->build_target . '.' . $view_code . '.' .
|
||||
$option['as'] . '.' . $join_field[1],
|
||||
$on_field[0]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
unset($item->join_view_table[$nr]);
|
||||
}
|
||||
}
|
||||
unset($item->join_view_table);
|
||||
|
||||
// set join_db_table details
|
||||
$item->join_db_table = json_decode(
|
||||
(string) $item->join_db_table, true
|
||||
);
|
||||
|
||||
if (ArrayHelper::check($item->join_db_table))
|
||||
{
|
||||
// start the part of a table bucket
|
||||
$_part_of_a = [];
|
||||
// build relationship
|
||||
$_relationship = array_map(
|
||||
function ($op) use (&$_part_of_a) {
|
||||
$bucket = [];
|
||||
// array(on_field_as, on_field)
|
||||
$bucket['on_field'] = array_map(
|
||||
'trim',
|
||||
explode('.', (string) $op['on_field'])
|
||||
);
|
||||
// array(join_field_as, join_field)
|
||||
$bucket['join_field'] = array_map(
|
||||
'trim',
|
||||
explode('.', (string) $op['join_field'])
|
||||
);
|
||||
// triget filed that has table a relationship
|
||||
if ($op['row_type'] == 1
|
||||
&& ($bucket['on_field'][0] === 'a'
|
||||
|| isset($_part_of_a[$bucket['on_field'][0]])
|
||||
|| isset($_part_of_a[$bucket['join_field'][0]])))
|
||||
{
|
||||
$_part_of_a[$op['as']] = $op['as'];
|
||||
}
|
||||
|
||||
return $bucket;
|
||||
}, $item->join_db_table
|
||||
);
|
||||
|
||||
// loop joints
|
||||
foreach ($item->join_db_table as $nr => &$option1)
|
||||
{
|
||||
if (StringHelper::check($option1['selection']))
|
||||
{
|
||||
// convert the type
|
||||
$option1['type'] = $this->jointer[$option1['type']];
|
||||
// convert the operator
|
||||
$option1['operator'] = $this->operator[$option1['operator']];
|
||||
// get the on field values
|
||||
$on_field = $_relationship[$nr]['on_field'];
|
||||
// get the join field values
|
||||
$join_field = $_relationship[$nr]['join_field'];
|
||||
// set selection
|
||||
$option1['selection'] = $this->selection->get(
|
||||
$item->key,
|
||||
$view_code,
|
||||
$option1['selection'],
|
||||
$option1['db_table'],
|
||||
$option1['as'],
|
||||
'db',
|
||||
$option1['row_type']
|
||||
);
|
||||
$option1['key'] = $item->key;
|
||||
$option1['context'] = $context;
|
||||
// load to the getters
|
||||
if ($option1['row_type'] == 1)
|
||||
{
|
||||
$item->main_get[] = $option1;
|
||||
if ($on_field[0] === 'a'
|
||||
|| isset($_part_of_a[$join_field[0]])
|
||||
|| isset($_part_of_a[$on_field[0]]))
|
||||
{
|
||||
$this->sitemainget->set(
|
||||
$this->config->build_target . '.' . $view_code . '.' .
|
||||
$option1['as'], $option1['as']
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->sitedynamicget->set(
|
||||
$this->config->build_target . '.' . $view_code . '.' .
|
||||
$option1['as'] . '.' . $join_field[1],
|
||||
$on_field[0]
|
||||
);
|
||||
}
|
||||
}
|
||||
elseif ($option1['row_type'] == 2)
|
||||
{
|
||||
$item->custom_get[] = $option1;
|
||||
if ($on_field[0] != 'a')
|
||||
{
|
||||
$this->sitedynamicget->set(
|
||||
$this->config->build_target . '.' . $view_code . '.' .
|
||||
$option1['as'] . '.' . $join_field[1],
|
||||
$on_field[0]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
unset($item->join_db_table[$nr]);
|
||||
}
|
||||
}
|
||||
unset($item->join_db_table);
|
||||
|
||||
// set filter details
|
||||
$item->filter = json_decode(
|
||||
(string) $item->filter, true
|
||||
);
|
||||
|
||||
if (ArrayHelper::check($item->filter))
|
||||
{
|
||||
foreach ($item->filter as $nr => &$option2)
|
||||
{
|
||||
if (isset($option2['operator']))
|
||||
{
|
||||
$option2['operator'] = $this->operator[$option2['operator']];
|
||||
$option2['state_key'] = $this->placeholder->update_(
|
||||
$this->customcode->update(
|
||||
$option2['state_key']
|
||||
)
|
||||
);
|
||||
$option2['key'] = $item->key;
|
||||
}
|
||||
else
|
||||
{
|
||||
unset($item->filter[$nr]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set where details
|
||||
$item->where = json_decode((string) $item->where, true);
|
||||
if (ArrayHelper::check($item->where))
|
||||
{
|
||||
foreach ($item->where as $nr => &$option3)
|
||||
{
|
||||
if (isset($option3['operator']))
|
||||
{
|
||||
$option3['operator'] = $this->operator[$option3['operator']];
|
||||
}
|
||||
else
|
||||
{
|
||||
unset($item->where[$nr]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
unset($item->where);
|
||||
}
|
||||
|
||||
// set order details
|
||||
$item->order = json_decode((string) $item->order, true);
|
||||
if (!ArrayHelper::check($item->order))
|
||||
{
|
||||
unset($item->order);
|
||||
}
|
||||
|
||||
// set grouping
|
||||
$item->group = json_decode((string) $item->group, true);
|
||||
if (!ArrayHelper::check($item->group))
|
||||
{
|
||||
unset($item->group);
|
||||
}
|
||||
|
||||
// set global details
|
||||
$item->global = json_decode(
|
||||
(string) $item->global, true
|
||||
);
|
||||
|
||||
if (!ArrayHelper::check($item->global))
|
||||
{
|
||||
unset($item->global);
|
||||
}
|
||||
$this->processJoins($item, $viewCode, $context);
|
||||
$this->processFilters($item);
|
||||
$this->processWhere($item);
|
||||
$this->processOrderGroupGlobal($item);
|
||||
}
|
||||
else
|
||||
{
|
||||
// when we have a custom query script we do not add the dynamic options
|
||||
unset($item->join_view_table);
|
||||
unset($item->join_db_table);
|
||||
unset($item->filter);
|
||||
unset(
|
||||
$item->join_view_table,
|
||||
$item->join_db_table,
|
||||
$item->filter,
|
||||
$item->where,
|
||||
$item->order,
|
||||
$item->group,
|
||||
$item->global
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the main_get using view-based data.
|
||||
*
|
||||
* @param object $item The item data
|
||||
* @param string $viewCode The view code name
|
||||
* @param string $context The context for events
|
||||
*
|
||||
* @return void
|
||||
* @since 5.1.1
|
||||
*/
|
||||
private function configureViewSource(object &$item, string $viewCode, string $context): void
|
||||
{
|
||||
if ($item->select_all == 1)
|
||||
{
|
||||
$item->view_selection = '*';
|
||||
}
|
||||
|
||||
$item->main_get[] = [
|
||||
'selection' => $this->selection->get(
|
||||
$item->key,
|
||||
$viewCode,
|
||||
$item->view_selection,
|
||||
$item->view_table_main,
|
||||
'a',
|
||||
'view'
|
||||
),
|
||||
'as' => 'a',
|
||||
'key' => $item->key,
|
||||
'context' => $context
|
||||
];
|
||||
|
||||
unset($item->view_selection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the main_get using database-table data.
|
||||
*
|
||||
* @param object $item The item data
|
||||
* @param string $viewCode The view code name
|
||||
* @param string $context The context for events
|
||||
*
|
||||
* @return void
|
||||
* @since 5.1.1
|
||||
*/
|
||||
private function configureDbSource(object &$item, string $viewCode, string $context): void
|
||||
{
|
||||
if ($item->select_all == 1)
|
||||
{
|
||||
$item->db_selection = '*';
|
||||
}
|
||||
|
||||
$item->main_get[] = [
|
||||
'selection' => $this->selection->get(
|
||||
$item->key,
|
||||
$viewCode,
|
||||
$item->db_selection,
|
||||
$item->db_table_main,
|
||||
'a',
|
||||
'db'
|
||||
),
|
||||
'as' => 'a',
|
||||
'key' => $item->key,
|
||||
'context' => $context
|
||||
];
|
||||
|
||||
unset($item->db_selection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the main_get using a custom PHP query.
|
||||
*
|
||||
* @param object $item The item data
|
||||
* @param string $viewCode The view code name
|
||||
* @param string $context The context for events
|
||||
*
|
||||
* @return void
|
||||
* @since 5.1.1
|
||||
*/
|
||||
private function configureCustomSource(object &$item, string $viewCode, string $context): void
|
||||
{
|
||||
$this->guiMapper['field'] = 'php_custom_get';
|
||||
|
||||
$query = $this->gui->set(
|
||||
$this->customcode->update(
|
||||
base64_decode((string) $item->php_custom_get)
|
||||
),
|
||||
$this->guiMapper
|
||||
);
|
||||
|
||||
$table = GetHelper::between($query, '$query->from(', ')');
|
||||
$tableName = '';
|
||||
|
||||
if (StringHelper::check($table) && strpos($table, '#__') !== false)
|
||||
{
|
||||
$tableName = GetHelper::between($table, '#__', "'") ?: GetHelper::between($table, '#__', '"');
|
||||
}
|
||||
|
||||
$item->main_get[] = [
|
||||
'selection' => [
|
||||
'select' => $query,
|
||||
'from' => '',
|
||||
'table' => '',
|
||||
'type' => '',
|
||||
'name' => $tableName
|
||||
],
|
||||
'as' => 'a',
|
||||
'key' => $item->key,
|
||||
'context' => $context
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Process filter statements on the item.
|
||||
*
|
||||
* @param object $item The item data
|
||||
*
|
||||
* @return void
|
||||
* @since 5.1.1
|
||||
*/
|
||||
private function processFilters(object &$item): void
|
||||
{
|
||||
$item->filter = json_decode((string) $item->filter, true);
|
||||
|
||||
if (ArrayHelper::check($item->filter))
|
||||
{
|
||||
foreach ($item->filter as $nr => &$option)
|
||||
{
|
||||
if (isset($option['operator']) && isset($this->operator[$option['operator']]))
|
||||
{
|
||||
$option['operator'] = $this->operator[$option['operator']];
|
||||
$option['state_key'] = $this->placeholder->update_(
|
||||
$this->customcode->update($option['state_key'])
|
||||
);
|
||||
$option['key'] = $item->key;
|
||||
}
|
||||
else
|
||||
{
|
||||
unset($item->filter[$nr]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process where clause on the item.
|
||||
*
|
||||
* @param object $item The item data
|
||||
*
|
||||
* @return void
|
||||
* @since 5.1.1
|
||||
*/
|
||||
private function processWhere(object &$item): void
|
||||
{
|
||||
$item->where = json_decode((string) $item->where, true);
|
||||
|
||||
if (ArrayHelper::check($item->where))
|
||||
{
|
||||
foreach ($item->where as $nr => &$option)
|
||||
{
|
||||
if (isset($option['operator']) && isset($this->operator[$option['operator']]))
|
||||
{
|
||||
$option['operator'] = $this->operator[$option['operator']];
|
||||
}
|
||||
else
|
||||
{
|
||||
unset($item->where[$nr]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
unset($item->where);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process order, group, and global JSON attributes on the item.
|
||||
*
|
||||
* @param object $item The item data
|
||||
*
|
||||
* @return void
|
||||
* @since 5.1.1
|
||||
*/
|
||||
private function processOrderGroupGlobal(object &$item): void
|
||||
{
|
||||
$item->order = json_decode((string) $item->order, true);
|
||||
|
||||
if (!ArrayHelper::check($item->order))
|
||||
{
|
||||
unset($item->order);
|
||||
}
|
||||
|
||||
$item->group = json_decode((string) $item->group, true);
|
||||
|
||||
if (!ArrayHelper::check($item->group))
|
||||
{
|
||||
unset($item->group);
|
||||
}
|
||||
|
||||
$item->global = json_decode((string) $item->global, true);
|
||||
|
||||
if (!ArrayHelper::check($item->global))
|
||||
{
|
||||
unset($item->global);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process join logic for both view and db tables.
|
||||
*
|
||||
* @param object $item The item data
|
||||
* @param string $viewCode The view code name
|
||||
* @param string $context The context for events
|
||||
*
|
||||
* @return void
|
||||
* @since 5.1.1
|
||||
*/
|
||||
private function processJoins(object &$item, string $viewCode, string $context): void
|
||||
{
|
||||
$this->processJoinGroup($item, $viewCode, $context, 'join_view_table', 'view');
|
||||
$this->processJoinGroup($item, $viewCode, $context, 'join_db_table', 'db');
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal helper to process individual join groups.
|
||||
*
|
||||
* @param object $item The item data
|
||||
* @param string $viewCode The view code name
|
||||
* @param string $context The context for events
|
||||
* @param string $joinKey The property name (join_view_table or join_db_table)
|
||||
* @param string $sourceType The source type (view or db)
|
||||
*
|
||||
* @return void
|
||||
* @since 5.1.1
|
||||
*/
|
||||
private function processJoinGroup(object &$item, string $viewCode, string $context, string $joinKey, string $sourceType): void
|
||||
{
|
||||
$joins = json_decode((string) ($item->{$joinKey} ?? ''), true);
|
||||
if (!ArrayHelper::check($joins))
|
||||
{
|
||||
unset($item->$joinKey);
|
||||
return;
|
||||
}
|
||||
|
||||
$_part_of_a = [];
|
||||
$_relationship = array_map(function ($op) use (&$_part_of_a) {
|
||||
$bucket = [];
|
||||
$bucket['on_field'] = array_map('trim', explode('.', (string) $op['on_field']));
|
||||
$bucket['join_field'] = array_map('trim', explode('.', (string) $op['join_field']));
|
||||
if ($op['row_type'] == 1
|
||||
&& ($bucket['on_field'][0] === 'a'
|
||||
|| isset($_part_of_a[$bucket['on_field'][0]])
|
||||
|| isset($_part_of_a[$bucket['join_field'][0]])))
|
||||
{
|
||||
$_part_of_a[$op['as']] = $op['as'];
|
||||
}
|
||||
return $bucket;
|
||||
}, $joins);
|
||||
|
||||
foreach ($joins as $nr => &$option)
|
||||
{
|
||||
if (!StringHelper::check($option['selection'] ?? null))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$option['type'] = $this->jointer[$option['type']];
|
||||
$option['operator'] = $this->operator[$option['operator']] ?? null;
|
||||
$on_field = $_relationship[$nr]['on_field'];
|
||||
$join_field = $_relationship[$nr]['join_field'];
|
||||
|
||||
$option['selection'] = $this->selection->get(
|
||||
$item->key,
|
||||
$viewCode,
|
||||
$option['selection'],
|
||||
$option[$sourceType === 'view' ? 'view_table' : 'db_table'],
|
||||
$option['as'],
|
||||
$sourceType,
|
||||
$option['row_type']
|
||||
);
|
||||
|
||||
$option['key'] = $item->key;
|
||||
$option['context'] = $context;
|
||||
|
||||
if ($option['row_type'] == 1)
|
||||
{
|
||||
$item->main_get[] = $option;
|
||||
if ($on_field[0] === 'a' || isset($_part_of_a[$join_field[0]]) || isset($_part_of_a[$on_field[0]]))
|
||||
{
|
||||
$this->sitemainget->set(
|
||||
"{$this->config->build_target}.{$viewCode}.{$option['as']}",
|
||||
$option['as']
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->sitedynamicget->set(
|
||||
"{$this->config->build_target}.{$viewCode}.{$option['as']}.{$join_field[1]}",
|
||||
$on_field[0]
|
||||
);
|
||||
}
|
||||
}
|
||||
elseif ($option['row_type'] == 2)
|
||||
{
|
||||
$item->custom_get[] = $option;
|
||||
if ($on_field[0] !== 'a')
|
||||
{
|
||||
$this->sitedynamicget->set(
|
||||
"{$this->config->build_target}.{$viewCode}.{$option['as']}.{$join_field[1]}",
|
||||
$on_field[0]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unset($item->$joinKey);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -63,9 +63,18 @@ class Sql
|
||||
*/
|
||||
public function set(object &$item)
|
||||
{
|
||||
if (isset($item->add_sql) && $item->add_sql == 1 && isset($item->source))
|
||||
if (isset($item->add_sql) && (int) $item->add_sql === 1
|
||||
&& isset($item->source) && isset($item->name_single_code))
|
||||
{
|
||||
if ($item->source == 1 && isset($item->tables) &&
|
||||
// avoid setting this a multiple time for the same name_singe_code
|
||||
if ((int) $item->source === 1 && isset($this->dispenser->hub['sql'])
|
||||
&& is_array($this->dispenser->hub['sql'])
|
||||
&& isset($this->dispenser->hub['sql'][$item->name_single_code]))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ((int) $item->source === 1 && isset($item->tables) &&
|
||||
($string = $this->dump->get(
|
||||
$item->tables, $item->name_single_code, $item->guid
|
||||
)) !== null)
|
||||
@@ -77,7 +86,7 @@ class Sql
|
||||
$this->dispenser->hub['sql'][$item->name_single_code]
|
||||
= $string;
|
||||
}
|
||||
elseif ($item->source == 2 && isset($item->sql))
|
||||
elseif ((int) $item->source === 2 && isset($item->sql))
|
||||
{
|
||||
// add the SQL dump string
|
||||
$this->dispenser->set(
|
||||
|
@@ -12,11 +12,11 @@
|
||||
namespace VDM\Joomla\Componentbuilder\Compiler\Model;
|
||||
|
||||
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\Database\DatabaseInterface as JoomlaDatabase;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Registry;
|
||||
use VDM\Joomla\Utilities\ArrayHelper;
|
||||
use VDM\Joomla\Database\QuoteTrait;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Utilities\Placefix;
|
||||
use VDM\Joomla\Utilities\StringHelper;
|
||||
|
||||
|
||||
/**
|
||||
@@ -26,6 +26,13 @@ use VDM\Joomla\Utilities\StringHelper;
|
||||
*/
|
||||
class Sqldump
|
||||
{
|
||||
/**
|
||||
* Function to quote values
|
||||
*
|
||||
* @since 5.1.1
|
||||
*/
|
||||
use QuoteTrait;
|
||||
|
||||
/**
|
||||
* The compiler registry
|
||||
*
|
||||
@@ -37,245 +44,91 @@ class Sqldump
|
||||
/**
|
||||
* Database object to query local DB
|
||||
*
|
||||
* @var JoomlaDatabase
|
||||
* @since 3.2.0
|
||||
**/
|
||||
protected $db;
|
||||
protected JoomlaDatabase $db;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param Registry $registry The compiler registry object.
|
||||
|
||||
* @param Registry $registry The compiler registry object.
|
||||
* @param JoomlaDatabase|null $db The joomla database object.
|
||||
* @since 3.2.0
|
||||
*/
|
||||
public function __construct(Registry $registry)
|
||||
public function __construct(Registry $registry, ?JoomlaDatabase $db = null)
|
||||
{
|
||||
$this->registry = $registry;
|
||||
$this->db = Factory::getDbo();
|
||||
$this->db = $db ?: Factory::getContainer()->get(JoomlaDatabase::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get SQL Dump
|
||||
* Generate SQL dump for given view data.
|
||||
*
|
||||
* @param array $tables The tables to use in build
|
||||
* @param string $view The target view/table to dump in
|
||||
* @param string $view_guid The guid of the target view
|
||||
* @param array $tables Tables configuration array.
|
||||
* @param string $view Target view name.
|
||||
* @param string $viewGuid Unique GUID for view (used in registry path).
|
||||
*
|
||||
* @return string|null The data found with the alias
|
||||
* @since 3.2.0
|
||||
* @return string|null SQL dump or null on failure.
|
||||
* @since 3.2.0
|
||||
*/
|
||||
public function get(array $tables, string $view, string $view_guid): ?string
|
||||
public function get(array $tables, string $view, string $viewGuid): ?string
|
||||
{
|
||||
// first build a query statement to get all the data (insure it must be added - check the tweaking)
|
||||
if (ArrayHelper::check($tables)
|
||||
&& $this->registry-> // default is to add
|
||||
get('builder.sql_tweak.' . $view_guid . '.add', true))
|
||||
if (empty($tables) || !$this->shouldBuildDump($viewGuid))
|
||||
{
|
||||
$counter = 'a';
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create a new query object.
|
||||
$query = $this->db->getQuery(true);
|
||||
$query = $this->db->getQuery(true);
|
||||
$runQuery = false;
|
||||
$alias = 'a';
|
||||
$fieldsAdded = false;
|
||||
|
||||
// switch to only trigger the run of the query if we have tables to query
|
||||
$run_query = false;
|
||||
foreach ($tables as $table)
|
||||
foreach ($tables as $tableConfig)
|
||||
{
|
||||
if (empty($tableConfig['table']) || empty($tableConfig['sourcemap']))
|
||||
{
|
||||
if (isset($table['table']))
|
||||
continue;
|
||||
}
|
||||
|
||||
$fieldMappings = $this->parseFieldMappings($tableConfig['sourcemap'], $alias);
|
||||
|
||||
if ($alias === 'a')
|
||||
{
|
||||
if (!empty($fieldMappings['select']))
|
||||
{
|
||||
if ($counter === 'a')
|
||||
{
|
||||
// the main table fields
|
||||
if (strpos((string) $table['sourcemap'], PHP_EOL) !== false)
|
||||
{
|
||||
$fields = explode(PHP_EOL, (string) $table['sourcemap']);
|
||||
if (ArrayHelper::check($fields))
|
||||
{
|
||||
// reset array buckets
|
||||
$sourceArray = [];
|
||||
$targetArray = [];
|
||||
foreach ($fields as $field)
|
||||
{
|
||||
if (strpos($field, "=>") !== false)
|
||||
{
|
||||
list($source, $target) = explode(
|
||||
"=>", $field
|
||||
);
|
||||
$sourceArray[] = $counter . '.' . trim(
|
||||
$source
|
||||
);
|
||||
$targetArray[] = trim($target);
|
||||
}
|
||||
}
|
||||
if (ArrayHelper::check(
|
||||
$sourceArray
|
||||
)
|
||||
&& ArrayHelper::check(
|
||||
$targetArray
|
||||
))
|
||||
{
|
||||
// add to query
|
||||
$query->select(
|
||||
$this->db->quoteName(
|
||||
$sourceArray, $targetArray
|
||||
)
|
||||
);
|
||||
$query->from(
|
||||
'#__' . $table['table'] . ' AS a'
|
||||
);
|
||||
$run_query = true;
|
||||
}
|
||||
// we may need to filter the selection
|
||||
if (($ids_ = $this->registry->
|
||||
get('builder.sql_tweak.' . $view_guid . '.where', null)) !== null)
|
||||
{
|
||||
// add to query the where filter
|
||||
$query->where(
|
||||
'a.id IN (' . $ids_ . ')'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// the other tables
|
||||
if (strpos((string) $table['sourcemap'], PHP_EOL) !== false)
|
||||
{
|
||||
$fields = explode(PHP_EOL, (string) $table['sourcemap']);
|
||||
if (ArrayHelper::check($fields))
|
||||
{
|
||||
// reset array buckets
|
||||
$sourceArray = [];
|
||||
$targetArray = [];
|
||||
foreach ($fields as $field)
|
||||
{
|
||||
if (strpos($field, "=>") !== false)
|
||||
{
|
||||
list($source, $target) = explode(
|
||||
"=>", $field
|
||||
);
|
||||
$sourceArray[] = $counter . '.' . trim(
|
||||
$source
|
||||
);
|
||||
$targetArray[] = trim($target);
|
||||
}
|
||||
if (strpos($field, "==") !== false)
|
||||
{
|
||||
list($aKey, $bKey) = explode(
|
||||
"==", $field
|
||||
);
|
||||
// add to query
|
||||
$query->join(
|
||||
'LEFT', $this->db->quoteName(
|
||||
'#__' . $table['table'],
|
||||
$counter
|
||||
) . ' ON (' . $this->db->quoteName(
|
||||
'a.' . trim($aKey)
|
||||
) . ' = ' . $this->db->quoteName(
|
||||
$counter . '.' . trim($bKey)
|
||||
) . ')'
|
||||
);
|
||||
}
|
||||
}
|
||||
if (ArrayHelper::check(
|
||||
$sourceArray
|
||||
)
|
||||
&& ArrayHelper::check(
|
||||
$targetArray
|
||||
))
|
||||
{
|
||||
// add to query
|
||||
$query->select(
|
||||
$this->db->quoteName(
|
||||
$sourceArray, $targetArray
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$counter++;
|
||||
$query->select($this->db->quoteName($fieldMappings['select'], $fieldMappings['alias']));
|
||||
$query->from($this->db->quoteName('#__' . $tableConfig['table'], $alias));
|
||||
$this->applyWhereFilter($query, $viewGuid);
|
||||
$fieldsAdded = true;
|
||||
$runQuery = true;
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->applyJoins($query, $tableConfig['table'], $alias, $fieldMappings['joins']);
|
||||
if (!empty($fieldMappings['select']))
|
||||
{
|
||||
// see where
|
||||
// var_dump($view);
|
||||
// jexit();
|
||||
$query->select($this->db->quoteName($fieldMappings['select'], $fieldMappings['alias']));
|
||||
$fieldsAdded = true;
|
||||
}
|
||||
}
|
||||
|
||||
// check if we should run query
|
||||
if ($run_query)
|
||||
{
|
||||
// now get the data
|
||||
$this->db->setQuery($query);
|
||||
$this->db->execute();
|
||||
$alias++;
|
||||
}
|
||||
|
||||
if ($runQuery && $fieldsAdded)
|
||||
{
|
||||
try {
|
||||
$this->db->setQuery($query)->execute();
|
||||
|
||||
if ($this->db->getNumRows())
|
||||
{
|
||||
// get the data
|
||||
$data = $this->db->loadObjectList();
|
||||
|
||||
// start building the MySql dump
|
||||
$dump = "--";
|
||||
$dump .= PHP_EOL . "-- Dumping data for table `#__"
|
||||
. Placefix::_("component") . "_" . $view
|
||||
. "`";
|
||||
$dump .= PHP_EOL . "--";
|
||||
$dump .= PHP_EOL . PHP_EOL . "INSERT INTO `#__" . Placefix::_("component") . "_" . $view . "` (";
|
||||
foreach ($data as $line)
|
||||
{
|
||||
$comaSet = 0;
|
||||
foreach ($line as $fieldName => $fieldValue)
|
||||
{
|
||||
if ($comaSet == 0)
|
||||
{
|
||||
$dump .= $this->db->quoteName($fieldName);
|
||||
}
|
||||
else
|
||||
{
|
||||
$dump .= ", " . $this->db->quoteName(
|
||||
$fieldName
|
||||
);
|
||||
}
|
||||
$comaSet++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
$dump .= ") VALUES";
|
||||
$coma = 0;
|
||||
foreach ($data as $line)
|
||||
{
|
||||
if ($coma == 0)
|
||||
{
|
||||
$dump .= PHP_EOL . "(";
|
||||
}
|
||||
else
|
||||
{
|
||||
$dump .= "," . PHP_EOL . "(";
|
||||
}
|
||||
$comaSet = 0;
|
||||
foreach ($line as $fieldName => $fieldValue)
|
||||
{
|
||||
if ($comaSet == 0)
|
||||
{
|
||||
$dump .= $this->escape($fieldValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
$dump .= ", " . $this->escape(
|
||||
$fieldValue
|
||||
);
|
||||
}
|
||||
$comaSet++;
|
||||
}
|
||||
$dump .= ")";
|
||||
$coma++;
|
||||
}
|
||||
$dump .= ";";
|
||||
|
||||
// return build dump query
|
||||
return $dump;
|
||||
return $this->buildSqlDump($view, $data);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
// Log or handle exception if needed
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,35 +136,141 @@ class Sqldump
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape the values for a SQL dump
|
||||
* Determine if a dump should be built.
|
||||
*
|
||||
* @param string|array $value the value to escape
|
||||
* @param string $viewGuid
|
||||
*
|
||||
* @return string|array on success with escaped string
|
||||
* @since 3.2.0
|
||||
* @return bool
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected function shouldBuildDump(string $viewGuid): bool
|
||||
{
|
||||
return (bool) $this->registry->get("builder.sql_tweak.{$viewGuid}.add", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply optional WHERE clause if set in registry.
|
||||
*
|
||||
* @param $query
|
||||
* @param string $viewGuid
|
||||
*
|
||||
* @return void
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected function applyWhereFilter($query, string $viewGuid): void
|
||||
{
|
||||
if ($ids = $this->registry->get("builder.sql_tweak.{$viewGuid}.where"))
|
||||
{
|
||||
$query->where("a.id IN ({$ids})");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse sourcemap lines into SELECT and JOIN definitions.
|
||||
*
|
||||
* @param string $map
|
||||
* @param string $alias
|
||||
*
|
||||
* @return array{select: string[], alias: string[], joins: array<int, array{from: string, to: string}>}
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected function parseFieldMappings(string $map, string $alias): array
|
||||
{
|
||||
$lines = explode(PHP_EOL, trim($map));
|
||||
$select = [];
|
||||
$aliasFields = [];
|
||||
$joins = [];
|
||||
|
||||
foreach ($lines as $line)
|
||||
{
|
||||
$line = trim($line);
|
||||
|
||||
if (str_contains($line, '=>'))
|
||||
{
|
||||
[$from, $to] = array_map('trim', explode('=>', $line));
|
||||
$select[] = "{$alias}.{$from}";
|
||||
$aliasFields[] = $to;
|
||||
}
|
||||
elseif (str_contains($line, '=='))
|
||||
{
|
||||
[$left, $right] = array_map('trim', explode('==', $line));
|
||||
$joins[] = ['from' => $left, 'to' => $right];
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'select' => $select,
|
||||
'alias' => $aliasFields,
|
||||
'joins' => $joins,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply JOINs to the query.
|
||||
*
|
||||
* @param $query
|
||||
* @param string $table
|
||||
* @param string $alias
|
||||
* @param array $joins
|
||||
*
|
||||
* @return void
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected function applyJoins($query, string $table, string $alias, array $joins): void
|
||||
{
|
||||
foreach ($joins as $join)
|
||||
{
|
||||
$query->join(
|
||||
'LEFT',
|
||||
$this->db->quoteName("#__{$table}", $alias) . ' ON (' .
|
||||
$this->db->quoteName("a.{$join['from']}") . ' = ' .
|
||||
$this->db->quoteName("{$alias}.{$join['to']}") . ')'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the SQL INSERT DUMP statement from data.
|
||||
*
|
||||
* @param string $view
|
||||
* @param array<object> $data
|
||||
*
|
||||
* @return string
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected function buildSqlDump(string $view, array $data): string
|
||||
{
|
||||
$tableName = "#__" . Placefix::_("component") . "_{$view}";
|
||||
$fields = array_keys((array) $data[0]);
|
||||
|
||||
$header = "--\n-- Dumping data for table `{$tableName}`\n--\n";
|
||||
$insert = "INSERT INTO `{$tableName}` (" . implode(', ', array_map([$this->db, 'quoteName'], $fields)) . ") VALUES\n";
|
||||
|
||||
$rows = array_map(function ($row)
|
||||
{
|
||||
$values = array_map([$this, 'escape'], (array) $row);
|
||||
return '(' . implode(', ', $values) . ')';
|
||||
}, $data);
|
||||
|
||||
return $header . $insert . implode(",\n", $rows) . ";";
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape SQL value for safe dump using strict quoting rules.
|
||||
*
|
||||
* @param mixed $value The value to escape.
|
||||
*
|
||||
* @return mixed Escaped SQL-safe literal or quoted string.
|
||||
* @since 3.2.0
|
||||
*/
|
||||
protected function escape($value)
|
||||
{
|
||||
// if array then return mapped
|
||||
if (ArrayHelper::check($value))
|
||||
if (is_array($value))
|
||||
{
|
||||
return array_map(__METHOD__, $value);
|
||||
return implode(', ', array_map([$this, 'escape'], $value));
|
||||
}
|
||||
|
||||
// if string make sure it is correctly escaped
|
||||
if (StringHelper::check($value) && !is_numeric($value))
|
||||
{
|
||||
return $this->db->quote($value);
|
||||
}
|
||||
|
||||
// if empty value return place holder
|
||||
if (empty($value))
|
||||
{
|
||||
return "''";
|
||||
}
|
||||
|
||||
// if not array or string then return number
|
||||
return $value;
|
||||
return $this->quote($value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -15,7 +15,6 @@ namespace VDM\Joomla\Componentbuilder\Compiler\Model;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Registry;
|
||||
use VDM\Joomla\Utilities\JsonHelper;
|
||||
use VDM\Joomla\Utilities\ArrayHelper;
|
||||
use VDM\Joomla\Utilities\ObjectHelper;
|
||||
|
||||
|
||||
/**
|
||||
@@ -63,117 +62,101 @@ class Sqltweaking
|
||||
if (ArrayHelper::check($item->sql_tweak))
|
||||
{
|
||||
// build the tweak settings
|
||||
$this->tweak(
|
||||
array_map(
|
||||
fn($array) => array_map(
|
||||
function ($value) {
|
||||
if (!ArrayHelper::check($value)
|
||||
&& !ObjectHelper::check(
|
||||
$value
|
||||
)
|
||||
&& strval($value) === strval(
|
||||
intval($value)
|
||||
))
|
||||
{
|
||||
return $value;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}, $array
|
||||
), array_values($item->sql_tweak)
|
||||
)
|
||||
);
|
||||
$this->tweak($item->sql_tweak);
|
||||
}
|
||||
|
||||
unset($item->sql_tweak);
|
||||
}
|
||||
|
||||
/**
|
||||
* To limit the SQL Demo data build in the views
|
||||
* Limit the SQL Demo data build in the views by applying tweak settings.
|
||||
*
|
||||
* @param array $settings Tweaking array.
|
||||
* @param array $settings The tweak configuration array.
|
||||
*
|
||||
* @return void
|
||||
* @since 3.2.0
|
||||
* @since 3.2.0
|
||||
*/
|
||||
protected function tweak($settings)
|
||||
protected function tweak(array $settings): void
|
||||
{
|
||||
if (ArrayHelper::check($settings))
|
||||
if (!ArrayHelper::check($settings))
|
||||
{
|
||||
foreach ($settings as $setting)
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($settings as $setting)
|
||||
{
|
||||
$adminView = $setting['adminview'] ?? null;
|
||||
|
||||
if (!$adminView)
|
||||
{
|
||||
// should sql dump be added
|
||||
if (1 == $setting['add_sql'])
|
||||
continue;
|
||||
}
|
||||
|
||||
$addSql = (int) ($setting['add_sql'] ?? 0);
|
||||
$addSqlOptions = (int) ($setting['add_sql_options'] ?? 0);
|
||||
|
||||
if ($addSql === 1 && $addSqlOptions === 2)
|
||||
{
|
||||
$ids = $setting['ids'] ?? '';
|
||||
$idArray = $this->normalizeIds($ids);
|
||||
|
||||
if (!empty($idArray))
|
||||
{
|
||||
// add sql (by option)
|
||||
if (2 == $setting['add_sql_options'])
|
||||
{
|
||||
// rest always
|
||||
$id_array = [];
|
||||
|
||||
// by id (first remove backups)
|
||||
$ids = $setting['ids'];
|
||||
|
||||
// now get the ids
|
||||
if (strpos((string) $ids, ',') !== false)
|
||||
{
|
||||
$id_array = (array) array_map(
|
||||
'trim', explode(',', (string) $ids)
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
$id_array[] = trim((string) $ids);
|
||||
}
|
||||
$id_array_new = [];
|
||||
|
||||
// check for ranges
|
||||
foreach ($id_array as $key => $id)
|
||||
{
|
||||
if (strpos($id, '=>') !== false)
|
||||
{
|
||||
$id_range = (array) array_map(
|
||||
'trim', explode('=>', $id)
|
||||
);
|
||||
unset($id_array[$key]);
|
||||
// build range
|
||||
if (count((array) $id_range) == 2)
|
||||
{
|
||||
$range = range(
|
||||
$id_range[0], $id_range[1]
|
||||
);
|
||||
$id_array_new = [...$id_array_new, ...$range];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ArrayHelper::check($id_array_new))
|
||||
{
|
||||
$id_array = [...$id_array_new, ...$id_array];
|
||||
}
|
||||
|
||||
// final fixing to array
|
||||
if (ArrayHelper::check($id_array))
|
||||
{
|
||||
// unique
|
||||
$id_array = array_unique($id_array, SORT_NUMERIC);
|
||||
// sort
|
||||
sort($id_array, SORT_NUMERIC);
|
||||
// now set it to global
|
||||
$this->registry->
|
||||
set('builder.sql_tweak.' . $setting['adminview'] . '.where', implode(',', $id_array));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// do not add sql dump options
|
||||
$this->registry->
|
||||
set('builder.sql_tweak.' . $setting['adminview'] . '.add', false);
|
||||
$this->registry->set(
|
||||
'builder.sql_tweak.' . $adminView . '.where',
|
||||
implode(',', $idArray)
|
||||
);
|
||||
}
|
||||
}
|
||||
elseif ($addSql === 0)
|
||||
{
|
||||
$this->registry->set(
|
||||
'builder.sql_tweak.' . $adminView . '.add',
|
||||
false
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Normalize a comma-separated string of IDs or ID ranges into a unique, sorted array.
|
||||
*
|
||||
* Supports individual IDs (e.g., "1,3,5") and ranges (e.g., "10 => 12").
|
||||
*
|
||||
* @param string $ids Raw ID string from settings.
|
||||
*
|
||||
* @return array<int> Normalized list of numeric IDs.
|
||||
* @since 5.1.1
|
||||
*/
|
||||
private function normalizeIds(string $ids): array
|
||||
{
|
||||
$rawIds = array_map('trim', explode(',', $ids));
|
||||
$finalIds = [];
|
||||
|
||||
foreach ($rawIds as $id)
|
||||
{
|
||||
if (strpos($id, '=>') !== false)
|
||||
{
|
||||
$rangeParts = array_map('trim', explode('=>', $id));
|
||||
|
||||
if (count($rangeParts) === 2 && is_numeric($rangeParts[0]) && is_numeric($rangeParts[1]))
|
||||
{
|
||||
$range = range((int) $rangeParts[0], (int) $rangeParts[1]);
|
||||
$finalIds = array_merge($finalIds, $range);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_numeric($id))
|
||||
{
|
||||
$finalIds[] = (int) $id;
|
||||
}
|
||||
}
|
||||
|
||||
$finalIds = array_unique($finalIds, SORT_NUMERIC);
|
||||
sort($finalIds, SORT_NUMERIC);
|
||||
|
||||
return $finalIds;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -78,13 +78,13 @@ class Updatesql
|
||||
{
|
||||
$newItem = true;
|
||||
|
||||
// check if this is an id to ignore
|
||||
// check if this is an id/guid to ignore
|
||||
if (ArrayHelper::check($ignore)
|
||||
&& in_array(
|
||||
$item, $ignore
|
||||
))
|
||||
{
|
||||
// don't add ignored ids
|
||||
// don't add ignored ids/guids
|
||||
$newItem = false;
|
||||
}
|
||||
// check if this is old repeatable field
|
||||
@@ -142,11 +142,11 @@ class Updatesql
|
||||
// search to see if this is a new value
|
||||
$newItem = true;
|
||||
|
||||
// check if this is an id to ignore
|
||||
// check if this is an id/guid to ignore
|
||||
if (ArrayHelper::check($ignore)
|
||||
&& in_array($item[$type], $ignore))
|
||||
{
|
||||
// don't add ignored ids
|
||||
// don't add ignored ids/guids
|
||||
$newItem = false;
|
||||
}
|
||||
// check if this is old repeatable field
|
||||
|
@@ -25,7 +25,7 @@ use VDM\Joomla\Componentbuilder\Compiler\Config;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Placeholder;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Customcode;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Customcode\Gui;
|
||||
use VDM\Joomla\Componentbuilder\Power\Remote\Get as Superpower;
|
||||
use VDM\Joomla\Componentbuilder\Remote\Get as Superpower;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Interfaces\PowerInterface;
|
||||
|
||||
|
||||
|
@@ -773,9 +773,11 @@ class Creator implements ServiceProviderInterface
|
||||
$container->get('Field.Name'),
|
||||
$container->get('Field.Type.Name'),
|
||||
$container->get('Field.Attributes'),
|
||||
$container->get('Field.ModalSelect'),
|
||||
$container->get('Utilities.Xml'),
|
||||
$container->get('Compiler.Creator.Custom.Field.Type.File'),
|
||||
$container->get('Utilities.Counter')
|
||||
$container->get('Utilities.Counter'),
|
||||
$container->get('Compiler.Builder.Component.Fields')
|
||||
);
|
||||
}
|
||||
|
||||
@@ -797,8 +799,10 @@ class Creator implements ServiceProviderInterface
|
||||
$container->get('Field.Name'),
|
||||
$container->get('Field.Type.Name'),
|
||||
$container->get('Field.Attributes'),
|
||||
$container->get('Field.ModalSelect'),
|
||||
$container->get('Compiler.Creator.Custom.Field.Type.File'),
|
||||
$container->get('Utilities.Counter')
|
||||
$container->get('Utilities.Counter'),
|
||||
$container->get('Compiler.Builder.Component.Fields')
|
||||
);
|
||||
}
|
||||
|
||||
@@ -816,6 +820,7 @@ class Creator implements ServiceProviderInterface
|
||||
$container->get('Field.Name'),
|
||||
$container->get('Field.Type.Name'),
|
||||
$container->get('Field.Attributes'),
|
||||
$container->get('Field.ModalSelect'),
|
||||
$container->get('Field.Groups'),
|
||||
$container->get('Compiler.Builder.Field.Names'),
|
||||
$container->get('Compiler.Creator.Field.Type'),
|
||||
|
@@ -20,6 +20,7 @@ use VDM\Joomla\Componentbuilder\Compiler\Field\Data;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Field\Groups;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Field\Attributes;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Field\Name;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Field\ModalSelect;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Field\TypeName;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Field\UniqueName;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Field\Rule;
|
||||
@@ -83,6 +84,9 @@ class Field implements ServiceProviderInterface
|
||||
$container->alias(Name::class, 'Field.Name')
|
||||
->share('Field.Name', [$this, 'getName'], true);
|
||||
|
||||
$container->alias(ModalSelect::class, 'Field.ModalSelect')
|
||||
->share('Field.ModalSelect', [$this, 'getModalSelect'], true);
|
||||
|
||||
$container->alias(TypeName::class, 'Field.Type.Name')
|
||||
->share('Field.Type.Name', [$this, 'getTypeName'], true);
|
||||
|
||||
@@ -214,6 +218,22 @@ class Field implements ServiceProviderInterface
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get The ModalSelect Class.
|
||||
*
|
||||
* @param Container $container The DI container.
|
||||
*
|
||||
* @return ModalSelect
|
||||
* @since 5.2.1
|
||||
*/
|
||||
public function getModalSelect(Container $container): ModalSelect
|
||||
{
|
||||
return new ModalSelect(
|
||||
$container->get('Utilities.Structure'),
|
||||
$container->get('Compiler.Builder.Content.Multi')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get The TypeName Class.
|
||||
*
|
||||
|
@@ -16,7 +16,8 @@ use Joomla\DI\Container;
|
||||
use Joomla\DI\ServiceProviderInterface;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\JoomlaPower as Powers;
|
||||
use VDM\Joomla\Componentbuilder\JoomlaPower\Grep;
|
||||
use VDM\Joomla\Componentbuilder\JoomlaPower\Remote\Get;
|
||||
use VDM\Joomla\Componentbuilder\JoomlaPower\Remote\Config;
|
||||
use VDM\Joomla\Componentbuilder\Remote\Get;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\JoomlaPower\Extractor;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\JoomlaPower\Injector;
|
||||
|
||||
@@ -41,6 +42,9 @@ class JoomlaPower implements ServiceProviderInterface
|
||||
$container->alias(Powers::class, 'Joomla.Power')
|
||||
->share('Joomla.Power', [$this, 'getPowers'], true);
|
||||
|
||||
$container->alias(Config::class, 'Joomla.Power.Remote.Config')
|
||||
->share('Joomla.Power.Remote.Config', [$this, 'getRemoteConfig'], true);
|
||||
|
||||
$container->alias(Get::class, 'Joomla.Power.Remote.Get')
|
||||
->share('Joomla.Power.Remote.Get', [$this, 'getRemoteGet'], true);
|
||||
|
||||
@@ -73,6 +77,21 @@ class JoomlaPower implements ServiceProviderInterface
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get The Remote Config Class.
|
||||
*
|
||||
* @param Container $container The DI container.
|
||||
*
|
||||
* @return Config
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function getRemoteConfig(Container $container): Config
|
||||
{
|
||||
return new Config(
|
||||
$container->get('Power.Table')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Remote Get
|
||||
*
|
||||
@@ -84,8 +103,11 @@ class JoomlaPower implements ServiceProviderInterface
|
||||
public function getRemoteGet(Container $container): Get
|
||||
{
|
||||
return new Get(
|
||||
$container->get('Joomla.Power.Remote.Config'),
|
||||
$container->get('Joomla.Power.Grep'),
|
||||
$container->get('Data.Item')
|
||||
$container->get('Data.Item'),
|
||||
$container->get('Power.Tracker'),
|
||||
$container->get('Power.Message')
|
||||
);
|
||||
}
|
||||
|
||||
@@ -100,8 +122,10 @@ class JoomlaPower implements ServiceProviderInterface
|
||||
public function getGrep(Container $container): Grep
|
||||
{
|
||||
return new Grep(
|
||||
$container->get('Gitea.Repository.Contents'),
|
||||
$container->get('Joomla.Power.Remote.Config'),
|
||||
$container->get('Git.Repository.Contents'),
|
||||
$container->get('Network.Resolve'),
|
||||
$container->get('Power.Tracker'),
|
||||
$container->get('Config')->approved_joomla_paths
|
||||
);
|
||||
}
|
||||
|
@@ -15,7 +15,11 @@ namespace VDM\Joomla\Componentbuilder\Compiler\Service;
|
||||
use Joomla\DI\Container;
|
||||
use Joomla\DI\ServiceProviderInterface;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Power as Powers;
|
||||
use VDM\Joomla\Componentbuilder\Power\Remote\Get;
|
||||
use VDM\Joomla\Componentbuilder\Power\Table;
|
||||
use VDM\Joomla\Componentbuilder\Package\Dependency\Tracker;
|
||||
use VDM\Joomla\Componentbuilder\Package\MessageBus;
|
||||
use VDM\Joomla\Componentbuilder\Power\Remote\Config;
|
||||
use VDM\Joomla\Componentbuilder\Remote\Get;
|
||||
use VDM\Joomla\Componentbuilder\Power\Grep;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Power\Autoloader;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Power\Infusion;
|
||||
@@ -48,6 +52,18 @@ class Power implements ServiceProviderInterface
|
||||
$container->alias(Powers::class, 'Power')
|
||||
->share('Power', [$this, 'getPowers'], true);
|
||||
|
||||
$container->alias(Table::class, 'Power.Table')
|
||||
->share('Power.Table', [$this, 'getPowerTable'], true);
|
||||
|
||||
$container->alias(Tracker::class, 'Power.Tracker')
|
||||
->share('Power.Tracker', [$this, 'getPowerTracker'], true);
|
||||
|
||||
$container->alias(MessageBus::class, 'Power.Message')
|
||||
->share('Power.Message', [$this, 'getMessageBus'], true);
|
||||
|
||||
$container->alias(Config::class, 'Power.Remote.Config')
|
||||
->share('Power.Remote.Config', [$this, 'getRemoteConfig'], true);
|
||||
|
||||
$container->alias(Get::class, 'Power.Remote.Get')
|
||||
->share('Power.Remote.Get', [$this, 'getRemoteGet'], true);
|
||||
|
||||
@@ -101,6 +117,60 @@ class Power implements ServiceProviderInterface
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get The Power Table Class.
|
||||
*
|
||||
* @param Container $container The DI container.
|
||||
*
|
||||
* @return Table
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function getPowerTable(Container $container): Table
|
||||
{
|
||||
return new Table();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get The Tracker Class.
|
||||
*
|
||||
* @param Container $container The DI container.
|
||||
*
|
||||
* @return Tracker
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function getPowerTracker(Container $container): Tracker
|
||||
{
|
||||
return new Tracker();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get The Message Bus Class.
|
||||
*
|
||||
* @param Container $container The DI container.
|
||||
*
|
||||
* @return MessageBus
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function getMessageBus(Container $container): MessageBus
|
||||
{
|
||||
return new MessageBus();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get The Remote Config Class.
|
||||
*
|
||||
* @param Container $container The DI container.
|
||||
*
|
||||
* @return Config
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function getRemoteConfig(Container $container): Config
|
||||
{
|
||||
return new Config(
|
||||
$container->get('Power.Table')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get The Remote Get Class.
|
||||
*
|
||||
@@ -112,8 +182,11 @@ class Power implements ServiceProviderInterface
|
||||
public function getRemoteGet(Container $container): Get
|
||||
{
|
||||
return new Get(
|
||||
$container->get('Power.Remote.Config'),
|
||||
$container->get('Power.Grep'),
|
||||
$container->get('Data.Item')
|
||||
$container->get('Data.Item'),
|
||||
$container->get('Power.Tracker'),
|
||||
$container->get('Power.Message')
|
||||
);
|
||||
}
|
||||
|
||||
@@ -128,8 +201,10 @@ class Power implements ServiceProviderInterface
|
||||
public function getGrep(Container $container): Grep
|
||||
{
|
||||
return new Grep(
|
||||
$container->get('Gitea.Repository.Contents'),
|
||||
$container->get('Power.Remote.Config'),
|
||||
$container->get('Git.Repository.Contents'),
|
||||
$container->get('Network.Resolve'),
|
||||
$container->get('Power.Tracker'),
|
||||
$container->get('Config')->approved_paths,
|
||||
$container->get('Config')->local_powers_repository_path
|
||||
);
|
||||
|
@@ -12,7 +12,7 @@
|
||||
namespace VDM\Joomla\Componentbuilder\Compiler\Utilities;
|
||||
|
||||
|
||||
use Joomla\CMS\Filesystem\File as JoomlaFile;
|
||||
use Joomla\Filesystem\File as JoomlaFile;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Factory as Compiler;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Utilities\Counter;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Utilities\Paths;
|
||||
|
@@ -12,8 +12,8 @@
|
||||
namespace VDM\Joomla\Componentbuilder\Compiler\Utilities;
|
||||
|
||||
|
||||
use Joomla\CMS\Filesystem\Folder as JoomlaFolder;
|
||||
use Joomla\CMS\Filesystem\File as JoomlaFile;
|
||||
use Joomla\Filesystem\Folder as JoomlaFolder;
|
||||
use Joomla\Filesystem\File as JoomlaFile;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Factory as Compiler;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Utilities\Counter;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Utilities\File;
|
||||
@@ -69,7 +69,7 @@ class Folder
|
||||
public function create(string $path, bool $addHtml = true)
|
||||
{
|
||||
// check if the path exist
|
||||
if (!JoomlaFolder::exists($path))
|
||||
if (!is_dir($path))
|
||||
{
|
||||
// create the path
|
||||
JoomlaFolder::create(
|
||||
@@ -100,7 +100,7 @@ class Folder
|
||||
*/
|
||||
public function remove(string $path, ?array $ignore = null): bool
|
||||
{
|
||||
if (!JoomlaFolder::exists($path))
|
||||
if (!is_dir($path))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@@ -15,8 +15,8 @@ namespace VDM\Joomla\Componentbuilder\Compiler\Utilities;
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Application\CMSApplication;
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\CMS\Filesystem\File as JoomlaFile;
|
||||
use Joomla\CMS\Filesystem\Folder;
|
||||
use Joomla\Filesystem\File as JoomlaFile;
|
||||
use Joomla\Filesystem\Folder;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Placeholder;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Interfaces\Component\SettingsInterface as Settings;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Utilities\Paths;
|
||||
@@ -208,7 +208,7 @@ class Structure
|
||||
}
|
||||
|
||||
// setup the folder
|
||||
if (!Folder::exists($path))
|
||||
if (!is_dir($path))
|
||||
{
|
||||
Folder::create($path);
|
||||
$this->file->html($zip_path);
|
||||
@@ -219,7 +219,7 @@ class Structure
|
||||
|
||||
$new_name = $this->getNewName($details, $item, $name, $fileName);
|
||||
|
||||
if (!JoomlaFile::exists($path . '/' . $new_name))
|
||||
if (!is_file($path . '/' . $new_name))
|
||||
{
|
||||
// move the file to its place
|
||||
JoomlaFile::copy(
|
||||
|
@@ -36,7 +36,7 @@ final class Guid
|
||||
'linkedTable' => 'fieldtype',
|
||||
'linkedColumn' => 'id',
|
||||
'array' => false,
|
||||
'valueType' => 1,
|
||||
'valueType' => 5,
|
||||
],
|
||||
[
|
||||
'table' => 'dynamic_get',
|
||||
@@ -455,6 +455,14 @@ final class Guid
|
||||
'array' => false,
|
||||
'valueType' => 1,
|
||||
],
|
||||
[
|
||||
'table' => 'joomla_component',
|
||||
'column' => 'dashboard',
|
||||
'linkedTables' => ['A' => 'admin_view', 'C' => 'custom_admin_view'],
|
||||
'linkedColumn' => 'id',
|
||||
'array' => false,
|
||||
'valueType' => 4
|
||||
],
|
||||
[
|
||||
'table' => 'joomla_module',
|
||||
'column' => 'libraries',
|
||||
@@ -656,6 +664,22 @@ final class Guid
|
||||
'array' => false,
|
||||
'valueType' => 1,
|
||||
],
|
||||
[
|
||||
'table' => 'class_method',
|
||||
'column' => 'joomla_plugin_group',
|
||||
'linkedTable' => 'class_property',
|
||||
'linkedColumn' => 'id',
|
||||
'array' => false,
|
||||
'valueType' => 1,
|
||||
],
|
||||
[
|
||||
'table' => 'class_property',
|
||||
'column' => 'joomla_plugin_group',
|
||||
'linkedTable' => 'class_property',
|
||||
'linkedColumn' => 'id',
|
||||
'array' => false,
|
||||
'valueType' => 1,
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -704,7 +728,7 @@ final class Guid
|
||||
// try to load the update the tables with the schema class
|
||||
try
|
||||
{
|
||||
$messages = $this->migrator->process($this->config);
|
||||
$messages = $this->migrator->process($this->config, $this);
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
|
@@ -13,10 +13,14 @@ namespace VDM\Joomla\Componentbuilder\Fieldtype;
|
||||
|
||||
|
||||
use Joomla\DI\Container;
|
||||
use VDM\Joomla\Componentbuilder\Fieldtype\Service\Fieldtype as Power;
|
||||
use VDM\Joomla\Componentbuilder\Fieldtype\Service\Fieldtype;
|
||||
use VDM\Joomla\Componentbuilder\Package\Service\Power;
|
||||
use VDM\Joomla\Service\Database;
|
||||
use VDM\Joomla\Service\Model;
|
||||
use VDM\Joomla\Service\Data;
|
||||
use VDM\Joomla\Componentbuilder\Power\Service\Git;
|
||||
use VDM\Joomla\Componentbuilder\Power\Service\Github;
|
||||
use VDM\Joomla\Github\Service\Utilities as GithubUtilities;
|
||||
use VDM\Joomla\Componentbuilder\Service\Gitea;
|
||||
use VDM\Joomla\Componentbuilder\Power\Service\Gitea as GiteaPower;
|
||||
use VDM\Joomla\Gitea\Service\Utilities as GiteaUtilities;
|
||||
@@ -51,10 +55,14 @@ abstract class Factory extends ExtendingFactory implements FactoryInterface
|
||||
protected static function createContainer(): Container
|
||||
{
|
||||
return (new Container())
|
||||
->registerServiceProvider(new Fieldtype())
|
||||
->registerServiceProvider(new Power())
|
||||
->registerServiceProvider(new Database())
|
||||
->registerServiceProvider(new Model())
|
||||
->registerServiceProvider(new Data())
|
||||
->registerServiceProvider(new Git())
|
||||
->registerServiceProvider(new Github())
|
||||
->registerServiceProvider(new GithubUtilities())
|
||||
->registerServiceProvider(new Gitea())
|
||||
->registerServiceProvider(new GiteaPower())
|
||||
->registerServiceProvider(new GiteaUtilities())
|
||||
|
@@ -14,15 +14,15 @@ namespace VDM\Joomla\Componentbuilder\Fieldtype;
|
||||
|
||||
use Joomla\CMS\Language\Text;
|
||||
use VDM\Joomla\Interfaces\GrepInterface;
|
||||
use VDM\Joomla\Abstraction\Grep as ExtendingGrep;
|
||||
use VDM\Joomla\Componentbuilder\Remote\Grep as ExtendingGrep;
|
||||
|
||||
|
||||
/**
|
||||
* Global Resource Empowerment Platform
|
||||
*
|
||||
* The Grep feature will try to find your joomla 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 [algorithm:cascading]
|
||||
* The Grep feature will try to find your power in the repositories
|
||||
* linked to this [area], and if it can't be found there will try the global core
|
||||
* 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 5.0.3
|
||||
@@ -37,105 +37,6 @@ final class Grep extends ExtendingGrep implements GrepInterface
|
||||
**/
|
||||
protected ?string $target = 'joomla-fieldtypes';
|
||||
|
||||
/**
|
||||
* Order of global search
|
||||
*
|
||||
* @var array
|
||||
* @since 5.0.3
|
||||
**/
|
||||
protected array $order = ['remote'];
|
||||
|
||||
/**
|
||||
* Search for a remote item
|
||||
*
|
||||
* @param string $guid The global unique id of the item
|
||||
*
|
||||
* @return object|null
|
||||
* @since 5.0.3
|
||||
*/
|
||||
protected function searchRemote(string $guid): ?object
|
||||
{
|
||||
// check if it exists remotely
|
||||
if (($path = $this->existsRemotely($guid)) !== null)
|
||||
{
|
||||
return $this->getRemote($path, $guid);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a remote joomla power
|
||||
*
|
||||
* @param object $path The repository path details
|
||||
* @param string $guid The global unique id of the power
|
||||
*
|
||||
* @return object|null
|
||||
* @since 5.0.3
|
||||
*/
|
||||
protected function getRemote(object $path, string $guid): ?object
|
||||
{
|
||||
$power = null;
|
||||
if (empty($path->index->{$guid}->path))
|
||||
{
|
||||
return $power;
|
||||
}
|
||||
|
||||
// get the branch name
|
||||
$branch = $this->getBranchName($path);
|
||||
|
||||
// load the base and token if set
|
||||
$this->loadApi($this->contents, $path->base ?? null, $path->token ?? null);
|
||||
|
||||
// get the settings
|
||||
if (($power = $this->loadRemoteFile($path->organisation, $path->repository, $path->index->{$guid}->path . '/item.json', $branch)) !== null &&
|
||||
isset($power->guid))
|
||||
{
|
||||
// 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}->path . '/item.json', $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 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
|
||||
$this->contents->reset_();
|
||||
|
||||
return $power;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set repository messages and errors based on given conditions.
|
||||
*
|
||||
|
@@ -23,49 +23,135 @@ use VDM\Joomla\Interfaces\Readme\ItemInterface;
|
||||
final class Item implements ItemInterface
|
||||
{
|
||||
/**
|
||||
* Get an item readme
|
||||
* Generate a README for a JCB Field Type in Markdown format.
|
||||
*
|
||||
* @param object $item An item details.
|
||||
* This includes the field type name, short and full description, and a table of properties if available.
|
||||
*
|
||||
* @return string
|
||||
* @since 3.2.2
|
||||
* @param object $item The field type definition object.
|
||||
*
|
||||
* @return string The generated README as Markdown.
|
||||
* @since 5.0.3
|
||||
*/
|
||||
public function get(object $item): string
|
||||
{
|
||||
// build readme
|
||||
$readme = ["```
|
||||
██╗ ██████╗ ██████╗ ███╗ ███╗██╗ █████╗ ███████╗██╗███████╗██╗ ██████╗ ████████╗██╗ ██╗██████╗ ███████╗
|
||||
██║██╔═══██╗██╔═══██╗████╗ ████║██║ ██╔══██╗ ██╔════╝██║██╔════╝██║ ██╔══██╗ ╚══██╔══╝╚██╗ ██╔╝██╔══██╗██╔════╝
|
||||
██║██║ ██║██║ ██║██╔████╔██║██║ ███████║ █████╗ ██║█████╗ ██║ ██║ ██║ ██║ ╚████╔╝ ██████╔╝█████╗
|
||||
██ ██║██║ ██║██║ ██║██║╚██╔╝██║██║ ██╔══██║ ██╔══╝ ██║██╔══╝ ██║ ██║ ██║ ██║ ╚██╔╝ ██╔═══╝ ██╔══╝
|
||||
╚█████╔╝╚██████╔╝╚██████╔╝██║ ╚═╝ ██║███████╗██║ ██║ ██║ ██║███████╗███████╗██████╔╝ ██║ ██║ ██║ ███████╗
|
||||
╚════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝
|
||||
```"];
|
||||
// system name
|
||||
$readme[] = "# " . $item->name;
|
||||
$readme = [];
|
||||
|
||||
// Title and system name
|
||||
$readme[] = '### JCB! Field Type';
|
||||
$readme[] = '# ' . ($item->name ?? 'error: missing field type name');
|
||||
$readme[] = '';
|
||||
|
||||
// Short description
|
||||
if (!empty($item->short_description)) {
|
||||
$readme[] = '> ' . trim($item->short_description);
|
||||
$readme[] = '';
|
||||
}
|
||||
|
||||
// Full description
|
||||
if (!empty($item->description))
|
||||
{
|
||||
$readme[] = "\n" . $item->description;
|
||||
$readme[] = trim($item->description);
|
||||
$readme[] = '';
|
||||
}
|
||||
elseif (!empty($item->short_description))
|
||||
|
||||
// Properties table
|
||||
if (!empty($item->properties) && (is_array($item->properties) || is_object($item->properties)))
|
||||
{
|
||||
$readme[] = "\n" . $item->short_description;
|
||||
$readme[] = $this->buildPropertyTable($item->properties);
|
||||
$readme[] = '';
|
||||
}
|
||||
|
||||
$readme[] = "\nThe Joomla! field types within this repository provide an essential mechanism for integrating Joomla-related field type into the Joomla Component Builder (JCB). Each field type is meticulously designed to ensure compatibility and ease of use within the JCB framework, allowing developers to effortlessly incorporate and manage custom fields in their components. By utilizing the reset functionality, users can seamlessly update individual field types to align with the latest versions maintained in our core repository, ensuring that their projects benefit from the most up-to-date features and fixes. Additionally, for those who prefer a more personalized approach, the repository can be forked, enabling developers to maintain and distribute their customized field types independently from the broader JCB community. This level of flexibility underscores the open-source nature of JCB, offering you the freedom to adapt and extend your components according to your specific needs, while still benefiting from a robust, community-driven ecosystem.";
|
||||
// Footer
|
||||
$readme[] = '> Integrate, customize, and update this JCB Fieldtype with ease through JCB\'s flexible ecosystem.';
|
||||
$readme[] = '';
|
||||
|
||||
$readme[] = <<<MD
|
||||
### Used in [Joomla Component Builder](https://www.joomlacomponentbuilder.com) - [Source](https://git.vdm.dev/joomla/Component-Builder) - [Mirror](https://github.com/vdm-io/Joomla-Component-Builder) - [Download](https://git.vdm.dev/joomla/pkg-component-builder/releases)
|
||||
|
||||
// yes you can remove this, but why?
|
||||
$readme[] = "\n---\n```
|
||||
██╗ ██████╗██████╗
|
||||
██║██╔════╝██╔══██╗
|
||||
██║██║ ██████╔╝
|
||||
██ ██║██║ ██╔══██╗
|
||||
╚█████╔╝╚██████╗██████╔╝
|
||||
╚════╝ ╚═════╝╚═════╝
|
||||
```\n> Build with [Joomla Component Builder](https://git.vdm.dev/joomla/Component-Builder)\n\n";
|
||||
---
|
||||
[](https://volunteers.joomla.org/joomlers/1396-llewellyn-van-der-merwe "Join Llewellyn on the Joomla Volunteer Portal: Shaping the Future Together!") [](https://git.vdm.dev/octoleo "--quiet") [](https://git.vdm.dev/Llewellyn "Collaborate and Innovate with Llewellyn on Git: Building a Better Code Future!") [](https://t.me/Joomla_component_builder "Join Llewellyn and the Community on Telegram: Building Joomla Components Together!") [](https://joomla.social/@llewellyn "Connect and Engage with Llewellyn on Joomla Social: Empowering Communities, One Post at a Time!") [](https://x.com/llewellynvdm "Join the Conversation with Llewellyn on X: Where Ideas Take Flight!") [](https://github.com/Llewellynvdm "Build, Innovate, and Thrive with Llewellyn on GitHub: Turning Ideas into Impact!") [](https://www.youtube.com/@OctoYou "Explore, Learn, and Create with Llewellyn on YouTube: Your Gateway to Inspiration!") [](https://n8n.io/creators/octoleo "Effortless Automation and Impactful Workflows with Llewellyn on n8n!") [](https://hub.docker.com/u/llewellyn "Llewellyn on Docker: Containerize Your Creativity!") [](https://opencollective.com/joomla-component-builder "Donate towards JCB: Help Llewellyn financially so he can continue developing this great tool!") [](https://git.vdm.dev/Llewellyn/gpg "Unlock Trust and Security with Llewellyn's GPG Key: Your Gateway to Verified Connections!")
|
||||
MD;
|
||||
|
||||
return implode("\n", $readme);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a Markdown table with details about each form property, including optional PHP code blocks.
|
||||
*
|
||||
* The table includes columns: Property, Example, Adjustable, Mandatory, and Description.
|
||||
* If the 'example' field contains PHP code patterns like variable assignment or object access,
|
||||
* it will be linked to an anchor section with the code shown below the table.
|
||||
* All '|' characters in example and description fields are replaced with '|' to prevent Markdown table breaking.
|
||||
*
|
||||
* @param array|object $properties Associative array of property objects.
|
||||
*
|
||||
* @return string The generated Markdown output.
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected function buildPropertyTable(array|object $properties): string
|
||||
{
|
||||
$properties = (array) $properties;
|
||||
$markdown = [];
|
||||
$codeSnippets = [];
|
||||
|
||||
// Table header
|
||||
$markdown[] = '| Property | Example | Adjustable | Mandatory | Description |';
|
||||
$markdown[] = '|----------|---------|------------|-----------|-------------|';
|
||||
|
||||
foreach ($properties as $prop)
|
||||
{
|
||||
$prop = (array) $prop;
|
||||
|
||||
$name = $prop['name'] ?? '—';
|
||||
$example = trim($prop['example'] ?? '');
|
||||
$description = trim($prop['description'] ?? '');
|
||||
|
||||
// Replace "|" in raw content to prevent Markdown table breakage
|
||||
$name = str_replace('|', '|', $name);
|
||||
$example = str_replace('|', '|', $example);
|
||||
$description = str_replace('|', '|', $description);
|
||||
|
||||
$adjustable = (!empty($prop['adjustable']) && $prop['adjustable'] == '1')
|
||||
? ''
|
||||
: '';
|
||||
|
||||
$mandatory = (!empty($prop['mandatory']) && $prop['mandatory'] == '1')
|
||||
? ''
|
||||
: '';
|
||||
|
||||
// Detect PHP-like code: variable assignment or object access
|
||||
$isCode = preg_match('/\$\w+\s*=|\$\w+->\w+/', $example);
|
||||
|
||||
if ($isCode)
|
||||
{
|
||||
$anchor = 'code-' . preg_replace('/[^a-z0-9]+/i', '-', strtolower($name));
|
||||
$link = "[code](#{$anchor})";
|
||||
$codeSnippets[$anchor] = $prop['example'];
|
||||
$example = $link;
|
||||
}
|
||||
elseif ($example === '')
|
||||
{
|
||||
$example = '—';
|
||||
}
|
||||
|
||||
$markdown[] = "| {$name} | {$example} | {$adjustable} | {$mandatory} | {$description} |";
|
||||
}
|
||||
|
||||
// Join the table
|
||||
$output = implode("\n", $markdown);
|
||||
|
||||
// Add code blocks if any
|
||||
if (!empty($codeSnippets))
|
||||
{
|
||||
$output .= "\n\n";
|
||||
foreach ($codeSnippets as $anchor => $code)
|
||||
{
|
||||
$output .= "### <a id=\"{$anchor}\"></a>Code for `{$anchor}`\n\n";
|
||||
$output .= "```php\n{$code}\n```\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -13,6 +13,7 @@ namespace VDM\Joomla\Componentbuilder\Fieldtype\Readme;
|
||||
|
||||
|
||||
use VDM\Joomla\Interfaces\Readme\MainInterface;
|
||||
use VDM\Joomla\Componentbuilder\Package\Readme\Main as ExtendingMain;
|
||||
|
||||
|
||||
/**
|
||||
@@ -20,215 +21,95 @@ use VDM\Joomla\Interfaces\Readme\MainInterface;
|
||||
*
|
||||
* @since 5.0.3
|
||||
*/
|
||||
final class Main implements MainInterface
|
||||
final class Main extends ExtendingMain implements MainInterface
|
||||
{
|
||||
/**
|
||||
* Get Main Readme
|
||||
* Generate the main README for the JCB Field Types repository in Markdown format.
|
||||
*
|
||||
* @param array $items All items of this repository.
|
||||
* Field Types define reusable templates for Joomla form fields inside JCB. Each field type links directly
|
||||
* to Joomla's native XML field structure and allows easy reuse of predefined properties across multiple fields.
|
||||
*
|
||||
* @return string
|
||||
* @since 3.2.0
|
||||
* @param array $items All field types currently stored in the repository.
|
||||
*
|
||||
* @return string The full generated Markdown README.
|
||||
* @since 3.2.0
|
||||
*/
|
||||
public function get(array $items): string
|
||||
{
|
||||
// build readme
|
||||
$readme = ["```
|
||||
██╗ ██████╗ ██████╗ ███╗ ███╗██╗ █████╗
|
||||
██║██╔═══██╗██╔═══██╗████╗ ████║██║ ██╔══██╗
|
||||
██║██║ ██║██║ ██║██╔████╔██║██║ ███████║
|
||||
██ ██║██║ ██║██║ ██║██║╚██╔╝██║██║ ██╔══██║
|
||||
╚█████╔╝╚██████╔╝╚██████╔╝██║ ╚═╝ ██║███████╗██║ ██║
|
||||
╚════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝
|
||||
$readme = [];
|
||||
|
||||
███████╗██╗███████╗██╗ ██████╗ ████████╗██╗ ██╗██████╗ ███████╗███████╗
|
||||
██╔════╝██║██╔════╝██║ ██╔══██╗ ╚══██╔══╝╚██╗ ██╔╝██╔══██╗██╔════╝██╔════╝
|
||||
█████╗ ██║█████╗ ██║ ██║ ██║ ██║ ╚████╔╝ ██████╔╝█████╗ ███████╗
|
||||
██╔══╝ ██║██╔══╝ ██║ ██║ ██║ ██║ ╚██╔╝ ██╔═══╝ ██╔══╝ ╚════██║
|
||||
██║ ██║███████╗███████╗██████╔╝ ██║ ██║ ██║ ███████╗███████║
|
||||
╚═╝ ╚═╝╚══════╝╚══════╝╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝╚══════╝
|
||||
```"];
|
||||
// Header
|
||||
$readme[] = '# JCB! Field Types';
|
||||
$readme[] = '';
|
||||
|
||||
// default description of super powers
|
||||
$readme[] = "\n### What is JCB Joomla Field Types?\nThe Joomla field types provide a powerful way to map Joomla-related field types, enabling seamless integration with Joomla Component Builder (JCB). This repository serves as a centralized system for maintaining, updating, and distributing these field types throughout the JCB ecosystem.\n
|
||||
\n
|
||||
When you need to update any field type in JCB, simply select the desired field type and click the \"reset\" button. This action will automatically sync the selected field type with its corresponding version hosted in our core repository, ensuring you always have the latest updates.\n
|
||||
\n
|
||||
Moreover, if you wish to tailor the field types to your specific needs, you can fork the repository and point your JCB instance to your fork. This allows you to maintain and update field types independently from the main JCB community, offering the flexibility that is at the heart of open-source philosophy.\n
|
||||
\n
|
||||
We believe this approach empowers you to extend and customize JCB to fit your unique requirements, exemplifying the true spirit of freedom in software development. We trust you will find this capability both useful and aligned with the expectations of how open-source software should function.\n";
|
||||
// What is it?
|
||||
$readme[] = '### What Are JCB Field Types?';
|
||||
$readme[] = <<<MD
|
||||
JCB Field Types act as **blueprints for Joomla form fields**.
|
||||
Each Field Type defines the structure, rules, and properties (such as required, translatable, adjustable) of a Joomla-compatible field.
|
||||
|
||||
// get the readme body
|
||||
$readme[] = $this->readmeBuilder($items);
|
||||
Instead of manually building repetitive XML field definitions, Field Types allow you to define:
|
||||
|
||||
// yes you can remove this, but why?
|
||||
$readme[] = "\n---\n```
|
||||
██╗ ██████╗ ██████╗ ███╗ ███╗██╗ █████╗
|
||||
██║██╔═══██╗██╔═══██╗████╗ ████║██║ ██╔══██╗
|
||||
██║██║ ██║██║ ██║██╔████╔██║██║ ███████║
|
||||
██ ██║██║ ██║██║ ██║██║╚██╔╝██║██║ ██╔══██║
|
||||
╚█████╔╝╚██████╔╝╚██████╔╝██║ ╚═╝ ██║███████╗██║ ██║
|
||||
╚════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝
|
||||
██████╗ ██████╗ ███╗ ███╗██████╗ ██████╗ ███╗ ██╗███████╗███╗ ██╗████████╗
|
||||
██╔════╝██╔═══██╗████╗ ████║██╔══██╗██╔═══██╗████╗ ██║██╔════╝████╗ ██║╚══██╔══╝
|
||||
██║ ██║ ██║██╔████╔██║██████╔╝██║ ██║██╔██╗ ██║█████╗ ██╔██╗ ██║ ██║
|
||||
██║ ██║ ██║██║╚██╔╝██║██╔═══╝ ██║ ██║██║╚██╗██║██╔══╝ ██║╚██╗██║ ██║
|
||||
╚██████╗╚██████╔╝██║ ╚═╝ ██║██║ ╚██████╔╝██║ ╚████║███████╗██║ ╚████║ ██║
|
||||
╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═════╝ ╚═╝ ╚═══╝╚══════╝╚═╝ ╚═══╝ ╚═╝
|
||||
██████╗ ██╗ ██╗██╗██╗ ██████╗ ███████╗██████╗
|
||||
██╔══██╗██║ ██║██║██║ ██╔══██╗██╔════╝██╔══██╗
|
||||
██████╔╝██║ ██║██║██║ ██║ ██║█████╗ ██████╔╝
|
||||
██╔══██╗██║ ██║██║██║ ██║ ██║██╔══╝ ██╔══██╗
|
||||
██████╔╝╚██████╔╝██║███████╗██████╔╝███████╗██║ ██║
|
||||
╚═════╝ ╚═════╝ ╚═╝╚══════╝╚═════╝ ╚══════╝╚═╝ ╚═╝
|
||||
```\n> Build with [Joomla Component Builder](https://git.vdm.dev/joomla/Component-Builder)\n\n";
|
||||
- **Input Types** (`text`, `list`, `radio`, etc.)
|
||||
- **Field Parameters** (`required`, `label`, `description`, etc.)
|
||||
- **Default Values and Validation**
|
||||
- **Custom Logic and Layout Overrides**
|
||||
|
||||
These definitions are stored centrally and reused across all fields built in JCB.
|
||||
|
||||
---
|
||||
MD;
|
||||
|
||||
$readme[] = '### Why Use Field Types?';
|
||||
$readme[] = <<<MD
|
||||
When you create a new Field in JCB, you start by selecting a Field Type.
|
||||
This selection **automatically inherits all the structure and metadata** you defined in the Field Type.
|
||||
|
||||
This templating system:
|
||||
|
||||
- Saves time and reduces duplication
|
||||
- Keeps your form field definitions consistent
|
||||
- Ensures compatibility with Joomla's XML-based field system
|
||||
- Allows global updates to propagate to all child fields
|
||||
|
||||
---
|
||||
MD;
|
||||
|
||||
$readme[] = '### How Do Field Types Integrate with Joomla?';
|
||||
$readme[] = <<<MD
|
||||
Each Field Type generates **conventional Joomla XML**, which Joomla uses to automatically render HTML forms in both admin and site interfaces.
|
||||
The entire workflow is standards-based — meaning what JCB builds, Joomla understands and renders as expected.
|
||||
|
||||
---
|
||||
MD;
|
||||
|
||||
$readme[] = '### Customization & Syncing';
|
||||
$readme[] = <<<MD
|
||||
Field Types are version-controlled and centrally managed.
|
||||
|
||||
- To **update a field type** from this repository in your JCB instance, simply use the `Reset` button inside the JCB GUI of field types.
|
||||
- You can also **fork this repository** and point your JCB to your fork — giving you full control and independence from upstream updates.
|
||||
|
||||
This design promotes **collaborative sharing** while allowing **custom autonomy**.
|
||||
|
||||
---
|
||||
MD;
|
||||
|
||||
$readme[] = '### Index of JCB Field Types';
|
||||
$readme[] = '';
|
||||
|
||||
// Field type listing
|
||||
$readme[] = $this->getIndex($items);
|
||||
$readme[] = '';
|
||||
|
||||
$readme[] = <<<MD
|
||||
### All used in [Joomla Component Builder](https://www.joomlacomponentbuilder.com) - [Source](https://git.vdm.dev/joomla/Component-Builder) - [Mirror](https://github.com/vdm-io/Joomla-Component-Builder) - [Download](https://git.vdm.dev/joomla/pkg-component-builder/releases)
|
||||
|
||||
---
|
||||
[](https://volunteers.joomla.org/joomlers/1396-llewellyn-van-der-merwe "Join Llewellyn on the Joomla Volunteer Portal: Shaping the Future Together!") [](https://git.vdm.dev/octoleo "--quiet") [](https://git.vdm.dev/Llewellyn "Collaborate and Innovate with Llewellyn on Git: Building a Better Code Future!") [](https://t.me/Joomla_component_builder "Join Llewellyn and the Community on Telegram: Building Joomla Components Together!") [](https://joomla.social/@llewellyn "Connect and Engage with Llewellyn on Joomla Social: Empowering Communities, One Post at a Time!") [](https://x.com/llewellynvdm "Join the Conversation with Llewellyn on X: Where Ideas Take Flight!") [](https://github.com/Llewellynvdm "Build, Innovate, and Thrive with Llewellyn on GitHub: Turning Ideas into Impact!") [](https://www.youtube.com/@OctoYou "Explore, Learn, and Create with Llewellyn on YouTube: Your Gateway to Inspiration!") [](https://n8n.io/creators/octoleo "Effortless Automation and Impactful Workflows with Llewellyn on n8n!") [](https://hub.docker.com/u/llewellyn "Llewellyn on Docker: Containerize Your Creativity!") [](https://opencollective.com/joomla-component-builder "Donate towards JCB: Help Llewellyn financially so he can continue developing this great tool!") [](https://git.vdm.dev/Llewellyn/gpg "Unlock Trust and Security with Llewellyn's GPG Key: Your Gateway to Verified Connections!")
|
||||
MD;
|
||||
|
||||
return implode("\n", $readme);
|
||||
}
|
||||
|
||||
/**
|
||||
* The readme builder
|
||||
*
|
||||
* @param array $classes The powers.
|
||||
*
|
||||
* @return string
|
||||
* @since 3.2.0
|
||||
*/
|
||||
private function readmeBuilder(array &$items): string
|
||||
{
|
||||
$classes = [];
|
||||
foreach ($items as $guid => $power)
|
||||
{
|
||||
// add to the sort bucket
|
||||
$classes[] = [
|
||||
'name' => $power['name'],
|
||||
'link' => $this->indexLinkPower($power)
|
||||
];
|
||||
}
|
||||
|
||||
return $this->readmeModel($classes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort and model the readme classes
|
||||
*
|
||||
* @param array $classes The powers.
|
||||
*
|
||||
* @return string
|
||||
* @since 3.2.0
|
||||
*/
|
||||
private function readmeModel(array &$classes): string
|
||||
{
|
||||
$this->sortClasses($classes);
|
||||
|
||||
return $this->generateIndex($classes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the index string for classes
|
||||
*
|
||||
* @param array $classes The sorted classes
|
||||
*
|
||||
* @return string The index string
|
||||
*/
|
||||
private function generateIndex(array &$classes): string
|
||||
{
|
||||
$result = "# Index of Joomla! Field Types\n";
|
||||
|
||||
foreach ($classes as $class)
|
||||
{
|
||||
// Add the class details
|
||||
$result .= "\n - " . $class['link'];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort the flattened array using a single sorting function
|
||||
*
|
||||
* @param array $classes The classes to sort
|
||||
*
|
||||
* @since 3.2.0
|
||||
*/
|
||||
private function sortClasses(array &$classes): void
|
||||
{
|
||||
usort($classes, function ($a, $b) {
|
||||
return $this->compareName($a, $b);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare the name of two classes
|
||||
*
|
||||
* @param array $a First class
|
||||
* @param array $b Second class
|
||||
*
|
||||
* @return int Comparison result
|
||||
* @since 3.2.0
|
||||
*/
|
||||
private function compareName(array $a, array $b): int
|
||||
{
|
||||
return strcmp($a['name'], $b['name']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the Link to the power in this repository
|
||||
*
|
||||
* @param array $power The power details.
|
||||
*
|
||||
* @return string
|
||||
* @since 3.2.0
|
||||
*/
|
||||
private function indexLinkPower(array &$power): string
|
||||
{
|
||||
$name = $power['name'] ?? 'error';
|
||||
return '**' . $name . "** | "
|
||||
. $this->linkPowerRepo($power) . ' | '
|
||||
. $this->linkPowerSettings($power) . ' | '
|
||||
. $this->linkPowerDesc($power);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the Link to the power in this repository
|
||||
*
|
||||
* @param array $power The power details.
|
||||
*
|
||||
* @return string
|
||||
* @since 3.2.0
|
||||
*/
|
||||
private function linkPowerRepo(array &$power): string
|
||||
{
|
||||
$path = $power['path'] ?? 'error';
|
||||
return '[Details](' . $path . ')';
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the Link to the power settings in this repository
|
||||
*
|
||||
* @param array $power The power details.
|
||||
*
|
||||
* @return string
|
||||
* @since 3.2.0
|
||||
*/
|
||||
private function linkPowerSettings(array &$power): string
|
||||
{
|
||||
$settings = $power['settings'] ?? 'error';
|
||||
return '[Settings](' . $settings . ')';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the short description
|
||||
*
|
||||
* @param array $power The power details.
|
||||
*
|
||||
* @return string
|
||||
* @since 3.2.0
|
||||
*/
|
||||
private function linkPowerDesc(array &$power): string
|
||||
{
|
||||
$jpk = $power['desc'] ?? '';
|
||||
return $jpk;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,102 @@
|
||||
<?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\Fieldtype\Remote;
|
||||
|
||||
|
||||
use VDM\Joomla\Interfaces\Remote\ConfigInterface;
|
||||
use VDM\Joomla\Abstraction\Remote\Config as ExtendingConfig;
|
||||
|
||||
|
||||
/**
|
||||
* Base Configure values for the remote classes
|
||||
*
|
||||
* @since 5.1.1
|
||||
*/
|
||||
final class Config extends ExtendingConfig implements ConfigInterface
|
||||
{
|
||||
/**
|
||||
* Table Name
|
||||
*
|
||||
* @var string
|
||||
* @since 5.0.3
|
||||
*/
|
||||
protected string $table = 'fieldtype';
|
||||
|
||||
/**
|
||||
* Area Name
|
||||
*
|
||||
* @var string|null
|
||||
* @since 5.0.3
|
||||
*/
|
||||
protected ?string $area = 'Field Type';
|
||||
|
||||
/**
|
||||
* Prefix Key
|
||||
*
|
||||
* @var string
|
||||
* @since 3.2.2
|
||||
*/
|
||||
protected string $prefix_key = '';
|
||||
|
||||
/**
|
||||
* Suffix Key
|
||||
*
|
||||
* @var string
|
||||
* @since 3.2.2
|
||||
*/
|
||||
protected string $suffix_key = '';
|
||||
|
||||
/**
|
||||
* The ignore fields
|
||||
*
|
||||
* @var array
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected array $ignore = ['catid', 'access'];
|
||||
|
||||
/**
|
||||
* The index map
|
||||
* must always have: [name,path,settings,guid]
|
||||
* you can add more
|
||||
*
|
||||
* @var array
|
||||
* @since 5.0.3
|
||||
*/
|
||||
protected array $index_map = [
|
||||
'name' => 'index_map_IndexName',
|
||||
'path' => 'index_map_IndexPath',
|
||||
'settings' => 'index_map_IndexSettingsPath',
|
||||
'guid' => 'index_map_IndexGUID',
|
||||
'desc' => 'index_map_ShortDescription'
|
||||
];
|
||||
|
||||
/**
|
||||
* The index header
|
||||
* mapping the index map to a table
|
||||
* must always have: [name,path,guid,local]
|
||||
* with [name] always first
|
||||
* with [path,guid,local] always last
|
||||
* you can add more in between
|
||||
*
|
||||
* @var array
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected array $index_header = [
|
||||
'name',
|
||||
'desc',
|
||||
'path',
|
||||
'settings',
|
||||
'guid',
|
||||
'local'
|
||||
];
|
||||
}
|
||||
|
@@ -12,6 +12,7 @@
|
||||
namespace VDM\Joomla\Componentbuilder\Fieldtype\Remote;
|
||||
|
||||
|
||||
use Joomla\CMS\Language\Text;
|
||||
use VDM\Joomla\Interfaces\Remote\SetInterface;
|
||||
use VDM\Joomla\Abstraction\Remote\Set as ExtendingSet;
|
||||
|
||||
@@ -23,67 +24,6 @@ use VDM\Joomla\Abstraction\Remote\Set as ExtendingSet;
|
||||
*/
|
||||
final class Set extends ExtendingSet implements SetInterface
|
||||
{
|
||||
/**
|
||||
* Table Name
|
||||
*
|
||||
* @var string
|
||||
* @since 5.0.3
|
||||
*/
|
||||
protected string $table = 'fieldtype';
|
||||
|
||||
/**
|
||||
* Area Name
|
||||
*
|
||||
* @var string
|
||||
* @since 5.0.3
|
||||
*/
|
||||
protected string $area = 'Joomla Field Type';
|
||||
|
||||
/**
|
||||
* Prefix Key
|
||||
*
|
||||
* @var string
|
||||
* @since 5.0.3
|
||||
*/
|
||||
protected string $prefix_key = '';
|
||||
|
||||
/**
|
||||
* The item map
|
||||
*
|
||||
* @var array
|
||||
* @since 5.0.3
|
||||
*/
|
||||
protected array $map = [
|
||||
'name' => 'name',
|
||||
'short_description' => 'short_description',
|
||||
'description' => 'description',
|
||||
'properties' => 'properties',
|
||||
'has_defaults' => 'has_defaults',
|
||||
'datatype' => 'datatype',
|
||||
'datalenght' => 'datalenght',
|
||||
'datalenght_other' => 'datalenght_other',
|
||||
'datadefault' => 'datadefault',
|
||||
'datadefault_other' => 'datadefault_other',
|
||||
'indexes' => 'indexes',
|
||||
'null_switch' => 'null_switch',
|
||||
'store' => 'store',
|
||||
'guid' => 'guid'
|
||||
];
|
||||
|
||||
/**
|
||||
* The index map
|
||||
*
|
||||
* @var array
|
||||
* @since 5.0.3
|
||||
*/
|
||||
protected array $index_map = [
|
||||
'name' => 'index_map_IndexName',
|
||||
'desc' => 'index_map_ShortDescription',
|
||||
'settings' => 'index_map_IndexSettingsPath',
|
||||
'path' => 'index_map_IndexPath',
|
||||
'guid' => 'index_map_IndexGUID'
|
||||
];
|
||||
|
||||
/**
|
||||
* update an existing item (if changed)
|
||||
*
|
||||
@@ -96,25 +36,38 @@ final class Set extends ExtendingSet implements SetInterface
|
||||
*/
|
||||
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);
|
||||
$area = $this->getArea();
|
||||
$item_name = $this->index_map_IndexName($item);
|
||||
$repo_name = $this->getRepoName($repo);
|
||||
|
||||
if ($sha === null || $this->areObjectsEqual($item, $existing))
|
||||
{
|
||||
$this->messages->add('warning', Text::sprintf('COM_COMPONENTBUILDER_S_ITEM_S_DETAILS_IN_REPOS_DID_NOT_CHANGE_SO_NO_UPDATE_WAS_MADE', $area, $item_name, $repo_name));
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->git->update(
|
||||
$result = $this->git->update(
|
||||
$repo->organisation, // The owner name.
|
||||
$repo->repository, // The repository name.
|
||||
'src/' . $item->guid . '/' . $this->getSettingsPath(), // The file path.
|
||||
$this->index_map_IndexSettingsPath($item), // The file path.
|
||||
json_encode($item, JSON_PRETTY_PRINT), // The file content.
|
||||
'Update ' . $item->name, // The commit message.
|
||||
$sha, // The blob SHA of the old file.
|
||||
$repo->write_branch // The branch name.
|
||||
$repo->write_branch, // The branch name.
|
||||
$repo->author_name, // The author name.
|
||||
$repo->author_email // The author email.
|
||||
);
|
||||
|
||||
return true;
|
||||
$success = is_object($result);
|
||||
|
||||
if (!$success)
|
||||
{
|
||||
$this->messages->add('warning', Text::sprintf('COM_COMPONENTBUILDER_S_ITEM_S_DETAILS_IN_REPOS_FAILED_TO_UPDATE', $area, $item_name, $repo_name));
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -123,19 +76,23 @@ final class Set extends ExtendingSet implements SetInterface
|
||||
* @param object $item
|
||||
* @param object $repo
|
||||
*
|
||||
* @return void
|
||||
* @return bool
|
||||
* @since 5.0.3
|
||||
*/
|
||||
protected function createItem(object $item, object $repo): void
|
||||
protected function createItem(object $item, object $repo): bool
|
||||
{
|
||||
$this->git->create(
|
||||
$result = $this->git->create(
|
||||
$repo->organisation, // The owner name.
|
||||
$repo->repository, // The repository name.
|
||||
'src/' . $item->guid . '/' . $this->getSettingsPath(), // The file path.
|
||||
$this->index_map_IndexSettingsPath($item), // The file path.
|
||||
json_encode($item, JSON_PRETTY_PRINT), // The file content.
|
||||
'Create ' . $item->name, // The commit message.
|
||||
$repo->write_branch // The branch name.
|
||||
$repo->write_branch, // The branch name.
|
||||
$repo->author_name, // The author name.
|
||||
$repo->author_email // The author email.
|
||||
);
|
||||
|
||||
return is_object($result);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -160,11 +117,13 @@ final class Set extends ExtendingSet implements SetInterface
|
||||
$this->git->update(
|
||||
$repo->organisation, // The owner name.
|
||||
$repo->repository, // The repository name.
|
||||
'src/' . $item->guid . '/README.md', // The file path.
|
||||
$this->index_map_IndexReadmePath($item), // The file path.
|
||||
$this->itemReadme->get($item), // The file content.
|
||||
'Update ' . $item->name . ' readme file', // The commit message.
|
||||
'Update ' . ($this->index_map_IndexName($item) ?? 'fieldtype') . ' readme file', // The commit message.
|
||||
$sha, // The blob SHA of the old file.
|
||||
$repo->write_branch // The branch name.
|
||||
$repo->write_branch, // The branch name.
|
||||
$repo->author_name, // The author name.
|
||||
$repo->author_email // The author email.
|
||||
);
|
||||
}
|
||||
|
||||
@@ -182,20 +141,24 @@ final class Set extends ExtendingSet implements SetInterface
|
||||
$this->git->create(
|
||||
$repo->organisation, // The owner name.
|
||||
$repo->repository, // The repository name.
|
||||
'src/' . $item->guid . '/README.md', // The file path.
|
||||
$this->index_map_IndexReadmePath($item), // The file path.
|
||||
$this->itemReadme->get($item), // The file content.
|
||||
'Create ' . $item->name . ' readme file', // The commit message.
|
||||
$repo->write_branch // The branch name.
|
||||
'Create ' . ($this->index_map_IndexName($item) ?? 'fieldtype') . ' readme file', // The commit message.
|
||||
$repo->write_branch, // The branch name.
|
||||
$repo->author_name, // The author name.
|
||||
$repo->author_email // The author email.
|
||||
);
|
||||
}
|
||||
|
||||
//// index_map_ (area) /////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Get the item name for the index values
|
||||
*
|
||||
* @param object $item
|
||||
*
|
||||
* @return string|null
|
||||
* @since 5.0.3
|
||||
* @since 3.2.2
|
||||
*/
|
||||
protected function index_map_IndexName(object $item): ?string
|
||||
{
|
||||
@@ -205,14 +168,18 @@ final class Set extends ExtendingSet implements SetInterface
|
||||
/**
|
||||
* Get the item Short Description for the index values
|
||||
*
|
||||
* @param object $item
|
||||
* @param object $item The item object to extract description from.
|
||||
*
|
||||
* @return string|null
|
||||
* @return string|null The trimmed short or fallback description, or null if both are empty.
|
||||
* @since 5.0.3
|
||||
*/
|
||||
protected function index_map_ShortDescription(object $item): ?string
|
||||
{
|
||||
return $item->short_description ?? null;
|
||||
$description = $item->short_description ?? $item->description ?? '';
|
||||
|
||||
$description = trim($description);
|
||||
|
||||
return $description !== '' ? $description : null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -15,9 +15,9 @@ namespace VDM\Joomla\Componentbuilder\Fieldtype\Service;
|
||||
use Joomla\DI\Container;
|
||||
use Joomla\DI\ServiceProviderInterface;
|
||||
use VDM\Joomla\Componentbuilder\Fieldtype\Config;
|
||||
use VDM\Joomla\Componentbuilder\Table;
|
||||
use VDM\Joomla\Componentbuilder\Fieldtype\Grep;
|
||||
use VDM\Joomla\Componentbuilder\Fieldtype\Remote\Get;
|
||||
use VDM\Joomla\Componentbuilder\Fieldtype\Remote\Config as RemoteConfig;
|
||||
use VDM\Joomla\Componentbuilder\Remote\Get;
|
||||
use VDM\Joomla\Componentbuilder\Fieldtype\Remote\Set;
|
||||
use VDM\Joomla\Componentbuilder\Fieldtype\Readme\Item as ItemReadme;
|
||||
use VDM\Joomla\Componentbuilder\Fieldtype\Readme\Main as MainReadme;
|
||||
@@ -40,15 +40,15 @@ class Fieldtype implements ServiceProviderInterface
|
||||
*/
|
||||
public function register(Container $container)
|
||||
{
|
||||
$container->alias(Config::class, 'Config')
|
||||
->share('Config', [$this, 'getConfig'], true);
|
||||
|
||||
$container->alias(Table::class, 'Table')
|
||||
->share('Table', [$this, 'getTable'], true);
|
||||
$container->alias(Config::class, 'Joomla.Fieldtype.Config')->alias('Config', 'Joomla.Fieldtype.Config')
|
||||
->share('Joomla.Fieldtype.Config', [$this, 'getConfig'], true);
|
||||
|
||||
$container->alias(Grep::class, 'Joomla.Fieldtype.Grep')
|
||||
->share('Joomla.Fieldtype.Grep', [$this, 'getGrep'], true);
|
||||
|
||||
$container->alias(RemoteConfig::class, 'Joomla.Fieldtype.Remote.Config')
|
||||
->share('Joomla.Fieldtype.Remote.Config', [$this, 'getRemoteConfig'], true);
|
||||
|
||||
$container->alias(Get::class, 'Joomla.Fieldtype.Remote.Get')
|
||||
->share('Joomla.Fieldtype.Remote.Get', [$this, 'getRemoteGet'], true);
|
||||
|
||||
@@ -76,18 +76,31 @@ class Fieldtype implements ServiceProviderInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Get The Table Class.
|
||||
* Get The Power Table Class.
|
||||
*
|
||||
* @param Container $container The DI container.
|
||||
*
|
||||
* @return Table
|
||||
* @since 3.2.1
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function getTable(Container $container): Table
|
||||
public function getPowerTable(Container $container): Table
|
||||
{
|
||||
return new Table();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get The Message Bus Class.
|
||||
*
|
||||
* @param Container $container The DI container.
|
||||
*
|
||||
* @return MessageBus
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function getMessageBus(Container $container): MessageBus
|
||||
{
|
||||
return new MessageBus();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get The Grep Class.
|
||||
*
|
||||
@@ -99,9 +112,26 @@ class Fieldtype implements ServiceProviderInterface
|
||||
public function getGrep(Container $container): Grep
|
||||
{
|
||||
return new Grep(
|
||||
$container->get('Gitea.Repository.Contents'),
|
||||
$container->get('Joomla.Fieldtype.Remote.Config'),
|
||||
$container->get('Git.Repository.Contents'),
|
||||
$container->get('Network.Resolve'),
|
||||
$container->get('Config')->approved_joomla_paths
|
||||
$container->get('Power.Tracker'),
|
||||
$container->get('Joomla.Fieldtype.Config')->approved_joomla_paths
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get The Remote Config Class.
|
||||
*
|
||||
* @param Container $container The DI container.
|
||||
*
|
||||
* @return RemoteConfig
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function getRemoteConfig(Container $container): RemoteConfig
|
||||
{
|
||||
return new RemoteConfig(
|
||||
$container->get('Power.Table')
|
||||
);
|
||||
}
|
||||
|
||||
@@ -116,8 +146,11 @@ class Fieldtype implements ServiceProviderInterface
|
||||
public function getRemoteGet(Container $container): Get
|
||||
{
|
||||
return new Get(
|
||||
$container->get('Joomla.Fieldtype.Remote.Config'),
|
||||
$container->get('Joomla.Fieldtype.Grep'),
|
||||
$container->get('Data.Item')
|
||||
$container->get('Data.Item'),
|
||||
$container->get('Power.Tracker'),
|
||||
$container->get('Power.Message')
|
||||
);
|
||||
}
|
||||
|
||||
@@ -132,12 +165,15 @@ class Fieldtype implements ServiceProviderInterface
|
||||
public function getRemoteSet(Container $container): Set
|
||||
{
|
||||
return new Set(
|
||||
$container->get('Config')->approved_joomla_paths,
|
||||
$container->get('Joomla.Fieldtype.Remote.Config'),
|
||||
$container->get('Joomla.Fieldtype.Grep'),
|
||||
$container->get('Data.Items'),
|
||||
$container->get('Joomla.Fieldtype.Readme.Item'),
|
||||
$container->get('Joomla.Fieldtype.Readme.Main'),
|
||||
$container->get('Gitea.Repository.Contents')
|
||||
$container->get('Git.Repository.Contents'),
|
||||
$container->get('Power.Tracker'),
|
||||
$container->get('Power.Message'),
|
||||
$container->get('Joomla.Fieldtype.Config')->approved_joomla_paths
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Joomla.Component.Builder
|
||||
*
|
||||
* @created 4th September, 2020
|
||||
* @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\File;
|
||||
|
||||
|
||||
use Joomla\CMS\Image\Image as JoomlaImage;
|
||||
use Joomla\CMS\Log\Log;
|
||||
use Joomla\Filesystem\File;
|
||||
use Joomla\Filesystem\Folder;
|
||||
use VDM\Joomla\Utilities\MimeHelper;
|
||||
|
||||
|
||||
/**
|
||||
* Image Class
|
||||
*
|
||||
* @since 5.1.1
|
||||
*/
|
||||
final class Image
|
||||
{
|
||||
/**
|
||||
* Process one image into multiple dimensioned versions.
|
||||
*
|
||||
* @param string $source Full path to source image.
|
||||
* @param string $destinationDir Destination folder (will be created if missing).
|
||||
* @param array $dimensions Format: [['name' => 'thumb.jpg', 'width' => 100, 'height' => 100], ...]
|
||||
*
|
||||
* @return array Result array: ['thumb.jpg' => [...metadata...], 'invalid.jpg' => null, ...]
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function process(string $source, string $destinationDir, array $dimensions): array
|
||||
{
|
||||
$results = [];
|
||||
|
||||
foreach ($dimensions as $set)
|
||||
{
|
||||
if (
|
||||
!isset($set['name'], $set['width'], $set['height']) ||
|
||||
!is_numeric($set['width']) ||
|
||||
!is_numeric($set['height'])
|
||||
)
|
||||
{
|
||||
$results[$set['name'] ?? 'unknown'] = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
$outputPath = rtrim($destinationDir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $set['name'];
|
||||
$results[$set['name']] = $this->cropResize($source, $outputPath, (int) $set['width'], (int) $set['height']);
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Crop or scale an image to target size using center crop or just resize if aspect ratio matches.
|
||||
*
|
||||
* @param string $source Full absolute path to source image.
|
||||
* @param string $destination Full absolute path to destination image.
|
||||
* @param int $targetW Target width
|
||||
* @param int $targetH Target height
|
||||
*
|
||||
* @return array|null Image metadata on success, false on failure.
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function cropResize(string $source, string $destination, int $targetW, int $targetH): ?array
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!is_file($source))
|
||||
{
|
||||
throw new \RuntimeException("Source image not found: $source");
|
||||
}
|
||||
|
||||
$destFolder = dirname($destination);
|
||||
if (!is_dir($destFolder))
|
||||
{
|
||||
Folder::create($destFolder);
|
||||
}
|
||||
|
||||
$image = new JoomlaImage($source);
|
||||
|
||||
if (!$image->isLoaded())
|
||||
{
|
||||
throw new \RuntimeException("Failed to load image: $source");
|
||||
}
|
||||
|
||||
$originalW = $image->getWidth();
|
||||
$originalH = $image->getHeight();
|
||||
|
||||
// If already correct size, copy directly
|
||||
if ($originalW === $targetW && $originalH === $targetH)
|
||||
{
|
||||
File::copy($source, $destination);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Perform crop-resize directly
|
||||
$image = $image->cropResize($targetW, $targetH, true);
|
||||
$type = $this->getImageType($source);
|
||||
|
||||
if ($type === null || !$image->toFile($destination, $type))
|
||||
{
|
||||
throw new \RuntimeException("Failed to save image to $destination");
|
||||
}
|
||||
}
|
||||
|
||||
// Return metadata
|
||||
return [
|
||||
'name' => basename($destination),
|
||||
'extension' => MimeHelper::Extension($destination),
|
||||
'size' => is_file($destination) ? filesize($destination) : 0,
|
||||
'mime' => MimeHelper::MimeType($destination),
|
||||
'path' => $destination,
|
||||
];
|
||||
}
|
||||
catch (\Throwable $e)
|
||||
{
|
||||
Log::add($e->getMessage(), Log::ERROR, 'image-cropper');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the image type constant from the file path
|
||||
*
|
||||
* @param string $path Absolute path to the image file
|
||||
*
|
||||
* @return int|null Returns the IMAGETYPE_* constant or null if undetectable
|
||||
* @since 5.1.1
|
||||
*/
|
||||
private static function getImageType(string $path): ?int
|
||||
{
|
||||
// Use exif_imagetype to get the constant
|
||||
$type = @exif_imagetype($path);
|
||||
|
||||
// Validate it's a known IMAGETYPE
|
||||
return is_int($type) ? $type : null;
|
||||
}
|
||||
}
|
||||
|
@@ -21,6 +21,7 @@ use VDM\Joomla\Interfaces\Data\ItemsInterface as Items;
|
||||
use VDM\Joomla\Data\Guid;
|
||||
use VDM\Joomla\Componentbuilder\File\Type;
|
||||
use VDM\Joomla\Componentbuilder\File\Handler;
|
||||
use VDM\Joomla\Componentbuilder\File\Image;
|
||||
use VDM\Joomla\Utilities\MimeHelper;
|
||||
|
||||
|
||||
@@ -29,7 +30,7 @@ use VDM\Joomla\Utilities\MimeHelper;
|
||||
*
|
||||
* @since 5.0.2
|
||||
*/
|
||||
final class Manager
|
||||
class Manager
|
||||
{
|
||||
/**
|
||||
* The Globally Unique Identifier.
|
||||
@@ -70,6 +71,14 @@ final class Manager
|
||||
*/
|
||||
protected Handler $handler;
|
||||
|
||||
/**
|
||||
* The Image Class.
|
||||
*
|
||||
* @var Image
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected Image $image;
|
||||
|
||||
/**
|
||||
* The active user
|
||||
*
|
||||
@@ -93,15 +102,18 @@ final class Manager
|
||||
* @param Items $items The Items Class.
|
||||
* @param Type $type The Type Class.
|
||||
* @param Handler $handler The Handler Class.
|
||||
* @param Image $image The Image Class.
|
||||
*
|
||||
* @since 5.0.2
|
||||
*/
|
||||
public function __construct(Item $item, Items $items, Type $type, Handler $handler)
|
||||
public function __construct(Item $item, Items $items, Type $type, Handler $handler,
|
||||
Image $image)
|
||||
{
|
||||
$this->item = $item;
|
||||
$this->items = $items;
|
||||
$this->type = $type;
|
||||
$this->handler = $handler;
|
||||
$this->image = $image;
|
||||
$this->user = Factory::getApplication()->getIdentity();
|
||||
}
|
||||
|
||||
@@ -146,10 +158,10 @@ final class Manager
|
||||
throw new \RuntimeException($this->handler->getErrors());
|
||||
}
|
||||
|
||||
// we might need to crop images
|
||||
if ($fileType['type'] === 'image')
|
||||
{
|
||||
// $this->cropImage($details, $guid);
|
||||
$this->processImages($details, $guid, $entity, $target, $fileType);
|
||||
return;
|
||||
}
|
||||
|
||||
// store file in the file table
|
||||
@@ -225,6 +237,72 @@ final class Manager
|
||||
return $this->table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the image(s) as needed based on crop settings
|
||||
*
|
||||
* @param array $details The uploaded file details.
|
||||
* @param string $guid The file type guid
|
||||
* @param string $entity The entity guid
|
||||
* @param string $target The target entity name
|
||||
* @param array $fileType The file type
|
||||
*
|
||||
* @return void
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected function processImages(array $details, string $guid, string $entity, string $target, array $fileType): void
|
||||
{
|
||||
if (empty($fileType['crop']))
|
||||
{
|
||||
// store file in the file table
|
||||
$this->item->table($this->getTable())->set(
|
||||
$this->modelFileDetails($details, $guid, $entity, $target, $fileType)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
$source = $details['full_path'];
|
||||
$path = $details['path'];
|
||||
$cropping = $fileType['crop'];
|
||||
|
||||
$placeholders = [
|
||||
'{number}' => $this->getFileNumber($fileType, $entity),
|
||||
'{name}' => $this->getFileName($details, $entity),
|
||||
'{extension}' => $this->getFileExtension($source)
|
||||
];
|
||||
|
||||
foreach ($cropping as &$crop)
|
||||
{
|
||||
$crop['name'] = str_replace(array_keys($placeholders), array_values($placeholders), $crop['name']);
|
||||
}
|
||||
|
||||
$images = $this->image->process($source, $path, $cropping);
|
||||
|
||||
foreach($images as $image)
|
||||
{
|
||||
if (empty($image))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$details['name'] = $image['name'];
|
||||
$details['extension'] = $image['extension'];
|
||||
$details['size'] = $image['size'];
|
||||
$details['mime'] = $image['mime'];
|
||||
$details['full_path'] = $image['path'];
|
||||
|
||||
// store file in the file table
|
||||
$this->item->table($this->getTable())->set(
|
||||
$this->modelFileDetails($details, $guid, $entity, $target, $fileType)
|
||||
);
|
||||
}
|
||||
|
||||
// clean up source image
|
||||
if (is_file($source) && is_writable($source))
|
||||
{
|
||||
File::delete($source); // from file system
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* model the file details to store in the file table
|
||||
*
|
||||
@@ -252,6 +330,89 @@ final class Manager
|
||||
'guid' => $this->getGuid('guid'),
|
||||
'created_by' => $this->user->id
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file name without extension for download.
|
||||
*
|
||||
* If the original name is empty, return the entity GUID.
|
||||
* If the name does not contain a '.', return the name as is.
|
||||
* Otherwise, return the name without the final extension.
|
||||
*
|
||||
* @param array $details The uploaded file details.
|
||||
* @param string $entity The entity GUID used as fallback.
|
||||
*
|
||||
* @return string The extracted or fallback file name.
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected function getFileName(array $details, string $entity): string
|
||||
{
|
||||
// Check if name is set and non-empty
|
||||
$name = trim($details['name'] ?? '');
|
||||
|
||||
// Return entity if name is empty
|
||||
if ($name === '')
|
||||
{
|
||||
return $entity;
|
||||
}
|
||||
|
||||
// If there is no dot in the name, assume no extension — return as-is
|
||||
if (strpos($name, '.') === false)
|
||||
{
|
||||
return $name;
|
||||
}
|
||||
|
||||
// Use pathinfo to extract the name without extension
|
||||
$info = pathinfo($name);
|
||||
|
||||
// Return filename (without extension)
|
||||
return $info['filename'] ?? $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file number TODO: not ideal, if images are deleted we need a better solution
|
||||
*
|
||||
* @param array $fileType The uploaded file type details.
|
||||
* @param string $entity The entity guid
|
||||
*
|
||||
* @return int
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected function getFileNumber(array $fileType, string $entity): int
|
||||
{
|
||||
if (empty($fileType['crop']))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
$number = count($fileType['crop']);
|
||||
$number_files = 1;
|
||||
|
||||
if (($files = $this->items->table($this->getTable())->values([$entity], 'entity')) !== null)
|
||||
{
|
||||
$total = count($files);
|
||||
if ($total >= $number)
|
||||
{
|
||||
$number_files = round($total / $number);
|
||||
}
|
||||
|
||||
return ++$number_files;
|
||||
}
|
||||
|
||||
return $number_files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file extension
|
||||
*
|
||||
* @param sring $source The full path to the file
|
||||
*
|
||||
* @return string
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected function getFileExtension(string $source): string
|
||||
{
|
||||
return MimeHelper::extension($source);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -18,6 +18,7 @@ use VDM\Joomla\Componentbuilder\File\Type;
|
||||
use VDM\Joomla\Componentbuilder\File\Handler;
|
||||
use VDM\Joomla\Componentbuilder\File\Manager;
|
||||
use VDM\Joomla\Componentbuilder\File\Display;
|
||||
use VDM\Joomla\Componentbuilder\File\Image;
|
||||
|
||||
|
||||
/**
|
||||
@@ -48,6 +49,9 @@ class File implements ServiceProviderInterface
|
||||
|
||||
$container->alias(Display::class, 'File.Display')
|
||||
->share('File.Display', [$this, 'getDisplay'], true);
|
||||
|
||||
$container->alias(Image::class, 'File.Image')
|
||||
->share('File.Image', [$this, 'getImage'], true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,7 +96,8 @@ class File implements ServiceProviderInterface
|
||||
$container->get('Data.Item'),
|
||||
$container->get('Data.Items'),
|
||||
$container->get('File.Type'),
|
||||
$container->get('File.Handler')
|
||||
$container->get('File.Handler'),
|
||||
$container->get('File.Image')
|
||||
);
|
||||
}
|
||||
|
||||
@@ -110,6 +115,19 @@ class File implements ServiceProviderInterface
|
||||
$container->get('Data.Item'),
|
||||
$container->get('Data.Items')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get The Image Class.
|
||||
*
|
||||
* @param Container $container The DI container.
|
||||
*
|
||||
* @return Image
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public function getImage(Container $container): Image
|
||||
{
|
||||
return new Image();
|
||||
}
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user