jcb-compiler/src/673954d3-78d3-445a-b99f-4b4.../code.php

647 lines
15 KiB
PHP

<?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\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 VDM\Joomla\Componentbuilder\Compiler\Config;
use VDM\Joomla\Componentbuilder\Compiler\Registry;
use VDM\Joomla\Componentbuilder\Compiler\Placeholder;
use VDM\Joomla\Componentbuilder\Compiler\Interfaces\Component\SettingsInterface as Settings;
use VDM\Joomla\Componentbuilder\Compiler\Component;
use VDM\Joomla\Componentbuilder\Compiler\Builder\ContentOne as Content;
use VDM\Joomla\Componentbuilder\Compiler\Utilities\Counter;
use VDM\Joomla\Componentbuilder\Compiler\Utilities\Paths;
use VDM\Joomla\Componentbuilder\Compiler\Utilities\Files;
use VDM\Joomla\Utilities\StringHelper;
use VDM\Joomla\Componentbuilder\Compiler\Utilities\Indent;
/**
* Single Files and Folders Builder Class
*
* @since 3.2.0
*/
final class Structuresingle
{
/**
* The new name
*
* @var string
* @since 3.2.0
*/
protected string $newName;
/**
* Current Full Path
*
* @var string
* @since 3.2.0
*/
protected string $currentFullPath;
/**
* Package Full Path
*
* @var string
* @since 3.2.0
*/
protected string $packageFullPath;
/**
* ZIP Full Path
*
* @var string
* @since 3.2.0
*/
protected string $zipFullPath;
/**
* The Config Class.
*
* @var Config
* @since 3.2.0
*/
protected Config $config;
/**
* The Registry Class.
*
* @var Registry
* @since 3.2.0
*/
protected Registry $registry;
/**
* The Placeholder Class.
*
* @var Placeholder
* @since 3.2.0
*/
protected Placeholder $placeholder;
/**
* The SettingsInterface Class.
*
* @var Settings
* @since 3.2.0
*/
protected Settings $settings;
/**
* The Component Class.
*
* @var Component
* @since 3.2.0
*/
protected Component $component;
/**
* The ContentOne Class.
*
* @var Content
* @since 3.2.0
*/
protected Content $content;
/**
* The Counter Class.
*
* @var Counter
* @since 3.2.0
*/
protected Counter $counter;
/**
* The Paths Class.
*
* @var Paths
* @since 3.2.0
*/
protected Paths $paths;
/**
* The Files Class.
*
* @var Files
* @since 3.2.0
*/
protected Files $files;
/**
* Application object.
*
* @var CMSApplication
* @since 3.2.0
**/
protected CMSApplication $app;
/**
* Constructor.
*
* @param Config $config The Config Class.
* @param Registry $registry The Registry Class.
* @param Placeholder $placeholder The Placeholder Class.
* @param Settings $settings The SettingsInterface Class.
* @param Component $component The Component Class.
* @param Content $content The ContentOne Class.
* @param Counter $counter The Counter Class.
* @param Paths $paths The Paths Class.
* @param Files $files The Files Class.
* @param CMSApplication|null $app The CMS Application object.
*
* @since 3.2.0
*/
public function __construct(Config $config, Registry $registry,
Placeholder $placeholder, Settings $settings,
Component $component, Content $content, Counter $counter,
Paths $paths, Files $files, ?CMSApplication $app = null)
{
$this->config = $config;
$this->registry = $registry;
$this->placeholder = $placeholder;
$this->settings = $settings;
$this->component = $component;
$this->content = $content;
$this->counter = $counter;
$this->paths = $paths;
$this->files = $files;
$this->app = $app ?: Factory::getApplication();
}
/**
* Build the Single Files & Folders
*
* @return bool
* @since 3.2.0
*/
public function build(): bool
{
if ($this->settings->exists())
{
// TODO needs more looking at this must be dynamic actually
$this->registry->appendArray('files.not.new', 'LICENSE.txt');
// do license check
$LICENSE = $this->doLicenseCheck();
// do README check
$README = $this->doReadmeCheck();
// do CHANGELOG check
$CHANGELOG = $this->doChangelogCheck();
// start moving
foreach ($this->settings->single() as $target => $details)
{
// if not gnu/gpl license dont add the LICENSE.txt file
if ($details->naam === 'LICENSE.txt' && !$LICENSE)
{
continue;
}
// if not needed do not add
if (($details->naam === 'README.md' || $details->naam === 'README.txt')
&& !$README)
{
continue;
}
// if not needed do not add
if ($details->naam === 'CHANGELOG.md' && !$CHANGELOG)
{
continue;
}
// set new name
$this->setNewName($details);
// set all paths
$this->setPaths($details);
// check if the path exists
if ($this->pathExist($details))
{
// set the target
$this->setTarget($target, $details);
}
// set dynamic target as needed
$this->setDynamicTarget($details);
}
return true;
}
return false;
}
/**
* Check if license must be added
*
* @return bool
* @since 3.2.0
*/
private function doLicenseCheck(): bool
{
$licenseChecker = strtolower((string) $this->component->get('license', ''));
if (strpos($licenseChecker, 'gnu') !== false
&& strpos(
$licenseChecker, '2'
) !== false
&& (strpos($licenseChecker, 'gpl') !== false
|| strpos(
$licenseChecker, 'general public license'
) !== false))
{
return true;
}
return false;
}
/**
* Check if readme must be added
*
* @return bool
* @since 3.2.0
*/
private function doReadmeCheck(): bool
{
return (bool) $this->component->get('addreadme', false);
}
/**
* Check if changelog must be added
*
* @return bool
* @since 3.2.0
*/
private function doChangelogCheck(): bool
{
return (bool) $this->component->get('changelog', false);
}
/**
* Set the new name
*
* @param object $details
*
* @return void
* @since 3.2.0
*/
private function setNewName(object $details)
{
// do the file renaming
if (isset($details->rename) && $details->rename)
{
if ($details->rename === 'new')
{
$newName = $details->newName;
}
else
{
$naam = $details->naam ?? 'error';
$newName = str_replace(
$details->rename,
$this->config->component_code_name,
(string) $naam
);
}
}
else
{
$newName = $details->naam ?? 'error';
}
$this->newName = $this->placeholder->update_($newName);
}
/**
* Set all needed paths
*
* @param object $details
*
* @return void
* @since 3.2.0
*/
private function setPaths(object $details)
{
// check if we have a target value
if (isset($details->_target))
{
// set destination path
$zipPath = str_replace(
$details->_target['type'] . '/', '', (string) $details->path
);
$path = str_replace(
$details->_target['type'] . '/',
$this->registry->get('dynamic_paths.' . $details->_target['key'], '') . '/',
(string) $details->path
);
}
else
{
// set destination path
$zipPath = str_replace('c0mp0n3nt/', '', (string) $details->path);
$path = str_replace(
'c0mp0n3nt/', $this->paths->component_path . '/', (string) $details->path
);
}
// set the template folder path
$templatePath = (isset($details->custom) && $details->custom)
? (($details->custom !== 'full') ? $this->paths->template_path_custom
. '/' : '') : $this->paths->template_path . '/';
// set the final paths
$currentFullPath = (preg_match('/^[a-z]:/i', (string) $details->naam)) ? $details->naam
: $templatePath . '/' . $details->naam;
$this->currentFullPath = str_replace('//', '/', (string) $currentFullPath);
$this->packageFullPath = str_replace('//', '/', $path . '/' . $this->newName);
$this->zipFullPath = str_replace(
'//', '/', $zipPath . '/' . $this->newName
);
}
/**
* Check if path exists
*
* @param object $details
*
* @return bool
* @since 3.2.0
*/
private function pathExist(object $details): bool
{
// check if this has a type
if (!isset($details->type))
{
return false;
}
// take action based on type
elseif ($details->type === 'file' && !File::exists($this->currentFullPath))
{
$this->app->enqueueMessage(
Text::_('COM_COMPONENTBUILDER_HR_HTHREEFILE_PATH_ERRORHTHREE'), 'Error'
);
$this->app->enqueueMessage(
Text::sprintf('COM_COMPONENTBUILDER_THE_FILE_PATH_BSB_DOES_NOT_EXIST_AND_WAS_NOT_ADDED',
$this->currentFullPath
), 'Error'
);
return false;
}
elseif ($details->type === 'folder' && !Folder::exists($this->currentFullPath))
{
$this->app->enqueueMessage(
Text::_('COM_COMPONENTBUILDER_HR_HTHREEFOLDER_PATH_ERRORHTHREE'),
'Error'
);
$this->app->enqueueMessage(
Text::sprintf('COM_COMPONENTBUILDER_THE_FOLDER_PATH_BSB_DOES_NOT_EXIST_AND_WAS_NOT_ADDED',
$this->currentFullPath
), 'Error'
);
return false;
}
return true;
}
/**
* Set the target based on target type
*
* @param string $target
* @param object $details
*
* @return void
* @since 3.2.0
*/
private function setTarget(string $target, object $details)
{
// take action based on type
if ($details->type === 'file')
{
// move the file
$this->moveFile();
// register the file
$this->registerFile($target, $details);
}
elseif ($details->type === 'folder')
{
// move the folder to its place
Folder::copy(
$this->currentFullPath, $this->packageFullPath, '', true
);
// count the folder created
$this->counter->folder++;
}
}
/**
* Move/Copy the file into place
*
* @return void
* @since 3.2.0
*/
private function moveFile()
{
// get base name && get the path only
$packageFullPath0nly = str_replace(
basename($this->packageFullPath), '', $this->packageFullPath
);
// check if path exist, if not creat it
if (!Folder::exists($packageFullPath0nly))
{
Folder::create($packageFullPath0nly);
}
// move the file to its place
File::copy($this->currentFullPath, $this->packageFullPath);
// count the file created
$this->counter->file++;
}
/**
* Register the file
*
* @param string $target
* @param object $details
*
* @return void
* @since 3.2.0
*/
private function registerFile(string $target, object $details)
{
// store the new files
if (!in_array($target, $this->registry->get('files.not.new', [])))
{
if (isset($details->_target))
{
$this->files->appendArray($details->_target['key'],
[
'path' => $this->packageFullPath,
'name' => $this->newName,
'zip' => $this->zipFullPath
]
);
}
else
{
$this->files->appendArray('static',
[
'path' => $this->packageFullPath,
'name' => $this->newName,
'zip' => $this->zipFullPath
]
);
}
}
// ensure we update this file if needed
if ($this->registry->exists('update.file.content.' . $target))
{
// remove the pointer
$this->registry->remove('update.file.content.' . $target);
// set the full path
$this->registry->set('update.file.content.' . $this->packageFullPath, $this->packageFullPath);
}
}
/**
* Set Dynamic Target
*
* @param object $details
*
* @return void
* @since 3.2.0
*/
private function setDynamicTarget(object $details)
{
// only add if no target found since those belong to plugins and modules
if (!isset($details->_target))
{
// check if we should add the dynamic folder moving script to the installer script
$checker = array_values((array) explode('/', $this->zipFullPath));
// TODO <-- this may not be the best way, will keep an eye on this.
// We basicly only want to check if a folder is added that is not in the stdFolders array
if (isset($checker[0])
&& StringHelper::check($checker[0])
&& !$this->settings->standardFolder($checker[0]))
{
// activate dynamic folders
$this->setDynamicFolders();
}
elseif (count((array) $checker) == 2
&& StringHelper::check($checker[0]))
{
$add_to_extra = false;
// set the target
$eNAME = 'FILES';
$ename = 'filename';
// this should not happen and must have been caught by the above if statment
if ($details->type === 'folder')
{
// only folders outside the standard folder are added
$eNAME = 'FOLDERS';
$ename = 'folder';
$add_to_extra = true;
}
// if this is a file, it can only be added to the admin/site/media folders
// all other folders are moved as a whole so their files do not need to be declared
elseif ($this->settings->standardFolder($checker[0])
&& !$this->settings->standardRootFile($checker[1]))
{
$add_to_extra = true;
}
// add if valid folder/file
if ($add_to_extra)
{
// set the tab
$eTab = Indent::_(2);
if ('admin' === $checker[0])
{
$eTab = Indent::_(3);
}
// set the xml file
$key_ = 'EXSTRA_'
. StringHelper::safe(
$checker[0], 'U'
) . '_' . $eNAME;
$this->content->add($key_,
PHP_EOL . $eTab . "<" . $ename . ">"
. $checker[1] . "</" . $ename . ">");
}
}
}
}
/**
* Add the dynamic folders
*
* @return void
* @since 3.2.0
*/
private function setDynamicFolders()
{
// check if we should add the dynamic folder moving script to the installer script
if (!$this->registry->get('set_move_folders_install_script'))
{
// add the setDynamicF0ld3rs() method to the install scipt.php file
$this->registry->set('set_move_folders_install_script', true);
$function = 'setDynamicF0ld3rs';
$script = 'script.php';
if ($this->config->get('joomla_version', 3) != 3)
{
$function = 'moveFolders';
$script = 'ComponentnameInstallerScript.php';
}
// set message that this was done (will still add a tutorial link later)
$this->app->enqueueMessage(
Text::_('COM_COMPONENTBUILDER_HR_HTHREEDYNAMIC_FOLDERS_WERE_DETECTEDHTHREE'),
'Notice'
);
$this->app->enqueueMessage(
Text::sprintf('COM_COMPONENTBUILDER_A_METHOD_S_WAS_ADDED_TO_THE_INSTALL_BSB_OF_THIS_PACKAGE_TO_INSURE_THAT_THE_FOLDERS_ARE_COPIED_INTO_THE_CORRECT_PLACE_WHEN_THIS_COMPONENT_IS_INSTALLED',
$function, $script
),
'Notice'
);
}
}
}