Release of v5.0.2-beta4

Add native plugin builder for Joomla 4 & 5.
This commit is contained in:
2024-08-15 01:35:23 +02:00
parent 6d24fcfd2b
commit 22aa94ad9c
84 changed files with 10930 additions and 2183 deletions

View File

@@ -0,0 +1,215 @@
<?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\Architecture\JoomlaFive\Plugin;
use VDM\Joomla\Componentbuilder\Compiler\Placeholder;
use VDM\Joomla\Componentbuilder\Compiler\Builder\ContentOne as Builder;
use VDM\Joomla\Componentbuilder\Power\Parser;
use VDM\Joomla\Componentbuilder\Compiler\Utilities\Indent;
use VDM\Joomla\Componentbuilder\Compiler\Interfaces\Architecture\Plugin\ExtensionInterface;
/**
* Plugin Extension Class for Joomla 5
*
* @since 5.0.2
*/
final class Extension implements ExtensionInterface
{
/**
* The Placeholder Class.
*
* @var Placeholder
* @since 5.0.2
*/
protected Placeholder $placeholder;
/**
* The ContentOne Class.
*
* @var Builder
* @since 5.0.2
*/
protected Builder $builder;
/**
* The Parser Class.
*
* @var Parser
* @since 5.0.2
*/
protected Parser $parser;
/**
* Constructor.
*
* @param Placeholder $placeholder The Placeholder Class.
* @param Builder $builder The Content One Class.
* @param Parser $parser The Parser Class.
*
* @since 5.0.2
*/
public function __construct(Placeholder $placeholder, Builder $builder, Parser $parser)
{
$this->placeholder = $placeholder;
$this->builder = $builder;
$this->parser = $parser;
}
/**
* Get the updated placeholder content for the given plugin.
*
* @param object $plugin The plugin object containing the necessary data.
*
* @return string The updated placeholder content.
*
* @since 5.0.2
*/
public function get(object $plugin): string
{
$add_subscriber_interface = $this->addNeededMethods($plugin->main_class_code);
$extension = [];
$extension[] = $plugin->comment . PHP_EOL . 'final class ';
$extension[] = $plugin->class_name . ' extends ' . $plugin->extends;
if ($add_subscriber_interface)
{
$extension[] = ' implements Joomla__' . '_c06c5116_6b9d_487c_9b09_5094ec4506a3___Power';
}
$extension[] = PHP_EOL . '{' . PHP_EOL;
$extension[] = $plugin->main_class_code;
$extension[] = PHP_EOL . '}' . PHP_EOL;
return $this->placeholder->update(
implode('', $extension),
$this->builder->allActive()
);
}
/**
* Ensures that the required methods are present in the plugin code.
*
* This method checks the plugin's code for the presence of required methods,
* particularly the method that indicates implementation of the SubscriberInterface.
* If the necessary method is missing, it adds it to the code.
*
* @param string $code The main code of the plugin, passed by reference.
*
* @return bool Returns true if the SubscriberInterface implementation is added or already present, false otherwise.
*
* @since 5.0.2
*/
protected function addNeededMethods(string &$code): bool
{
// Parse the code to extract its structure, particularly its methods.
$code_structure = $this->parser->code($code);
if (empty($code_structure['methods']))
{
return false;
}
// Check if methods are defined and if getSubscribedEvents is not present.
if (!$this->getSubscribedEvents($code_structure['methods']))
{
// Attempt to add the getSubscribedEvents method.
$method = $this->addGetSubscribedEvents($code_structure['methods']);
if ($method !== null)
{
// Append the new method to the code and indicate that the interface must be added.
$code .= $method;
return true;
}
// Return false if the event method could not be added.
return false;
}
// Return true if getSubscribedEvents is already present.
return true;
}
/**
* Add the getSubscribedEvents method
*
* @param array $methods The plugin methods.
*
* @return string|null The getSubscribedEvents code
*
* @since 5.0.2
*/
protected function addGetSubscribedEvents(array $methods): ?string
{
$events = [];
$counter = 0;
foreach ($methods as $method)
{
if ($method['access'] === 'public' && !$method['static'] && !$method['abstract'])
{
$events[$method['name']] = Indent::_(3) . "'{$method['name']}' => '{$method['name']}'";
// autoloaded when method start with 'on'
// so we can ignore adding the getSubscribedEvents
if (substr($method['name'], 0, 2) === 'on')
{
$counter++;
}
}
}
if ($events === [] || $counter == count($events))
{
return null;
}
$method = [];
$method[] = PHP_EOL . PHP_EOL . Indent::_(1) . '/**';
$method[] = Indent::_(1) . ' * Returns an array of events this subscriber will listen to.';
$method[] = Indent::_(1) . ' *';
$method[] = Indent::_(1) . ' * @return array';
$method[] = Indent::_(1) . ' *';
$method[] = Indent::_(1) . ' * @since 5.0.0';
$method[] = Indent::_(1) . ' */';
$method[] = Indent::_(1) . 'public static function getSubscribedEvents(): array';
$method[] = Indent::_(1) . '{';
$method[] = Indent::_(2) . 'return [';
$method[] = implode(',' . PHP_EOL, $events);
$method[] = Indent::_(2) . '];';
$method[] = Indent::_(1) . '}';
return implode(PHP_EOL, $method);
}
/**
* Check if the getSubscribedEvents is set
*
* @param array $methods The plugin methods.
*
* @return bool
*
* @since 5.0.2
*/
protected function getSubscribedEvents(array $methods): bool
{
foreach ($methods as $method)
{
if ($method['name'] === 'getSubscribedEvents' && $method['static'] && !$method['abstract'])
{
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,569 @@
<?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\Architecture\JoomlaFive\Plugin;
use Joomla\CMS\Filesystem\Folder;
use VDM\Joomla\Componentbuilder\Compiler\Config;
use VDM\Joomla\Componentbuilder\Compiler\Language;
use VDM\Joomla\Componentbuilder\Compiler\Language\Set;
use VDM\Joomla\Componentbuilder\Compiler\Language\Purge;
use VDM\Joomla\Componentbuilder\Compiler\Language\Translation;
use VDM\Joomla\Componentbuilder\Compiler\Language\Multilingual;
use VDM\Joomla\Componentbuilder\Compiler\Interfaces\EventInterface as Event;
use VDM\Joomla\Componentbuilder\Compiler\Creator\FieldsetExtension;
use VDM\Joomla\Componentbuilder\Compiler\Builder\ContentOne;
use VDM\Joomla\Componentbuilder\Compiler\Builder\Languages;
use VDM\Joomla\Componentbuilder\Compiler\Builder\Multilingual as BuilderMultilingual;
use VDM\Joomla\Componentbuilder\Compiler\Utilities\Counter;
use VDM\Joomla\Componentbuilder\Compiler\Utilities\File;
use VDM\Joomla\Componentbuilder\Compiler\Utilities\Line;
use VDM\Joomla\Componentbuilder\Compiler\Utilities\Indent;
use VDM\Joomla\Utilities\ArrayHelper;
use VDM\Joomla\Utilities\StringHelper;
use VDM\Joomla\Componentbuilder\Interfaces\Architecture\Plugin\MainXMLInterface;
/**
* Joomla 5 Plugin Main XML Class
*
* @since 5.0.2
*/
final class MainXML implements MainXMLInterface
{
/**
* The Config Class.
*
* @var Config
* @since 5.0.2
*/
protected Config $config;
/**
* The Language Class.
*
* @var Language
* @since 5.0.2
*/
protected Language $language;
/**
* The Set Class.
*
* @var Set
* @since 5.0.2
*/
protected Set $set;
/**
* The Purge Class.
*
* @var Purge
* @since 5.0.2
*/
protected Purge $purge;
/**
* The Translation Class.
*
* @var Translation
* @since 5.0.2
*/
protected Translation $translation;
/**
* The Multilingual Class.
*
* @var Multilingual
* @since 5.0.2
*/
protected Multilingual $multilingual;
/**
* The EventInterface Class.
*
* @var Event
* @since 5.0.2
*/
protected Event $event;
/**
* The FieldsetExtension Class.
*
* @var FieldsetExtension
* @since 5.0.2
*/
protected FieldsetExtension $fieldsetextension;
/**
* The ContentOne Class.
*
* @var ContentOne
* @since 5.0.2
*/
protected ContentOne $contentone;
/**
* The Languages Class.
*
* @var Languages
* @since 5.0.2
*/
protected Languages $languages;
/**
* The Multilingual Class.
*
* @var BuilderMultilingual
* @since 5.0.2
*/
protected BuilderMultilingual $buildermultilingual;
/**
* The Counter Class.
*
* @var Counter
* @since 5.0.2
*/
protected Counter $counter;
/**
* The File Class.
*
* @var File
* @since 5.0.2
*/
protected File $file;
/**
* Constructor.
*
* @param Config $config The Config Class.
* @param Language $language The Language Class.
* @param Set $set The Set Class.
* @param Purge $purge The Purge Class.
* @param Translation $translation The Translation Class.
* @param Multilingual $multilingual The Multilingual Class.
* @param Event $event The EventInterface Class.
* @param FieldsetExtension $fieldsetextension The FieldsetExtension Class.
* @param ContentOne $contentone The ContentOne Class.
* @param Languages $languages The Languages Class.
* @param BuilderMultilingual $buildermultilingual The Multilingual Class.
* @param Counter $counter The Counter Class.
* @param File $file The File Class.
*
* @since 5.0.2
*/
public function __construct(Config $config, Language $language, Set $set, Purge $purge,
Translation $translation, Multilingual $multilingual,
Event $event, FieldsetExtension $fieldsetextension,
ContentOne $contentone, Languages $languages,
BuilderMultilingual $buildermultilingual,
Counter $counter, File $file)
{
$this->config = $config;
$this->language = $language;
$this->set = $set;
$this->purge = $purge;
$this->translation = $translation;
$this->multilingual = $multilingual;
$this->event = $event;
$this->fieldsetextension = $fieldsetextension;
$this->contentone = $contentone;
$this->languages = $languages;
$this->buildermultilingual = $buildermultilingual;
$this->counter = $counter;
$this->file = $file;
}
/**
* Generates the main XML for the plugin.
*
* @param object $plugin The plugin object.
*
* @return string The generated XML.
* @since 5.0.2
*/
public function get(object $plugin): string
{
$config_fields = $this->buildConfigFields($plugin);
$add_component_path = $this->shouldAddComponentPath($plugin);
$language_files = $this->generateLanguageFiles($plugin);
$xml = $this->generateScriptAndSqlXml($plugin);
$xml .= $this->generateLanguageXml($plugin, $language_files);
$xml .= $this->generateFileXml($plugin, $language_files);
$xml .= $this->generateConfigXml($plugin, $config_fields, $add_component_path);
$xml .= $this->generateUpdateServerXml($plugin);
return $xml;
}
/**
* Build configuration fields XML.
*
* @param object $plugin The plugin object.
*
* @return array The configuration fields.
* @since 5.0.2
*/
protected function buildConfigFields(object $plugin): array
{
$configFields = [];
if (!isset($plugin->config_fields) || !ArrayHelper::check($plugin->config_fields))
{
return $configFields;
}
$dbKey = 'yy';
foreach ($plugin->config_fields as $fieldName => $fieldsets)
{
foreach ($fieldsets as $fieldset => $fields)
{
$xmlFields = $this->fieldsetextension->get($plugin, $fields, $dbKey);
if (isset($xmlFields) && StringHelper::check($xmlFields))
{
$configFields["{$fieldName}{$fieldset}"] = $xmlFields;
}
$dbKey++;
}
}
return $configFields;
}
/**
* Determine if the component path should be added.
*
* @param object $plugin The plugin object.
*
* @return bool True if the component path should be added, false otherwise.
* @since 5.0.2
*/
protected function shouldAddComponentPath(object $plugin): bool
{
if (!isset($plugin->config_fields) || !ArrayHelper::check($plugin->config_fields) ||
!isset($plugin->fieldsets_paths) || !ArrayHelper::check($plugin->fieldsets_paths))
{
return false;
}
foreach ($plugin->config_fields as $fieldName => $fieldsets)
{
foreach ($fieldsets as $fieldset => $fields)
{
if (isset($plugin->fieldsets_paths["{$fieldName}{$fieldset}"]) &&
$plugin->fieldsets_paths["{$fieldName}{$fieldset}"] == 1)
{
return true;
}
}
}
return false;
}
/**
* Generate XML for script and SQL files.
*
* @param object $plugin The plugin object.
*
* @return string The XML for script and SQL files.
* @since 5.0.2
*/
protected function generateScriptAndSqlXml(object $plugin): string
{
$xml = '';
if ($plugin->add_install_script)
{
$xml .= PHP_EOL . PHP_EOL . Indent::_(1) . '<!--' . Line::_(
__LINE__,__CLASS__
) . ' Scripts to run on installation -->';
$xml .= PHP_EOL . Indent::_(1) . '<scriptfile>script.php</scriptfile>';
}
if ($plugin->add_sql)
{
$xml .= PHP_EOL . PHP_EOL . Indent::_(1) . '<!--' . Line::_(
__LINE__,__CLASS__
) . ' Runs on install -->';
$xml .= PHP_EOL . Indent::_(1) . '<install>';
$xml .= PHP_EOL . Indent::_(2) . '<sql>';
$xml .= PHP_EOL . Indent::_(3) . '<file driver="mysql" charset="utf8">sql/mysql/install.sql</file>';
$xml .= PHP_EOL . Indent::_(2) . '</sql>';
$xml .= PHP_EOL . Indent::_(1) . '</install>';
}
if ($plugin->add_sql_uninstall)
{
$xml .= PHP_EOL . PHP_EOL . Indent::_(1) . '<!--' . Line::_(
__LINE__,__CLASS__
) . ' Runs on uninstall -->';
$xml .= PHP_EOL . Indent::_(1) . '<uninstall>';
$xml .= PHP_EOL . Indent::_(2) . '<sql>';
$xml .= PHP_EOL . Indent::_(3) . '<file driver="mysql" charset="utf8">sql/mysql/uninstall.sql</file>';
$xml .= PHP_EOL . Indent::_(2) . '</sql>';
$xml .= PHP_EOL . Indent::_(1) . '</uninstall>';
}
return $xml;
}
/**
* Generate XML for language files.
*
* @param object $plugin The plugin object.
* @param array $languageFiles The language files.
*
* @return string The XML for language files.
* @since 5.0.2
*/
protected function generateLanguageXml(object $plugin, array $languageFiles): string
{
$xml = '';
if (ArrayHelper::check($languageFiles))
{
$xml .= PHP_EOL . PHP_EOL . Indent::_(1) . '<!--' . Line::_(
__LINE__,__CLASS__
) . ' Language files -->';
$xml .= PHP_EOL . Indent::_(1) . '<languages folder="language">';
foreach ($languageFiles as $addTag)
{
$xml .= PHP_EOL . Indent::_(2) . '<language tag="'
. $addTag . '">' . $addTag . '/plg_'
. strtolower((string) $plugin->group) . '_' .
(string) $plugin->file_name
. '.ini</language>';
$xml .= PHP_EOL . Indent::_(2) . '<language tag="'
. $addTag . '">' . $addTag . '/plg_'
. strtolower((string) $plugin->group) . '_' .
(string) $plugin->file_name
. '.sys.ini</language>';
}
$xml .= PHP_EOL . Indent::_(1) . '</languages>';
}
return $xml;
}
/**
* Generate the XML for the files.
*
* @param object $plugin The plugin object.
* @param array $languageFiles The language files.
*
* @return string The XML for the files.
* @since 5.0.2
*/
protected function generateFileXml(object $plugin, array $languageFiles): string
{
$files = Folder::files($plugin->folder_path);
$folders = Folder::folders($plugin->folder_path);
$ignore = ['sql', 'language', 'script.php', "{$plugin->file_name}.xml"];
$xml = PHP_EOL . PHP_EOL . Indent::_(1) . '<!--' . Line::_(
__LINE__,__CLASS__
) . ' Plugin files -->';
$xml .= PHP_EOL . Indent::_(1) . '<files>';
$xml .= PHP_EOL . Indent::_(2) . "<folder plugin=\"{$plugin->context_name}\">services</folder>";
foreach ($files as $file)
{
if (!in_array($file, $ignore))
{
$xml .= PHP_EOL . Indent::_(2) . "<filename>{$file}</filename>";
}
}
if (!empty($languageFiles))
{
// $xml .= PHP_EOL . Indent::_(2) . '<folder>language</folder>';
}
if ($plugin->add_sql || $plugin->add_sql_uninstall)
{
$xml .= PHP_EOL . Indent::_(2) . '<folder>sql</folder>';
}
foreach ($folders as $folder)
{
if (!in_array($folder, $ignore))
{
$xml .= PHP_EOL . Indent::_(2) . "<folder>{$folder}</folder>";
}
}
$xml .= PHP_EOL . Indent::_(1) . '</files>';
return $xml;
}
/**
* Generate XML for configuration fields.
*
* @param object $plugin The plugin object.
* @param array $configFields The configuration fields.
* @param bool $addComponentPath Whether to add the component path.
*
* @return string The XML for configuration fields.
* @since 5.0.2
*/
protected function generateConfigXml(object $plugin, array $configFields, bool $addComponentPath): string
{
if (!isset($plugin->config_fields) || !ArrayHelper::check($configFields))
{
return '';
}
$xml = PHP_EOL . PHP_EOL . Indent::_(1) . '<!--' . Line::_(
__LINE__,__CLASS__
) . ' Config parameters -->';
$xml .= $addComponentPath ? PHP_EOL . Indent::_(1) . '<config' : PHP_EOL . Indent::_(1) . '<config>';
if ($addComponentPath)
{
$xml .= PHP_EOL . Indent::_(3) . 'addruleprefix="' . $this->config->namespace_prefix . '\Component\\' . $this->contentone->get('ComponentNamespace') . '\Administrator\Rule"';
$xml .= PHP_EOL . Indent::_(3) . 'addfieldprefix="' . $this->config->namespace_prefix . '\Component\\' . $this->contentone->get('ComponentNamespace') . '\Administrator\Field">';
$xml .= PHP_EOL . Indent::_(1) . '>';
}
foreach ($plugin->config_fields as $fieldName => $fieldsets)
{
$xml .= PHP_EOL . Indent::_(1) . "<fields name=\"{$fieldName}\">";
foreach ($fieldsets as $fieldset => $fields)
{
$label = $plugin->fieldsets_label["{$fieldName}{$fieldset}"] ?? $fieldset;
$xml .= PHP_EOL . Indent::_(1) . "<fieldset name=\"{$fieldset}\" label=\"{$label}\">";
if (isset($configFields["{$fieldName}{$fieldset}"]))
{
$xml .= $configFields["{$fieldName}{$fieldset}"];
}
$xml .= PHP_EOL . Indent::_(1) . '</fieldset>';
}
$xml .= PHP_EOL . Indent::_(1) . '</fields>';
}
$xml .= PHP_EOL . Indent::_(1) . '</config>';
return $xml;
}
/**
* Generate XML for update servers.
*
* @param object $plugin The plugin object.
*
* @return string The XML for update servers.
* @since 5.0.2
*/
protected function generateUpdateServerXml(object $plugin): string
{
$xml = '';
if ($plugin->add_update_server)
{
$xml = PHP_EOL . PHP_EOL . Indent::_(1) . '<!--' . Line::_(
__LINE__,__CLASS__
) . ' Update servers -->';
$xml .= PHP_EOL . Indent::_(1) . '<updateservers>';
$xml .= PHP_EOL . Indent::_(2) . '<server type="extension" priority="1" name="' . $plugin->official_name . '">' . $plugin->update_server_url . '</server>';
$xml .= PHP_EOL . Indent::_(1) . '</updateservers>';
}
return $xml;
}
/**
* Generate language files.
*
* @param object $plugin The plugin object.
*
* @return array The language files.
* @since 5.0.2
*/
protected function generateLanguageFiles(object $plugin): array
{
$languageFiles = [];
if (!$this->language->exist($plugin->key))
{
return $languageFiles;
}
$langContent = $this->language->getTarget($plugin->key);
$this->event->trigger('jcb_ce_onBeforeBuildPluginLang', [&$plugin, &$langContent]);
$values = array_unique($langContent);
$this->buildermultilingual->set('plugins', $this->multilingual->get($values));
$langTag = $this->config->get('lang_tag', 'en-GB');
$this->languages->set("plugins.{$langTag}.all", $langContent);
$this->language->setTarget($plugin->key, null);
$this->set->execute($values, $plugin->id, 'plugins');
$this->purge->execute($values, $plugin->id, 'plugins');
$this->event->trigger('jcb_ce_onBeforeBuildPluginLangFiles', [&$plugin]);
if ($this->languages->IsArray('plugins'))
{
foreach ($this->languages->get('plugins') as $tag => $areas)
{
$tag = trim($tag);
foreach ($areas as $area => $languageStrings)
{
$fileName = "plg_" . strtolower((string)$plugin->group) . '_' . $plugin->file_name . '.ini';
$total = count($values);
if ($this->translation->check($tag, $languageStrings, $total, $fileName))
{
$lang = array_map(
fn($langString, $placeholder) => "{$placeholder}=\"{$langString}\"",
array_values($languageStrings),
array_keys($languageStrings)
);
$path = "{$plugin->folder_path}/language/{$tag}/";
if (!Folder::exists($path))
{
Folder::create($path);
$this->counter->folder++;
}
$this->file->write($path . $fileName, implode(PHP_EOL, $lang));
$this->file->write(
$path . 'plg_' . strtolower((string)$plugin->group) . '_' . $plugin->file_name . '.sys.ini',
implode(PHP_EOL, $lang)
);
$this->counter->line += count($lang);
unset($lang);
$languageFiles[$tag] = $tag;
}
}
}
}
return $languageFiles;
}
}

View File

@@ -0,0 +1,106 @@
<?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\Architecture\JoomlaFive\Plugin;
use VDM\Joomla\Componentbuilder\Compiler\Placeholder;
use VDM\Joomla\Componentbuilder\Compiler\Builder\ContentOne as Builder;
use VDM\Joomla\Componentbuilder\Compiler\Utilities\Indent;
use VDM\Joomla\Componentbuilder\Compiler\Utilities\Line;
use VDM\Joomla\Componentbuilder\Compiler\Interfaces\Architecture\Plugin\ProviderInterface;
/**
* Plugin Provider Class for Joomla 5
*
* @since 5.0.2
*/
final class Provider implements ProviderInterface
{
/**
* The Placeholder Class.
*
* @var Placeholder
* @since 5.0.2
*/
protected Placeholder $placeholder;
/**
* The ContentOne Class.
*
* @var Builder
* @since 5.0.2
*/
protected Builder $builder;
/**
* Constructor.
*
* @param Placeholder $placeholder The Placeholder Class.
* @param Builder $builder The Content One Class.
*
* @since 5.0.2
*/
public function __construct(Placeholder $placeholder, Builder $builder)
{
$this->placeholder = $placeholder;
$this->builder = $builder;
}
/**
* Get the updated provider for the given plugin.
*
* @param object $plugin The plugin object containing the necessary data.
*
* @return string The provider content.
*
* @since 5.0.2
*/
public function get(object $plugin): string
{
$group = strtolower((string) $plugin->group);
$provider = [];
$provider[] = PHP_EOL . PHP_EOL . "return new class () implements ServiceProviderInterface {";
$provider[] = Indent::_(1) . "/**";
$provider[] = Indent::_(1) . "*" . Line::_(__Line__, __Class__)
. " Registers the service provider with a DI container.";
$provider[] = Indent::_(1) . "*";
$provider[] = Indent::_(1) . "* @param Container \$container The DI container.";
$provider[] = Indent::_(1) . "*";
$provider[] = Indent::_(1) . "* @return void";
$provider[] = Indent::_(1) . "* @since 4.3.0";
$provider[] = Indent::_(1) . "*/";
$provider[] = Indent::_(1) . "public function register(Container \$container)";
$provider[] = Indent::_(1) . "{";
$provider[] = Indent::_(2) . "\$container->set(";
$provider[] = Indent::_(3) . "PluginInterface::class,";
$provider[] = Indent::_(3) . "function (Container \$container) {";
$provider[] = Indent::_(4) . "\$plugin = new {$plugin->class_name}(";
$provider[] = Indent::_(5) . "\$container->get(DispatcherInterface::class),";
$provider[] = Indent::_(5) . "(array) PluginHelper::getPlugin('{$group}', '{$plugin->context_name}')";
$provider[] = Indent::_(4) . ");";
$provider[] = Indent::_(4) . "\$plugin->setApplication(Factory::getApplication());";
$provider[] = $plugin->service_provider ?? ''; // to add extra plug-in suff
$provider[] = Indent::_(4) . "return \$plugin;";
$provider[] = Indent::_(3) . "}";
$provider[] = Indent::_(2) . ");";
$provider[] = Indent::_(1) . "}";
$provider[] = "};";
return $this->placeholder->update(
implode(PHP_EOL, $provider). PHP_EOL,
$this->builder->allActive()
);
}
}

View File

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

View File

@@ -0,0 +1,215 @@
<?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\Architecture\JoomlaFour\Plugin;
use VDM\Joomla\Componentbuilder\Compiler\Placeholder;
use VDM\Joomla\Componentbuilder\Compiler\Builder\ContentOne as Builder;
use VDM\Joomla\Componentbuilder\Power\Parser;
use VDM\Joomla\Componentbuilder\Compiler\Utilities\Indent;
use VDM\Joomla\Componentbuilder\Compiler\Interfaces\Architecture\Plugin\ExtensionInterface;
/**
* Plugin Extension Class for Joomla 4
*
* @since 5.0.2
*/
final class Extension implements ExtensionInterface
{
/**
* The Placeholder Class.
*
* @var Placeholder
* @since 5.0.2
*/
protected Placeholder $placeholder;
/**
* The ContentOne Class.
*
* @var Builder
* @since 5.0.2
*/
protected Builder $builder;
/**
* The Parser Class.
*
* @var Parser
* @since 5.0.2
*/
protected Parser $parser;
/**
* Constructor.
*
* @param Placeholder $placeholder The Placeholder Class.
* @param Builder $builder The Content One Class.
* @param Parser $parser The Parser Class.
*
* @since 5.0.2
*/
public function __construct(Placeholder $placeholder, Builder $builder, Parser $parser)
{
$this->placeholder = $placeholder;
$this->builder = $builder;
$this->parser = $parser;
}
/**
* Get the updated placeholder content for the given plugin.
*
* @param object $plugin The plugin object containing the necessary data.
*
* @return string The updated placeholder content.
*
* @since 5.0.2
*/
public function get(object $plugin): string
{
$add_subscriber_interface = $this->addNeededMethods($plugin->main_class_code);
$extension = [];
$extension[] = $plugin->comment . PHP_EOL . 'final class ';
$extension[] = $plugin->class_name . ' extends ' . $plugin->extends;
if ($add_subscriber_interface)
{
$extension[] = ' implements Joomla__' . '_c06c5116_6b9d_487c_9b09_5094ec4506a3___Power';
}
$extension[] = PHP_EOL . '{' . PHP_EOL;
$extension[] = $plugin->main_class_code;
$extension[] = PHP_EOL . '}' . PHP_EOL;
return $this->placeholder->update(
implode('', $extension),
$this->builder->allActive()
);
}
/**
* Ensures that the required methods are present in the plugin code.
*
* This method checks the plugin's code for the presence of required methods,
* particularly the method that indicates implementation of the SubscriberInterface.
* If the necessary method is missing, it adds it to the code.
*
* @param string $code The main code of the plugin, passed by reference.
*
* @return bool Returns true if the SubscriberInterface implementation is added or already present, false otherwise.
*
* @since 5.0.2
*/
protected function addNeededMethods(string &$code): bool
{
// Parse the code to extract its structure, particularly its methods.
$code_structure = $this->parser->code($code);
if (empty($code_structure['methods']))
{
return false;
}
// Check if methods are defined and if getSubscribedEvents is not present.
if (!$this->getSubscribedEvents($code_structure['methods']))
{
// Attempt to add the getSubscribedEvents method.
$method = $this->addGetSubscribedEvents($code_structure['methods']);
if ($method !== null)
{
// Append the new method to the code and indicate that the interface must be added.
$code .= $method;
return true;
}
// Return false if the event method could not be added.
return false;
}
// Return true if getSubscribedEvents is already present.
return true;
}
/**
* Add the getSubscribedEvents method
*
* @param array $methods The plugin methods.
*
* @return string|null The getSubscribedEvents code
*
* @since 5.0.2
*/
protected function addGetSubscribedEvents(array $methods): ?string
{
$events = [];
$counter = 0;
foreach ($methods as $method)
{
if ($method['access'] === 'public' && !$method['static'] && !$method['abstract'])
{
$events[$method['name']] = Indent::_(3) . "'{$method['name']}' => '{$method['name']}'";
// autoloaded when method start with 'on'
// so we can ignore adding the getSubscribedEvents
if (substr($method['name'], 0, 2) === 'on')
{
$counter++;
}
}
}
if ($events === [] || $counter == count($events))
{
return null;
}
$method = [];
$method[] = PHP_EOL . PHP_EOL . Indent::_(1) . '/**';
$method[] = Indent::_(1) . ' * Returns an array of events this subscriber will listen to.';
$method[] = Indent::_(1) . ' *';
$method[] = Indent::_(1) . ' * @return array';
$method[] = Indent::_(1) . ' *';
$method[] = Indent::_(1) . ' * @since 5.0.0';
$method[] = Indent::_(1) . ' */';
$method[] = Indent::_(1) . 'public static function getSubscribedEvents(): array';
$method[] = Indent::_(1) . '{';
$method[] = Indent::_(2) . 'return [';
$method[] = implode(',' . PHP_EOL, $events);
$method[] = Indent::_(2) . '];';
$method[] = Indent::_(1) . '}';
return implode(PHP_EOL, $method);
}
/**
* Check if the getSubscribedEvents is set
*
* @param array $methods The plugin methods.
*
* @return bool
*
* @since 5.0.2
*/
protected function getSubscribedEvents(array $methods): bool
{
foreach ($methods as $method)
{
if ($method['name'] === 'getSubscribedEvents' && $method['static'] && !$method['abstract'])
{
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,569 @@
<?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\Architecture\JoomlaFour\Plugin;
use Joomla\CMS\Filesystem\Folder;
use VDM\Joomla\Componentbuilder\Compiler\Config;
use VDM\Joomla\Componentbuilder\Compiler\Language;
use VDM\Joomla\Componentbuilder\Compiler\Language\Set;
use VDM\Joomla\Componentbuilder\Compiler\Language\Purge;
use VDM\Joomla\Componentbuilder\Compiler\Language\Translation;
use VDM\Joomla\Componentbuilder\Compiler\Language\Multilingual;
use VDM\Joomla\Componentbuilder\Compiler\Interfaces\EventInterface as Event;
use VDM\Joomla\Componentbuilder\Compiler\Creator\FieldsetExtension;
use VDM\Joomla\Componentbuilder\Compiler\Builder\ContentOne;
use VDM\Joomla\Componentbuilder\Compiler\Builder\Languages;
use VDM\Joomla\Componentbuilder\Compiler\Builder\Multilingual as BuilderMultilingual;
use VDM\Joomla\Componentbuilder\Compiler\Utilities\Counter;
use VDM\Joomla\Componentbuilder\Compiler\Utilities\File;
use VDM\Joomla\Componentbuilder\Compiler\Utilities\Line;
use VDM\Joomla\Componentbuilder\Compiler\Utilities\Indent;
use VDM\Joomla\Utilities\ArrayHelper;
use VDM\Joomla\Utilities\StringHelper;
use VDM\Joomla\Componentbuilder\Interfaces\Architecture\Plugin\MainXMLInterface;
/**
* Joomla 4 Plugin Main XML Class
*
* @since 5.0.2
*/
final class MainXML implements MainXMLInterface
{
/**
* The Config Class.
*
* @var Config
* @since 5.0.2
*/
protected Config $config;
/**
* The Language Class.
*
* @var Language
* @since 5.0.2
*/
protected Language $language;
/**
* The Set Class.
*
* @var Set
* @since 5.0.2
*/
protected Set $set;
/**
* The Purge Class.
*
* @var Purge
* @since 5.0.2
*/
protected Purge $purge;
/**
* The Translation Class.
*
* @var Translation
* @since 5.0.2
*/
protected Translation $translation;
/**
* The Multilingual Class.
*
* @var Multilingual
* @since 5.0.2
*/
protected Multilingual $multilingual;
/**
* The EventInterface Class.
*
* @var Event
* @since 5.0.2
*/
protected Event $event;
/**
* The FieldsetExtension Class.
*
* @var FieldsetExtension
* @since 5.0.2
*/
protected FieldsetExtension $fieldsetextension;
/**
* The ContentOne Class.
*
* @var ContentOne
* @since 5.0.2
*/
protected ContentOne $contentone;
/**
* The Languages Class.
*
* @var Languages
* @since 5.0.2
*/
protected Languages $languages;
/**
* The Multilingual Class.
*
* @var BuilderMultilingual
* @since 5.0.2
*/
protected BuilderMultilingual $buildermultilingual;
/**
* The Counter Class.
*
* @var Counter
* @since 5.0.2
*/
protected Counter $counter;
/**
* The File Class.
*
* @var File
* @since 5.0.2
*/
protected File $file;
/**
* Constructor.
*
* @param Config $config The Config Class.
* @param Language $language The Language Class.
* @param Set $set The Set Class.
* @param Purge $purge The Purge Class.
* @param Translation $translation The Translation Class.
* @param Multilingual $multilingual The Multilingual Class.
* @param Event $event The EventInterface Class.
* @param FieldsetExtension $fieldsetextension The FieldsetExtension Class.
* @param ContentOne $contentone The ContentOne Class.
* @param Languages $languages The Languages Class.
* @param BuilderMultilingual $buildermultilingual The Multilingual Class.
* @param Counter $counter The Counter Class.
* @param File $file The File Class.
*
* @since 5.0.2
*/
public function __construct(Config $config, Language $language, Set $set, Purge $purge,
Translation $translation, Multilingual $multilingual,
Event $event, FieldsetExtension $fieldsetextension,
ContentOne $contentone, Languages $languages,
BuilderMultilingual $buildermultilingual,
Counter $counter, File $file)
{
$this->config = $config;
$this->language = $language;
$this->set = $set;
$this->purge = $purge;
$this->translation = $translation;
$this->multilingual = $multilingual;
$this->event = $event;
$this->fieldsetextension = $fieldsetextension;
$this->contentone = $contentone;
$this->languages = $languages;
$this->buildermultilingual = $buildermultilingual;
$this->counter = $counter;
$this->file = $file;
}
/**
* Generates the main XML for the plugin.
*
* @param object $plugin The plugin object.
*
* @return string The generated XML.
* @since 5.0.2
*/
public function get(object $plugin): string
{
$config_fields = $this->buildConfigFields($plugin);
$add_component_path = $this->shouldAddComponentPath($plugin);
$language_files = $this->generateLanguageFiles($plugin);
$xml = $this->generateScriptAndSqlXml($plugin);
$xml .= $this->generateLanguageXml($plugin, $language_files);
$xml .= $this->generateFileXml($plugin, $language_files);
$xml .= $this->generateConfigXml($plugin, $config_fields, $add_component_path);
$xml .= $this->generateUpdateServerXml($plugin);
return $xml;
}
/**
* Build configuration fields XML.
*
* @param object $plugin The plugin object.
*
* @return array The configuration fields.
* @since 5.0.2
*/
protected function buildConfigFields(object $plugin): array
{
$configFields = [];
if (!isset($plugin->config_fields) || !ArrayHelper::check($plugin->config_fields))
{
return $configFields;
}
$dbKey = 'yy';
foreach ($plugin->config_fields as $fieldName => $fieldsets)
{
foreach ($fieldsets as $fieldset => $fields)
{
$xmlFields = $this->fieldsetextension->get($plugin, $fields, $dbKey);
if (isset($xmlFields) && StringHelper::check($xmlFields))
{
$configFields["{$fieldName}{$fieldset}"] = $xmlFields;
}
$dbKey++;
}
}
return $configFields;
}
/**
* Determine if the component path should be added.
*
* @param object $plugin The plugin object.
*
* @return bool True if the component path should be added, false otherwise.
* @since 5.0.2
*/
protected function shouldAddComponentPath(object $plugin): bool
{
if (!isset($plugin->config_fields) || !ArrayHelper::check($plugin->config_fields) ||
!isset($plugin->fieldsets_paths) || !ArrayHelper::check($plugin->fieldsets_paths))
{
return false;
}
foreach ($plugin->config_fields as $fieldName => $fieldsets)
{
foreach ($fieldsets as $fieldset => $fields)
{
if (isset($plugin->fieldsets_paths["{$fieldName}{$fieldset}"]) &&
$plugin->fieldsets_paths["{$fieldName}{$fieldset}"] == 1)
{
return true;
}
}
}
return false;
}
/**
* Generate XML for script and SQL files.
*
* @param object $plugin The plugin object.
*
* @return string The XML for script and SQL files.
* @since 5.0.2
*/
protected function generateScriptAndSqlXml(object $plugin): string
{
$xml = '';
if ($plugin->add_install_script)
{
$xml .= PHP_EOL . PHP_EOL . Indent::_(1) . '<!--' . Line::_(
__LINE__,__CLASS__
) . ' Scripts to run on installation -->';
$xml .= PHP_EOL . Indent::_(1) . '<scriptfile>script.php</scriptfile>';
}
if ($plugin->add_sql)
{
$xml .= PHP_EOL . PHP_EOL . Indent::_(1) . '<!--' . Line::_(
__LINE__,__CLASS__
) . ' Runs on install -->';
$xml .= PHP_EOL . Indent::_(1) . '<install>';
$xml .= PHP_EOL . Indent::_(2) . '<sql>';
$xml .= PHP_EOL . Indent::_(3) . '<file driver="mysql" charset="utf8">sql/mysql/install.sql</file>';
$xml .= PHP_EOL . Indent::_(2) . '</sql>';
$xml .= PHP_EOL . Indent::_(1) . '</install>';
}
if ($plugin->add_sql_uninstall)
{
$xml .= PHP_EOL . PHP_EOL . Indent::_(1) . '<!--' . Line::_(
__LINE__,__CLASS__
) . ' Runs on uninstall -->';
$xml .= PHP_EOL . Indent::_(1) . '<uninstall>';
$xml .= PHP_EOL . Indent::_(2) . '<sql>';
$xml .= PHP_EOL . Indent::_(3) . '<file driver="mysql" charset="utf8">sql/mysql/uninstall.sql</file>';
$xml .= PHP_EOL . Indent::_(2) . '</sql>';
$xml .= PHP_EOL . Indent::_(1) . '</uninstall>';
}
return $xml;
}
/**
* Generate XML for language files.
*
* @param object $plugin The plugin object.
* @param array $languageFiles The language files.
*
* @return string The XML for language files.
* @since 5.0.2
*/
protected function generateLanguageXml(object $plugin, array $languageFiles): string
{
$xml = '';
if (ArrayHelper::check($languageFiles))
{
$xml .= PHP_EOL . PHP_EOL . Indent::_(1) . '<!--' . Line::_(
__LINE__,__CLASS__
) . ' Language files -->';
$xml .= PHP_EOL . Indent::_(1) . '<languages folder="language">';
foreach ($languageFiles as $addTag)
{
$xml .= PHP_EOL . Indent::_(2) . '<language tag="'
. $addTag . '">' . $addTag . '/plg_'
. strtolower((string) $plugin->group) . '_' .
(string) $plugin->file_name
. '.ini</language>';
$xml .= PHP_EOL . Indent::_(2) . '<language tag="'
. $addTag . '">' . $addTag . '/plg_'
. strtolower((string) $plugin->group) . '_' .
(string) $plugin->file_name
. '.sys.ini</language>';
}
$xml .= PHP_EOL . Indent::_(1) . '</languages>';
}
return $xml;
}
/**
* Generate the XML for the files.
*
* @param object $plugin The plugin object.
* @param array $languageFiles The language files.
*
* @return string The XML for the files.
* @since 5.0.2
*/
protected function generateFileXml(object $plugin, array $languageFiles): string
{
$files = Folder::files($plugin->folder_path);
$folders = Folder::folders($plugin->folder_path);
$ignore = ['sql', 'language', 'script.php', "{$plugin->file_name}.xml"];
$xml = PHP_EOL . PHP_EOL . Indent::_(1) . '<!--' . Line::_(
__LINE__,__CLASS__
) . ' Plugin files -->';
$xml .= PHP_EOL . Indent::_(1) . '<files>';
$xml .= PHP_EOL . Indent::_(2) . "<folder plugin=\"{$plugin->context_name}\">services</folder>";
foreach ($files as $file)
{
if (!in_array($file, $ignore))
{
$xml .= PHP_EOL . Indent::_(2) . "<filename>{$file}</filename>";
}
}
if (!empty($languageFiles))
{
// $xml .= PHP_EOL . Indent::_(2) . '<folder>language</folder>';
}
if ($plugin->add_sql || $plugin->add_sql_uninstall)
{
$xml .= PHP_EOL . Indent::_(2) . '<folder>sql</folder>';
}
foreach ($folders as $folder)
{
if (!in_array($folder, $ignore))
{
$xml .= PHP_EOL . Indent::_(2) . "<folder>{$folder}</folder>";
}
}
$xml .= PHP_EOL . Indent::_(1) . '</files>';
return $xml;
}
/**
* Generate XML for configuration fields.
*
* @param object $plugin The plugin object.
* @param array $configFields The configuration fields.
* @param bool $addComponentPath Whether to add the component path.
*
* @return string The XML for configuration fields.
* @since 5.0.2
*/
protected function generateConfigXml(object $plugin, array $configFields, bool $addComponentPath): string
{
if (!isset($plugin->config_fields) || !ArrayHelper::check($configFields))
{
return '';
}
$xml = PHP_EOL . PHP_EOL . Indent::_(1) . '<!--' . Line::_(
__LINE__,__CLASS__
) . ' Config parameters -->';
$xml .= $addComponentPath ? PHP_EOL . Indent::_(1) . '<config' : PHP_EOL . Indent::_(1) . '<config>';
if ($addComponentPath)
{
$xml .= PHP_EOL . Indent::_(3) . 'addruleprefix="' . $this->config->namespace_prefix . '\Component\\' . $this->contentone->get('ComponentNamespace') . '\Administrator\Rule"';
$xml .= PHP_EOL . Indent::_(3) . 'addfieldprefix="' . $this->config->namespace_prefix . '\Component\\' . $this->contentone->get('ComponentNamespace') . '\Administrator\Field">';
$xml .= PHP_EOL . Indent::_(1) . '>';
}
foreach ($plugin->config_fields as $fieldName => $fieldsets)
{
$xml .= PHP_EOL . Indent::_(1) . "<fields name=\"{$fieldName}\">";
foreach ($fieldsets as $fieldset => $fields)
{
$label = $plugin->fieldsets_label["{$fieldName}{$fieldset}"] ?? $fieldset;
$xml .= PHP_EOL . Indent::_(1) . "<fieldset name=\"{$fieldset}\" label=\"{$label}\">";
if (isset($configFields["{$fieldName}{$fieldset}"]))
{
$xml .= $configFields["{$fieldName}{$fieldset}"];
}
$xml .= PHP_EOL . Indent::_(1) . '</fieldset>';
}
$xml .= PHP_EOL . Indent::_(1) . '</fields>';
}
$xml .= PHP_EOL . Indent::_(1) . '</config>';
return $xml;
}
/**
* Generate XML for update servers.
*
* @param object $plugin The plugin object.
*
* @return string The XML for update servers.
* @since 5.0.2
*/
protected function generateUpdateServerXml(object $plugin): string
{
$xml = '';
if ($plugin->add_update_server)
{
$xml = PHP_EOL . PHP_EOL . Indent::_(1) . '<!--' . Line::_(
__LINE__,__CLASS__
) . ' Update servers -->';
$xml .= PHP_EOL . Indent::_(1) . '<updateservers>';
$xml .= PHP_EOL . Indent::_(2) . '<server type="extension" priority="1" name="' . $plugin->official_name . '">' . $plugin->update_server_url . '</server>';
$xml .= PHP_EOL . Indent::_(1) . '</updateservers>';
}
return $xml;
}
/**
* Generate language files.
*
* @param object $plugin The plugin object.
*
* @return array The language files.
* @since 5.0.2
*/
protected function generateLanguageFiles(object $plugin): array
{
$languageFiles = [];
if (!$this->language->exist($plugin->key))
{
return $languageFiles;
}
$langContent = $this->language->getTarget($plugin->key);
$this->event->trigger('jcb_ce_onBeforeBuildPluginLang', [&$plugin, &$langContent]);
$values = array_unique($langContent);
$this->buildermultilingual->set('plugins', $this->multilingual->get($values));
$langTag = $this->config->get('lang_tag', 'en-GB');
$this->languages->set("plugins.{$langTag}.all", $langContent);
$this->language->setTarget($plugin->key, null);
$this->set->execute($values, $plugin->id, 'plugins');
$this->purge->execute($values, $plugin->id, 'plugins');
$this->event->trigger('jcb_ce_onBeforeBuildPluginLangFiles', [&$plugin]);
if ($this->languages->IsArray('plugins'))
{
foreach ($this->languages->get('plugins') as $tag => $areas)
{
$tag = trim($tag);
foreach ($areas as $area => $languageStrings)
{
$fileName = "plg_" . strtolower((string)$plugin->group) . '_' . $plugin->file_name . '.ini';
$total = count($values);
if ($this->translation->check($tag, $languageStrings, $total, $fileName))
{
$lang = array_map(
fn($langString, $placeholder) => "{$placeholder}=\"{$langString}\"",
array_values($languageStrings),
array_keys($languageStrings)
);
$path = "{$plugin->folder_path}/language/{$tag}/";
if (!Folder::exists($path))
{
Folder::create($path);
$this->counter->folder++;
}
$this->file->write($path . $fileName, implode(PHP_EOL, $lang));
$this->file->write(
$path . 'plg_' . strtolower((string)$plugin->group) . '_' . $plugin->file_name . '.sys.ini',
implode(PHP_EOL, $lang)
);
$this->counter->line += count($lang);
unset($lang);
$languageFiles[$tag] = $tag;
}
}
}
}
return $languageFiles;
}
}

View File

@@ -0,0 +1,106 @@
<?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\Architecture\JoomlaFour\Plugin;
use VDM\Joomla\Componentbuilder\Compiler\Placeholder;
use VDM\Joomla\Componentbuilder\Compiler\Builder\ContentOne as Builder;
use VDM\Joomla\Componentbuilder\Compiler\Utilities\Indent;
use VDM\Joomla\Componentbuilder\Compiler\Utilities\Line;
use VDM\Joomla\Componentbuilder\Compiler\Interfaces\Architecture\Plugin\ProviderInterface;
/**
* Plugin Provider Class for Joomla 4
*
* @since 5.0.2
*/
final class Provider implements ProviderInterface
{
/**
* The Placeholder Class.
*
* @var Placeholder
* @since 5.0.2
*/
protected Placeholder $placeholder;
/**
* The ContentOne Class.
*
* @var Builder
* @since 5.0.2
*/
protected Builder $builder;
/**
* Constructor.
*
* @param Placeholder $placeholder The Placeholder Class.
* @param Builder $builder The Content One Class.
*
* @since 5.0.2
*/
public function __construct(Placeholder $placeholder, Builder $builder)
{
$this->placeholder = $placeholder;
$this->builder = $builder;
}
/**
* Get the updated provider for the given plugin.
*
* @param object $plugin The plugin object containing the necessary data.
*
* @return string The provider content.
*
* @since 5.0.2
*/
public function get(object $plugin): string
{
$group = strtolower((string) $plugin->group);
$provider = [];
$provider[] = PHP_EOL . PHP_EOL . "return new class () implements ServiceProviderInterface {";
$provider[] = Indent::_(1) . "/**";
$provider[] = Indent::_(1) . "*" . Line::_(__Line__, __Class__)
. " Registers the service provider with a DI container.";
$provider[] = Indent::_(1) . "*";
$provider[] = Indent::_(1) . "* @param Container \$container The DI container.";
$provider[] = Indent::_(1) . "*";
$provider[] = Indent::_(1) . "* @return void";
$provider[] = Indent::_(1) . "* @since 4.3.0";
$provider[] = Indent::_(1) . "*/";
$provider[] = Indent::_(1) . "public function register(Container \$container)";
$provider[] = Indent::_(1) . "{";
$provider[] = Indent::_(2) . "\$container->set(";
$provider[] = Indent::_(3) . "PluginInterface::class,";
$provider[] = Indent::_(3) . "function (Container \$container) {";
$provider[] = Indent::_(4) . "\$plugin = new {$plugin->class_name}(";
$provider[] = Indent::_(5) . "\$container->get(DispatcherInterface::class),";
$provider[] = Indent::_(5) . "(array) PluginHelper::getPlugin('{$group}', '{$plugin->context_name}')";
$provider[] = Indent::_(4) . ");";
$provider[] = Indent::_(4) . "\$plugin->setApplication(Factory::getApplication());";
$provider[] = $plugin->service_provider ?? ''; // to add extra plug-in suff
$provider[] = Indent::_(4) . "return \$plugin;";
$provider[] = Indent::_(3) . "}";
$provider[] = Indent::_(2) . ");";
$provider[] = Indent::_(1) . "}";
$provider[] = "};";
return $this->placeholder->update(
implode(PHP_EOL, $provider). PHP_EOL,
$this->builder->allActive()
);
}
}

View File

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

View File

@@ -0,0 +1,78 @@
<?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\Architecture\JoomlaThree\Plugin;
use VDM\Joomla\Componentbuilder\Compiler\Placeholder;
use VDM\Joomla\Componentbuilder\Compiler\Builder\ContentOne as Builder;
use VDM\Joomla\Componentbuilder\Compiler\Interfaces\Architecture\Plugin\ExtensionInterface;
/**
* Plugin Extension Class for Joomla 3
*
* @since 5.0.2
*/
final class Extension implements ExtensionInterface
{
/**
* The Placeholder Class.
*
* @var Placeholder
* @since 5.0.2
*/
protected Placeholder $placeholder;
/**
* The ContentOne Class.
*
* @var Builder
* @since 5.0.2
*/
protected Builder $builder;
/**
* Constructor.
*
* @param Placeholder $placeholder The Placeholder Class.
* @param Builder $builder The Content One Class.
*
* @since 5.0.2
*/
public function __construct(Placeholder $placeholder, Builder $builder)
{
$this->placeholder = $placeholder;
$this->builder = $builder;
}
/**
* Get the updated placeholder content for the given plugin.
*
* @param object $plugin The plugin object containing the necessary data.
*
* @return string The updated placeholder content.
*
* @since 5.0.2
*/
public function get(object $plugin): string
{
return $this->placeholder->update(
$plugin->comment . PHP_EOL . 'class ' .
$plugin->class_name . ' extends ' .
$plugin->extends . PHP_EOL . '{' . PHP_EOL .
$plugin->main_class_code . PHP_EOL .
"}" . PHP_EOL,
$this->builder->allActive()
);
}
}

View File

@@ -0,0 +1,570 @@
<?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\Architecture\JoomlaThree\Plugin;
use Joomla\CMS\Filesystem\Folder;
use VDM\Joomla\Componentbuilder\Compiler\Config;
use VDM\Joomla\Componentbuilder\Compiler\Language;
use VDM\Joomla\Componentbuilder\Compiler\Language\Set;
use VDM\Joomla\Componentbuilder\Compiler\Language\Purge;
use VDM\Joomla\Componentbuilder\Compiler\Language\Translation;
use VDM\Joomla\Componentbuilder\Compiler\Language\Multilingual;
use VDM\Joomla\Componentbuilder\Compiler\Interfaces\EventInterface as Event;
use VDM\Joomla\Componentbuilder\Compiler\Creator\FieldsetExtension;
use VDM\Joomla\Componentbuilder\Compiler\Builder\ContentOne;
use VDM\Joomla\Componentbuilder\Compiler\Builder\Languages;
use VDM\Joomla\Componentbuilder\Compiler\Builder\Multilingual as BuilderMultilingual;
use VDM\Joomla\Componentbuilder\Compiler\Utilities\Counter;
use VDM\Joomla\Componentbuilder\Compiler\Utilities\File;
use VDM\Joomla\Componentbuilder\Compiler\Utilities\Line;
use VDM\Joomla\Componentbuilder\Compiler\Utilities\Indent;
use VDM\Joomla\Utilities\ArrayHelper;
use VDM\Joomla\Utilities\StringHelper;
use VDM\Joomla\Componentbuilder\Interfaces\Architecture\Plugin\MainXMLInterface;
/**
* Joomla 3 Plugin Main XML Class
*
* @since 5.0.2
*/
final class MainXML implements MainXMLInterface
{
/**
* The Config Class.
*
* @var Config
* @since 5.0.2
*/
protected Config $config;
/**
* The Language Class.
*
* @var Language
* @since 5.0.2
*/
protected Language $language;
/**
* The Set Class.
*
* @var Set
* @since 5.0.2
*/
protected Set $set;
/**
* The Purge Class.
*
* @var Purge
* @since 5.0.2
*/
protected Purge $purge;
/**
* The Translation Class.
*
* @var Translation
* @since 5.0.2
*/
protected Translation $translation;
/**
* The Multilingual Class.
*
* @var Multilingual
* @since 5.0.2
*/
protected Multilingual $multilingual;
/**
* The EventInterface Class.
*
* @var Event
* @since 5.0.2
*/
protected Event $event;
/**
* The FieldsetExtension Class.
*
* @var FieldsetExtension
* @since 5.0.2
*/
protected FieldsetExtension $fieldsetextension;
/**
* The ContentOne Class.
*
* @var ContentOne
* @since 5.0.2
*/
protected ContentOne $contentone;
/**
* The Languages Class.
*
* @var Languages
* @since 5.0.2
*/
protected Languages $languages;
/**
* The Multilingual Class.
*
* @var BuilderMultilingual
* @since 5.0.2
*/
protected BuilderMultilingual $buildermultilingual;
/**
* The Counter Class.
*
* @var Counter
* @since 5.0.2
*/
protected Counter $counter;
/**
* The File Class.
*
* @var File
* @since 5.0.2
*/
protected File $file;
/**
* Constructor.
*
* @param Config $config The Config Class.
* @param Language $language The Language Class.
* @param Set $set The Set Class.
* @param Purge $purge The Purge Class.
* @param Translation $translation The Translation Class.
* @param Multilingual $multilingual The Multilingual Class.
* @param Event $event The EventInterface Class.
* @param FieldsetExtension $fieldsetextension The FieldsetExtension Class.
* @param ContentOne $contentone The ContentOne Class.
* @param Languages $languages The Languages Class.
* @param BuilderMultilingual $buildermultilingual The Multilingual Class.
* @param Counter $counter The Counter Class.
* @param File $file The File Class.
*
* @since 5.0.2
*/
public function __construct(Config $config, Language $language, Set $set, Purge $purge,
Translation $translation, Multilingual $multilingual,
Event $event, FieldsetExtension $fieldsetextension,
ContentOne $contentone, Languages $languages,
BuilderMultilingual $buildermultilingual,
Counter $counter, File $file)
{
$this->config = $config;
$this->language = $language;
$this->set = $set;
$this->purge = $purge;
$this->translation = $translation;
$this->multilingual = $multilingual;
$this->event = $event;
$this->fieldsetextension = $fieldsetextension;
$this->contentone = $contentone;
$this->languages = $languages;
$this->buildermultilingual = $buildermultilingual;
$this->counter = $counter;
$this->file = $file;
}
/**
* Generates the main XML for the plugin.
*
* @param object $plugin The plugin object.
*
* @return string The generated XML.
* @since 5.0.2
*/
public function get(object $plugin): string
{
$config_fields = $this->buildConfigFields($plugin);
$add_component_path = $this->shouldAddComponentPath($plugin);
$language_files = $this->generateLanguageFiles($plugin);
$xml = $this->generateScriptAndSqlXml($plugin);
$xml .= $this->generateLanguageXml($plugin, $language_files);
$xml .= $this->generateFileXml($plugin, $language_files);
$xml .= $this->generateConfigXml($plugin, $config_fields, $add_component_path);
$xml .= $this->generateUpdateServerXml($plugin);
return $xml;
}
/**
* Build configuration fields XML.
*
* @param object $plugin The plugin object.
*
* @return array The configuration fields.
* @since 5.0.2
*/
protected function buildConfigFields(object $plugin): array
{
$configFields = [];
if (!isset($plugin->config_fields) || !ArrayHelper::check($plugin->config_fields))
{
return $configFields;
}
$dbKey = 'yy';
foreach ($plugin->config_fields as $fieldName => $fieldsets)
{
foreach ($fieldsets as $fieldset => $fields)
{
$xmlFields = $this->fieldsetextension->get($plugin, $fields, $dbKey);
if (isset($xmlFields) && StringHelper::check($xmlFields))
{
$configFields["{$fieldName}{$fieldset}"] = $xmlFields;
}
$dbKey++;
}
}
return $configFields;
}
/**
* Determine if the component path should be added.
*
* @param object $plugin The plugin object.
*
* @return bool True if the component path should be added, false otherwise.
* @since 5.0.2
*/
protected function shouldAddComponentPath(object $plugin): bool
{
if (!isset($plugin->config_fields) || !ArrayHelper::check($plugin->config_fields) ||
!isset($plugin->fieldsets_paths) || !ArrayHelper::check($plugin->fieldsets_paths))
{
return false;
}
foreach ($plugin->config_fields as $fieldName => $fieldsets)
{
foreach ($fieldsets as $fieldset => $fields)
{
if (isset($plugin->fieldsets_paths["{$fieldName}{$fieldset}"]) &&
$plugin->fieldsets_paths["{$fieldName}{$fieldset}"] == 1)
{
return true;
}
}
}
return false;
}
/**
* Generate XML for script and SQL files.
*
* @param object $plugin The plugin object.
*
* @return string The XML for script and SQL files.
* @since 5.0.2
*/
protected function generateScriptAndSqlXml(object $plugin): string
{
$xml = '';
if ($plugin->add_install_script)
{
$xml .= PHP_EOL . PHP_EOL . Indent::_(1) . '<!--' . Line::_(
__LINE__,__CLASS__
) . ' Scripts to run on installation -->';
$xml .= PHP_EOL . Indent::_(1) . '<scriptfile>script.php</scriptfile>';
}
if ($plugin->add_sql)
{
$xml .= PHP_EOL . PHP_EOL . Indent::_(1) . '<!--' . Line::_(
__LINE__,__CLASS__
) . ' Runs on install -->';
$xml .= PHP_EOL . Indent::_(1) . '<install>';
$xml .= PHP_EOL . Indent::_(2) . '<sql>';
$xml .= PHP_EOL . Indent::_(3) . '<file driver="mysql" charset="utf8">sql/mysql/install.sql</file>';
$xml .= PHP_EOL . Indent::_(2) . '</sql>';
$xml .= PHP_EOL . Indent::_(1) . '</install>';
}
if ($plugin->add_sql_uninstall)
{
$xml .= PHP_EOL . PHP_EOL . Indent::_(1) . '<!--' . Line::_(
__LINE__,__CLASS__
) . ' Runs on uninstall -->';
$xml .= PHP_EOL . Indent::_(1) . '<uninstall>';
$xml .= PHP_EOL . Indent::_(2) . '<sql>';
$xml .= PHP_EOL . Indent::_(3) . '<file driver="mysql" charset="utf8">sql/mysql/uninstall.sql</file>';
$xml .= PHP_EOL . Indent::_(2) . '</sql>';
$xml .= PHP_EOL . Indent::_(1) . '</uninstall>';
}
return $xml;
}
/**
* Generate XML for language files.
*
* @param object $plugin The plugin object.
* @param array $languageFiles The language files.
*
* @return string The XML for language files.
* @since 5.0.2
*/
protected function generateLanguageXml(object $plugin, array $languageFiles): string
{
$xml = '';
if (ArrayHelper::check($languageFiles))
{
$xml .= PHP_EOL . PHP_EOL . Indent::_(1) . '<!--' . Line::_(
__LINE__,__CLASS__
) . ' Language files -->';
$xml .= PHP_EOL . Indent::_(1) . '<languages folder="language">';
foreach ($languageFiles as $addTag)
{
$xml .= PHP_EOL . Indent::_(2) . '<language tag="'
. $addTag . '">' . $addTag . '/' . $addTag . '.plg_'
. strtolower((string) $plugin->group) . '_' . strtolower(
(string) $plugin->code_name
) . '.ini</language>';
$xml .= PHP_EOL . Indent::_(2) . '<language tag="'
. $addTag . '">' . $addTag . '/' . $addTag . '.plg_'
. strtolower((string) $plugin->group) . '_' . strtolower(
(string) $plugin->code_name
) . '.sys.ini</language>';
}
$xml .= PHP_EOL . Indent::_(1) . '</languages>';
}
return $xml;
}
/**
* Generate the XML for the files.
*
* @param object $plugin The plugin object.
* @param array $languageFiles The language files.
*
* @return string The XML for the files.
* @since 5.0.2
*/
protected function generateFileXml(object $plugin, array $languageFiles): string
{
$files = Folder::files($plugin->folder_path);
$folders = Folder::folders($plugin->folder_path);
$ignore = ['sql', 'language', 'script.php', "{$plugin->file_name}.xml", "{$plugin->file_name}.php"];
$xml = PHP_EOL . PHP_EOL . Indent::_(1) . '<!--' . Line::_(
__LINE__,__CLASS__
) . ' Plugin files -->';
$xml .= PHP_EOL . Indent::_(1) . '<files>';
$xml .= PHP_EOL . Indent::_(2) . "<filename plugin=\"{$plugin->file_name}\">{$plugin->file_name}.php</filename>";
foreach ($files as $file)
{
if (!in_array($file, $ignore))
{
$xml .= PHP_EOL . Indent::_(2) . "<filename>{$file}</filename>";
}
}
if (!empty($languageFiles))
{
$xml .= PHP_EOL . Indent::_(2) . '<folder>language</folder>';
}
if ($plugin->add_sql || $plugin->add_sql_uninstall)
{
$xml .= PHP_EOL . Indent::_(2) . '<folder>sql</folder>';
}
foreach ($folders as $folder)
{
if (!in_array($folder, $ignore))
{
$xml .= PHP_EOL . Indent::_(2) . "<folder>{$folder}</folder>";
}
}
$xml .= PHP_EOL . Indent::_(1) . '</files>';
return $xml;
}
/**
* Generate XML for configuration fields.
*
* @param object $plugin The plugin object.
* @param array $configFields The configuration fields.
* @param bool $addComponentPath Whether to add the component path.
*
* @return string The XML for configuration fields.
* @since 5.0.2
*/
protected function generateConfigXml(object $plugin, array $configFields, bool $addComponentPath): string
{
if (!isset($plugin->config_fields) || !ArrayHelper::check($configFields))
{
return '';
}
$xml = PHP_EOL . PHP_EOL . Indent::_(1) . '<!--' . Line::_(
__LINE__,__CLASS__
) . ' Config parameters -->';
$xml .= $addComponentPath ? PHP_EOL . Indent::_(1) . '<config' : PHP_EOL . Indent::_(1) . '<config>';
if ($addComponentPath)
{
$xml .= PHP_EOL . Indent::_(2) . 'addrulepath="/administrator/components/com_' . $this->config->component_code_name . '/models/rules"';
$xml .= PHP_EOL . Indent::_(2) . 'addfieldpath="/administrator/components/com_' . $this->config->component_code_name . '/models/fields"';
$xml .= PHP_EOL . Indent::_(1) . '>';
}
foreach ($plugin->config_fields as $fieldName => $fieldsets)
{
$xml .= PHP_EOL . Indent::_(1) . "<fields name=\"{$fieldName}\">";
foreach ($fieldsets as $fieldset => $fields)
{
$label = $plugin->fieldsets_label["{$fieldName}{$fieldset}"] ?? $fieldset;
$xml .= PHP_EOL . Indent::_(1) . "<fieldset name=\"{$fieldset}\" label=\"{$label}\">";
if (isset($configFields["{$fieldName}{$fieldset}"]))
{
$xml .= $configFields["{$fieldName}{$fieldset}"];
}
$xml .= PHP_EOL . Indent::_(1) . '</fieldset>';
}
$xml .= PHP_EOL . Indent::_(1) . '</fields>';
}
$xml .= PHP_EOL . Indent::_(1) . '</config>';
return $xml;
}
/**
* Generate XML for update servers.
*
* @param object $plugin The plugin object.
*
* @return string The XML for update servers.
* @since 5.0.2
*/
protected function generateUpdateServerXml(object $plugin): string
{
$xml = '';
if ($plugin->add_update_server)
{
$xml = PHP_EOL . PHP_EOL . Indent::_(1) . '<!--' . Line::_(
__LINE__,__CLASS__
) . ' Update servers -->';
$xml .= PHP_EOL . Indent::_(1) . '<updateservers>';
$xml .= PHP_EOL . Indent::_(2) . '<server type="extension" priority="1" name="' . $plugin->official_name . '">' . $plugin->update_server_url . '</server>';
$xml .= PHP_EOL . Indent::_(1) . '</updateservers>';
}
return $xml;
}
/**
* Generate language files.
*
* @param object $plugin The plugin object.
*
* @return array The language files.
* @since 5.0.2
*/
protected function generateLanguageFiles(object $plugin): array
{
$languageFiles = [];
if (!$this->language->exist($plugin->key))
{
return $languageFiles;
}
$langContent = $this->language->getTarget($plugin->key);
$this->event->trigger('jcb_ce_onBeforeBuildPluginLang', [&$plugin, &$langContent]);
$values = array_unique($langContent);
$this->buildermultilingual->set('plugins', $this->multilingual->get($values));
$langTag = $this->config->get('lang_tag', 'en-GB');
$this->languages->set("plugins.{$langTag}.all", $langContent);
$this->language->setTarget($plugin->key, null);
$this->set->execute($values, $plugin->id, 'plugins');
$this->purge->execute($values, $plugin->id, 'plugins');
$this->event->trigger('jcb_ce_onBeforeBuildPluginLangFiles', [&$plugin]);
if ($this->languages->IsArray('plugins'))
{
foreach ($this->languages->get('plugins') as $tag => $areas)
{
$tag = trim($tag);
foreach ($areas as $area => $languageStrings)
{
$fileName = "{$tag}.plg_" . strtolower((string)$plugin->group) . '_' . strtolower((string)$plugin->code_name) . '.ini';
$total = count($values);
if ($this->translation->check($tag, $languageStrings, $total, $fileName))
{
$lang = array_map(
fn($langString, $placeholder) => "{$placeholder}=\"{$langString}\"",
array_values($languageStrings),
array_keys($languageStrings)
);
$path = "{$plugin->folder_path}/language/{$tag}/";
if (!Folder::exists($path))
{
Folder::create($path);
$this->counter->folder++;
}
$this->file->write($path . $fileName, implode(PHP_EOL, $lang));
$this->file->write(
$path . $tag . '.plg_' . strtolower((string)$plugin->group) . '_' . strtolower((string)$plugin->code_name) . '.sys.ini',
implode(PHP_EOL, $lang)
);
$this->counter->line += count($lang);
unset($lang);
$languageFiles[$tag] = $tag;
}
}
}
}
return $languageFiles;
}
}

View File

@@ -0,0 +1,71 @@
<?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\Architecture\JoomlaThree\Plugin;
use VDM\Joomla\Componentbuilder\Compiler\Placeholder;
use VDM\Joomla\Componentbuilder\Compiler\Builder\ContentOne as Builder;
use VDM\Joomla\Componentbuilder\Compiler\Interfaces\Architecture\Plugin\ProviderInterface;
/**
* Plugin Provider Class for Joomla 3
*
* @since 5.0.2
*/
final class Provider implements ProviderInterface
{
/**
* The Placeholder Class.
*
* @var Placeholder
* @since 5.0.2
*/
protected Placeholder $placeholder;
/**
* The ContentOne Class.
*
* @var Builder
* @since 5.0.2
*/
protected Builder $builder;
/**
* Constructor.
*
* @param Placeholder $placeholder The Placeholder Class.
* @param Builder $builder The Content One Class.
*
* @since 5.0.2
*/
public function __construct(Placeholder $placeholder, Builder $builder)
{
$this->placeholder = $placeholder;
$this->builder = $builder;
}
/**
* Get the updated provider for the given plugin.
*
* @param object $plugin The plugin object containing the necessary data.
*
* @return string The provider content.
*
* @since 5.0.2
*/
public function get(object $plugin): string
{
return ''; // no provider in Joomla 3
}
}

View File

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