Release of v5.0.0-alpha8

Add power path override option on component level. Fix the sql build feature. #1032.
This commit is contained in:
2024-04-06 23:41:34 +02:00
parent 2f64eec95b
commit 2b7b8f90e1
762 changed files with 1900 additions and 1246 deletions

View File

@@ -0,0 +1,409 @@
<?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\Utilities;
use VDM\Joomla\Componentbuilder\Compiler\Builder\ContentOne as Content;
/**
* Compiler Utilities Counter
*
* @since 3.2.0
*/
class Counter
{
/**
* The folder counter
*
* @var int
* @since 3.2.0
*/
public int $folder = 0;
/**
* The file counter
*
* @var int
* @since 3.2.0
*/
public int $file = 0;
/**
* The page counter
*
* @var int
* @since 3.2.0
*/
public int $page = 0;
/**
* The line counter
*
* @var int
* @since 3.2.0
*/
public int $line = 0;
/**
* The field counter
*
* @var int
* @since 3.2.0
*/
public int $field = 0;
/**
* The access size
*
* @var int
* @since 3.2.0
*/
public int $accessSize = 0;
/**
* The seconds counter
*
* @var int
* @since 3.2.0
*/
protected int $seconds = 0;
/**
* The actual seconds counter
*
* @var float
* @since 3.2.0
*/
protected float $actualSeconds = 0;
/**
* The folder seconds counter
*
* @var int
* @since 3.2.0
*/
protected int $folderSeconds = 0;
/**
* The file seconds counter
*
* @var int
* @since 3.2.0
*/
protected int $fileSeconds = 0;
/**
* The line seconds counter
*
* @var int
* @since 3.2.0
*/
protected int $lineSeconds = 0;
/**
* The seconds debugging counter
*
* @var float
* @since 3.2.0
*/
protected float $secondsDebugging = 0;
/**
* The seconds planning counter
*
* @var float
* @since 3.2.0
*/
protected float $secondsPlanning = 0;
/**
* The seconds mapping counter
*
* @var float
* @since 3.2.0
*/
protected float $secondsMapping = 0;
/**
* The seconds office counter
*
* @var float
* @since 3.2.0
*/
protected float $secondsOffice = 0;
/**
* The total hours counter
*
* @var int
* @since 3.2.0
*/
protected int $totalHours = 0;
/**
* The debugging hours counter
*
* @var int
* @since 3.2.0
*/
protected int $debuggingHours = 0;
/**
* The planning hours counter
*
* @var int
* @since 3.2.0
*/
protected int $planningHours = 0;
/**
* The mapping hours counter
*
* @var int
* @since 3.2.0
*/
protected int $mappingHours = 0;
/**
* The office hours counter
*
* @var int
* @since 3.2.0
*/
protected int $officeHours = 0;
/**
* The actual Total Hours counter
*
* @var int
* @since 3.2.0
*/
protected int $actualTotalHours = 0;
/**
* The actual hours spent counter
*
* @var int
* @since 3.2.0
*/
protected int $actualHoursSpent = 0;
/**
* The actual days spent counter
*
* @var int
* @since 3.2.0
*/
protected int $actualDaysSpent = 0;
/**
* The total days counter
*
* @var int
* @since 3.2.0
*/
protected int $totalDays = 0;
/**
* The actual Total Days counter
*
* @var int
* @since 3.2.0
*/
protected int $actualTotalDays = 0;
/**
* The project week time counter
*
* @var float
* @since 3.2.0
*/
protected float $projectWeekTime = 0;
/**
* The project month time counter
*
* @var float
* @since 3.2.0
*/
protected float $projectMonthTime = 0;
/**
* The compiler start timer
*
* @var float
* @since 3.2.0
*/
protected float $start = 0;
/**
* The compiler end timer
*
* @var float
* @since 3.2.0
*/
protected float $end = 0;
/**
* The compiler timer
*
* @var float
* @since 3.2.0
*/
protected float $timer = 0;
/**
* The ContentOne Class.
*
* @var Content
* @since 3.2.0
*/
protected Content $content;
/**
* Constructor.
*
* @param Content $content The ContentOne Class.
*
* @since 3.2.0
*/
public function __construct(Content $content)
{
$this->content = $content;
}
/**
* Start the timer
*
* @return void
* @since 3.2.0
*/
public function start()
{
$this->start = microtime(true);
}
/**
* End the timer
*
* @return void
* @since 3.2.0
*/
public function end()
{
$this->end = microtime(true);
// calculate the lenght
$this->timer = $this->end - $this->start;
// compiler time
$this->content->set('COMPILER_TIMER_END', $this->end);
$this->content->set('COMPILER_TIMER', $this->timer);
}
/**
* Set all the time values
*
* @return void
* @since 3.2.0
*/
public function set()
{
// calculate all the values
$this->calculate();
// set some defaults
$this->content->set('LINE_COUNT', $this->line);
$this->content->set('FIELD_COUNT', $this->field);
$this->content->set('FILE_COUNT', $this->file);
$this->content->set('FOLDER_COUNT', $this->folder);
$this->content->set('PAGE_COUNT', $this->page);
$this->content->set('folders', $this->folderSeconds);
$this->content->set('foldersSeconds', $this->folderSeconds);
$this->content->set('files', $this->fileSeconds);
$this->content->set('filesSeconds', $this->fileSeconds);
$this->content->set('lines', $this->lineSeconds);
$this->content->set('linesSeconds', $this->lineSeconds);
$this->content->set('seconds', $this->actualSeconds);
$this->content->set('actualSeconds', $this->actualSeconds);
$this->content->set('totalHours', $this->totalHours);
$this->content->set('totalDays', $this->totalDays);
$this->content->set('debugging', $this->secondsDebugging);
$this->content->set('secondsDebugging', $this->secondsDebugging);
$this->content->set('planning', $this->secondsPlanning);
$this->content->set('secondsPlanning', $this->secondsPlanning);
$this->content->set('mapping', $this->secondsMapping);
$this->content->set('secondsMapping', $this->secondsMapping);
$this->content->set('office', $this->secondsOffice);
$this->content->set('secondsOffice', $this->secondsOffice);
$this->content->set('actualTotalHours', $this->actualTotalHours);
$this->content->set('actualTotalDays', $this->actualTotalDays);
$this->content->set('debuggingHours', $this->debuggingHours);
$this->content->set('planningHours', $this->planningHours);
$this->content->set('mappingHours', $this->mappingHours);
$this->content->set('officeHours', $this->officeHours);
$this->content->set('actualHoursSpent', $this->actualHoursSpent);
$this->content->set('actualDaysSpent', $this->actualDaysSpent);
$this->content->set('projectWeekTime', $this->projectWeekTime);
$this->content->set('projectMonthTime', $this->projectMonthTime);
// compiler time
$this->content->set('COMPILER_TIMER_START', $this->start);
}
/**
* Calculate all the time values
*
* @return void
* @since 3.2.0
*/
protected function calculate()
{
// what is the size in terms of an A4 book
$this->page = round($this->line / 56);
// setup the unrealistic numbers
$this->folderSeconds = $this->folder * 5;
$this->fileSeconds = $this->file * 5;
$this->lineSeconds = $this->line * 10;
$this->seconds = $this->folderSeconds + $this->fileSeconds
+ $this->lineSeconds;
$this->totalHours = round($this->seconds / 3600);
$this->totalDays = round($this->totalHours / 8);
// setup the more realistic numbers
$this->secondsDebugging = $this->seconds / 4;
$this->secondsPlanning = $this->seconds / 7;
$this->secondsMapping = $this->seconds / 10;
$this->secondsOffice = $this->seconds / 6;
$this->actualSeconds = $this->folderSeconds + $this->fileSeconds
+ $this->lineSeconds + $this->secondsDebugging
+ $this->secondsPlanning + $this->secondsMapping
+ $this->secondsOffice;
$this->actualTotalHours = round($this->actualSeconds / 3600);
$this->actualTotalDays = round($this->actualTotalHours / 8);
$this->debuggingHours = round($this->secondsDebugging / 3600);
$this->planningHours = round($this->secondsPlanning / 3600);
$this->mappingHours = round($this->secondsMapping / 3600);
$this->officeHours = round($this->secondsOffice / 3600);
// the actual time spent
$this->actualHoursSpent = $this->actualTotalHours - $this->totalHours;
$this->actualDaysSpent = $this->actualTotalDays - $this->totalDays;
// calculate the projects actual time frame of completion
$this->projectWeekTime = round($this->actualTotalDays / 5, 1);
$this->projectMonthTime = round($this->actualTotalDays / 24, 1);
}
}

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\Utilities;
use VDM\Joomla\Componentbuilder\Compiler\Factory as Compiler;
use VDM\Joomla\Componentbuilder\Compiler\Placeholder;
use VDM\Joomla\Componentbuilder\Utilities\Constantpaths;
/**
* Compiler Utilities Dynamic Path
*
* @since 3.2.0
*/
class Dynamicpath
{
/**
* Compiler Placeholder
*
* @var Placeholder
* @since 3.2.0
**/
protected Placeholder $placeholder;
/**
* Constant Paths
*
* @var array
* @since 3.2.0
**/
protected array $paths;
/**
* Constructor.
*
* @param Placeholder|null $placeholder The Compiler Placeholder object.
* @param Constantpaths|null $paths The Constant Paths object.
*
* @since 3.2.0
*/
public function __construct(?Placeholder $placeholder = null, ?Constantpaths $paths = null)
{
$this->placeholder = $placeholder ?: Compiler::_('Placeholder');
$paths = $paths ?: Compiler::_('Utilities.Constantpaths');
// load the constant paths
$this->paths = $paths->get();
}
/**
* Update path with dynamic value
*
* @param string $path The path to update
*
* @return string The updated path
* @since 3.2.0
*/
public function update(string $path): string
{
return $this->placeholder->update_(
$this->placeholder->update(
$path, $this->paths
)
);
}
}

View File

@@ -0,0 +1,53 @@
<?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\Utilities;
use VDM\Joomla\Utilities\StringHelper;
use VDM\Joomla\Utilities\GetHelper;
use VDM\Joomla\Utilities\Base64Helper;
/**
* The Field Helper
*
* @since 3.2.0
*/
abstract class FieldHelper
{
/**
* Get a field value from the XML stored string
*
* @param string $xml The xml string of the field
* @param string $get The value key to get from the string
* @param string $confirmation The value to confirm found value
*
* @return string The field value from xml
* @since 3.2.0
*/
public static function getValue(&$xml, string &$get, string $confirmation = ''): string
{
if (StringHelper::check($xml))
{
// if we have a PHP value, we must base64 decode it
if (strpos($get, 'type_php') !== false)
{
return Base64Helper::open(GetHelper::between($xml, $get . '="', '"', $confirmation));
}
return GetHelper::between($xml, $get . '="', '"', $confirmation);
}
return $confirmation;
}
}

View File

@@ -0,0 +1,109 @@
<?php
/**
* @package Joomla.Component.Builder
*
* @created 3rd 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\Compiler\Utilities;
use Joomla\CMS\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;
use VDM\Joomla\Utilities\FileHelper;
/**
* File helper
*
* @since 3.2.0
*/
class File
{
/**
* Compiler Utilities Counter
*
* @var Counter
* @since 3.2.0
*/
protected Counter $counter;
/**
* Compiler Utilities Paths
*
* @var Paths
* @since 3.2.0
*/
protected Paths $paths;
/**
* Constructor
*
* @param Counter|null $counter The compiler counter object.
* @param Paths|null $paths The compiler paths object.
*
* @since 3.2.0
*/
public function __construct(?Counter $counter = null, ?Paths $paths = null)
{
$this->counter = $counter ?: Compiler::_('Utilities.Counter');
$this->paths = $paths ?: Compiler::_('Utilities.Paths');
}
/**
* set HTML blank file to a path
*
* @param string $path The path to where to set the blank html file
* @param string $root The root path
*
* @return void
*/
public function html(string $path = '', string $root = 'component')
{
if ('component' === $root)
{
$root = $this->paths->component_path . '/';
}
// use path if exist
if (strlen($path) > 0)
{
JoomlaFile::copy(
$this->paths->template_path . '/index.html',
$root . $path . '/index.html'
);
}
else
{
JoomlaFile::copy(
$this->paths->template_path . '/index.html',
$root . '/index.html'
);
}
// count the file created
$this->counter->file++;
}
/**
* Create a file on the server if it does not exist, or Overwrite existing files
*
* @param string $path The path and file name where to safe the data
* @param string $data The data to safe
*
* @return bool true On success
* @since 3.2.0
*/
public function write(string $path, string $data): bool
{
return FileHelper::write($path, $data);
}
}

View File

@@ -0,0 +1,213 @@
<?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\Utilities;
use VDM\Joomla\Componentbuilder\Compiler\Power\Injector as Power;
use VDM\Joomla\Utilities\MathHelper;
/**
* File Injector
* Thanks to http://stackoverflow.com/a/16813550/1429677
*
* @since 3.2.0
*/
final class FileInjector
{
/**
* The Injector Class.
*
* @var Power
* @since 3.2.0
*/
protected Power $power;
/**
* The pattern to get the powers
*
* @var string
* @since 3.2.0
**/
protected string $pattern = '/Super_'.'_'.'_[a-zA-Z0-9_]+_'.'_'.'_Power/';
/**
* Constructor.
*
* @param Power $power The Injector Class.
*
* @since 3.2.0
*/
public function __construct(Power $power)
{
$this->power = $power;
}
/**
* Inserts or replaces data in a file at a specific position.
*
* @param string $file The path of the file to modify.
* @param string $data The data to insert or replace.
* @param int $position The position in the file where the data should be inserted or replaced.
* @param int|null $replace The number of bytes to replace; if null, data will be inserted.
*
* @return void
* @throws \RuntimeException If unable to open or modify the file.
* @throws \InvalidArgumentException If the position is negative.
* @since 3.2.0
*/
public function add(string $file, string $data, int $position, ?int $replace = null): void
{
if ($position < 0)
{
throw new \InvalidArgumentException('Position cannot be negative.');
}
$found_super_powers = preg_match($this->pattern, $data);
$actual_file = $this->openFileWithLock($file);
try
{
$temp_file = fopen('php://temp', "rw+");
if ($temp_file === false)
{
throw new \RuntimeException("Unable to open temporary file.");
}
$this->processFile($actual_file, $temp_file, $data, $position, $replace);
if ($found_super_powers)
{
$this->injectSuperPowers($actual_file);
}
}
finally
{
flock($actual_file, LOCK_UN);
fclose($actual_file);
if (isset($temp_file))
{
fclose($temp_file);
}
}
}
/**
* Opens a file and acquires an exclusive lock on it.
*
* @param string $file The file path to open.
*
* @return resource The file handle.
* @throws \RuntimeException If the file cannot be opened or locked.
* @since 3.2.0
*/
private function openFileWithLock(string $file)
{
$actual_file = fopen($file, "rw+");
if ($actual_file === false || !flock($actual_file, LOCK_EX))
{
throw new \RuntimeException("Unable to open and lock the file: {$file}");
}
return $actual_file;
}
/**
* Processes the file for data insertion and copying the remaining data.
*
* @param resource $actual_file The file handle of the actual file.
* @param resource $temp_file The file handle of the temporary file.
* @param string $data The data to be inserted.
* @param int $position The position in the file for the data insertion.
* @param int|null $replace The number of bytes to replace; if null, data will be inserted.
*
* @return void
* @since 3.2.0
*/
private function processFile($actual_file, $temp_file, string $data, int $position, ?int $replace): void
{
// Make a copy of the file in the temporary stream
stream_copy_to_stream($actual_file, $temp_file);
// Move to the position where the data should be added
fseek($actual_file, $position);
// Add the data
fwrite($actual_file, $data);
$this->truncateIfNeeded($actual_file, $data, $position);
$this->copyRemainingData($actual_file, $temp_file, $position, $replace);
}
/**
* Truncates the file after data insertion if necessary.
*
* @param resource $actual_file The file handle.
* @param string $data The data that was inserted.
* @param int $position The position where data was inserted.
*
* @return void
* @since 3.2.0
*/
private function truncateIfNeeded($actual_file, string $data, int $position): void
{
// Truncate the file at the end of the added data if replacing
$data_length = mb_strlen($data, '8bit');
$remove = MathHelper::bc('add', $position, $data_length);
ftruncate($actual_file, $remove);
}
/**
* Copies the remaining data from the temporary stream to the actual file.
*
* @param resource $actual_file The file handle of the actual file.
* @param resource $temp_file The file handle of the temporary file.
* @param int $position The position in the file where data insertion finished.
* @param int|null $replace The number of bytes that were replaced; if null, data was inserted.
*
* @return void
* @since 3.2.0
*/
private function copyRemainingData($actual_file, $temp_file, int $position, ?int $replace): void
{
// check if this was a replacement of data
$position = MathHelper::bc('add', $position, $replace ?: 0);
// Move to the position of the remaining data in the temporary stream
fseek($temp_file, $position);
// Copy the remaining data from the temporary stream to the file
stream_copy_to_stream($temp_file, $actual_file);
}
/**
* Injects super powers into the file content, if found, and updates the file.
*
* @param resource $actual_file The file handle of the actual file.
*
* @return void
* @since 3.2.0
*/
private function injectSuperPowers($actual_file): void
{
rewind($actual_file);
$power_data = $this->power->power(
stream_get_contents($actual_file)
);
ftruncate($actual_file, 0);
rewind($actual_file);
fwrite($actual_file, $power_data);
}
}

View File

@@ -0,0 +1,26 @@
<?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\Utilities;
use VDM\Joomla\Componentbuilder\Abstraction\BaseRegistry;
/**
* Compiler Utilities Files Bucket
*
* @since 3.2.0
*/
class Files extends BaseRegistry
{
}

View File

@@ -0,0 +1,170 @@
<?php
/**
* @package Joomla.Component.Builder
*
* @created 3rd 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\Compiler\Utilities;
use Joomla\CMS\Filesystem\Folder as JoomlaFolder;
use Joomla\CMS\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;
use VDM\Joomla\Utilities\ArrayHelper;
/**
* Folder helper
*
* @since 3.2.0
*/
class Folder
{
/**
* Compiler Counter
*
* @var Counter
* @since 3.2.0
*/
protected Counter $counter;
/**
* Compiler Utilities File
*
* @var File
* @since 3.2.0
*/
protected File $file;
/**
* Constructor
*
* @param Counter|null $counter The compiler counter object.
* @param File|null $file The compiler file object.
*
* @since 3.2.0
*/
public function __construct(?Counter $counter = null, ?File $file = null)
{
$this->counter = $counter ?: Compiler::_('Utilities.Counter');
$this->file = $file ?: Compiler::_('Utilities.File');
}
/**
* Create Path if not exist
*
* @param string $path The path to folder to create
* @param bool $addHtml The the switch to add the HTML
*
* @return void
* @since 3.2.0
*/
public function create(string $path, bool $addHtml = true)
{
// check if the path exist
if (!JoomlaFolder::exists($path))
{
// create the path
JoomlaFolder::create(
$path
);
// count the folder created
$this->counter->folder++;
if ($addHtml)
{
// add index.html (boring I know)
$this->file->html(
$path, ''
);
}
}
}
/**
* Remove folders with files
*
* @param string $path The path to folder to remove
* @param array|null $ignore The folders and files to ignore and not remove
*
* @return boolean True if all are removed
* @since 3.2.0
*/
public function remove(string $path, ?array $ignore = null): bool
{
if (!JoomlaFolder::exists($path))
{
return false;
}
$it = new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS);
$files = new \RecursiveIteratorIterator($it, \RecursiveIteratorIterator::CHILD_FIRST);
// Prepare a base path without trailing slash for comparison
$basePath = rtrim($path, '/');
foreach ($files as $fileinfo)
{
$filePath = $fileinfo->getRealPath();
if ($this->shouldIgnore($basePath, $filePath, $ignore))
{
continue;
}
if ($fileinfo->isDir())
{
JoomlaFolder::delete($filePath);
}
else
{
JoomlaFile::delete($filePath);
}
}
// Delete the root folder if ignore not set
if (!ArrayHelper::check($ignore))
{
return JoomlaFolder::delete($path);
}
return true;
}
/**
* Check if the current path should be ignored.
*
* @param string $basePath The base directory path
* @param string $filePath The current file or directory path
* @param array|null $ignore List of items to ignore
*
* @return boolean True if the path should be ignored
* @since 3.2.0
*/
protected function shouldIgnore(string $basePath, string $filePath, ?array $ignore = null): bool
{
if (!$ignore || !ArrayHelper::check($ignore))
{
return false;
}
foreach ($ignore as $item)
{
if (strpos($filePath, $basePath . '/' . $item) !== false)
{
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,90 @@
<?php
/**
* @package Joomla.Component.Builder
*
* @created 3rd 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\Utilities;
use VDM\Joomla\Componentbuilder\Compiler\Factory as Compiler;
/**
* The Indentation Factory
*
* @since 3.2.0
*/
abstract class Indent
{
/**
* Spacer bucket (to speed-up the build)
*
* @var array
* @since 3.2.0
*/
private static array $bucket = [];
/**
* The indentation string
*
* @var string
* @since 3.2.0
*/
private static string $indent;
/**
* Set the space
*
* @param int $nr The number of spaces
*
* @return string
* @since 3.2.0
*/
public static function _(int $nr): string
{
// check if we already have the string
if (!isset(self::$bucket[$nr]))
{
// get the string
self::$bucket[$nr] = str_repeat(self::indent(), (int) $nr);
}
// return stored indentation
return self::$bucket[$nr];
}
/**
* Get the indentation string
*
* @return string
* @since 3.2.0
*/
private static function indent(): string
{
if (empty(self::$indent))
{
self::init();
}
return self::$indent;
}
/**
* The constructor for indent
*
* @return void
* @since 3.2.0
*/
private static function init()
{
// the default is TAB
self::$indent = Compiler::_('Config')->indentation_value;
}
}

View File

@@ -0,0 +1,79 @@
<?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\Utilities;
use VDM\Joomla\Componentbuilder\Compiler\Factory as Compiler;
/**
* The Debug Line Number Factory
*
* @since 3.2.0
*/
abstract class Line
{
/**
* Should we add debug lines
*
* @since 3.2.0
**/
private static $add = 'check';
/**
* Set the line number in comments
*
* @param int $nr The line number
* @param string $class The class name
*
* @return string
* @since 3.2.0
*/
public static function _(int $nr, string $class): string
{
if (self::add())
{
return ' [' . $class . ' ' . $nr . ']';
}
return '';
}
/**
* Check if we should add the line number
*
* @return bool
* @since 3.2.0
*/
private static function add(): bool
{
if (!is_bool(self::$add))
{
self::init();
}
return self::$add;
}
/**
* The constructor for add
*
* @return void
* @since 3.2.0
*/
private static function init()
{
self::$add = Compiler::_('Config')->debug_line_nr;
}
}

View File

@@ -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\Componentbuilder\Compiler\Utilities;
use VDM\Minify\Css;
use VDM\Minify\JavaScript;
/**
* Compiler Minifier
*
* @since 3.2.0
*/
abstract class Minify
{
/**
* Minify JavaScript Class
*
* @var JavaScript
* @since 3.2.0
*/
public static JavaScript $js;
/**
* Minify Css Class
*
* @var Css
* @since 3.2.0
*/
public static Css $css;
/**
* Minify JavaScript
*
* @param string $data
*
* @return string
* @since 3.2.0
*/
public static function js(string $data): string
{
// check if instance already set
if (empty(self::$js))
{
// set instanceof on JavaScript
self::$js = new JavaScript;
}
// add the data
self::$js->add($data);
// return minified
return self::$js->minify();
}
/**
* Minify Css
*
* @param string $data
*
* @return string
* @since 3.2.0
*/
public static function css(string $data): string
{
// check if instance already set
if (empty(self::$css))
{
// set instanceof on Css
self::$css = new Css;
}
// add the data
self::$css->add($data);
// return minified
return self::$css->minify();
}
}

View File

@@ -0,0 +1,61 @@
<?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\Utilities;
use VDM\Joomla\Utilities\ArrayHelper;
use VDM\Joomla\Utilities\StringHelper;
/**
* Compiler Path Fix
*
* @since 3.2.0
*/
class Pathfix
{
/**
* Fix the path to work in the JCB script <-- (main issue here)
* Since we need / slash in all paths, for the JCB script even if it is Windows
* and since MS works with both forward and back slashes
* we just convert all slashes to forward slashes
*
* THIS is just my hack (fix) if you know a better way! speak-up!
*
* @param mixed $values the array of paths or the path as a string
* @param array $targets paths to target
*
* @return void
* @since 3.2.0
*/
public function set(&$values, array $targets = [])
{
// if multiple to gets searched and fixed
if (ArrayHelper::check($values) && ArrayHelper::check($targets))
{
foreach ($targets as $target)
{
if (isset($values[$target]))
{
$this->set($values[$target], $targets);
}
}
}
// if just a string
elseif (StringHelper::check($values) && strpos((string) $values, '\\') !== false)
{
$values = str_replace('\\', '/', (string) $values);
}
}
}

View File

@@ -0,0 +1,188 @@
<?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\Utilities;
use VDM\Joomla\Componentbuilder\Compiler\Config;
use VDM\Joomla\Componentbuilder\Compiler\Component;
use VDM\Joomla\Abstraction\Registry;
/**
* Compiler Utilities Paths
*
* @since 3.2.0
*/
class Paths extends Registry
{
/**
* Compiler Config
*
* @var Config
* @since 3.2.0
**/
protected Config $config;
/**
* Compiler Component
*
* @var Component
* @since 3.2.0
**/
protected Component $component;
/**
* Constructor
*
* @param Config $config The compiler config object.
* @param Component $component The component class.
*
* @since 3.2.0
*/
public function __construct(Config $config = null, Component $component = null)
{
$this->config = $config;
$this->component = $component;
// set the template path
$this->setTemplatePath();
// set component sales name
$this->setComponentSalesName();
// set component backup name
$this->setComponentBackupName();
// set component folder name
$this->setComponentFolderName();
// set component path
$this->setComponentPath();
// set the template path for custom
$this->setTemplatePathCustom();
}
/**
* getting any valid paths
*
* @param string $key The value's key/path name
*
* @return string The path found as a string
* @since 3.2.0
* @throws \InvalidArgumentException If $key is not a valid function name.
*/
public function __get(string $key): string
{
// check if it has been set
if ($this->exists($key))
{
return $this->get($key);
}
throw new \InvalidArgumentException(sprintf('Path %s could not be found in the Paths Class.', $key));
}
/**
* Set the template path
*
* @return void
*
* @since 3.2.0
*/
private function setTemplatePath(): void
{
$this->set('template_path',
$this->config->get('compiler_path', JPATH_COMPONENT_ADMINISTRATOR . '/compiler') . '/joomla_'
. $this->config->joomla_versions[$this->config->joomla_version]['folder_key']
);
}
/**
* Set component sales name
*
* @return void
*
* @since 3.2.0
*/
private function setComponentSalesName(): void
{
$this->set('component_sales_name',
'com_' . $this->component->get('sales_name') . '__J'
. $this->config->joomla_version
);
}
/**
* Set component backup name
*
* @return void
*
* @since 3.2.0
*/
private function setComponentBackupName(): void
{
$this->set('component_backup_name',
'com_' . $this->component->get('sales_name') . '_v' . str_replace(
'.', '_', (string) $this->component->get('component_version')
) . '__J' . $this->config->joomla_version
);
}
/**
* Set component folder name
*
* @return void
*
* @since 3.2.0
*/
private function setComponentFolderName(): void
{
$this->set('component_folder_name',
'com_' . $this->component->get('name_code') . '_v' . str_replace(
'.', '_', (string) $this->component->get('component_version')
) . '__J' . $this->config->joomla_version
);
}
/**
* Set component path
*
* @return void
*
* @since 3.2.0
*/
private function setComponentPath(): void
{
$this->set('component_path',
$this->config->get('compiler_path', JPATH_COMPONENT_ADMINISTRATOR . '/compiler') . '/'
. $this->get('component_folder_name')
);
}
/**
* set the template path for custom TODO: just use custom_folder_path in config
*
* @return void
*
* @since 3.2.0
*/
private function setTemplatePathCustom(): void
{
$this->set('template_path_custom',
$this->config->get(
'custom_folder_path', JPATH_COMPONENT_ADMINISTRATOR . '/custom'
)
);
}
}

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\Utilities;
/**
* The Placeholder Prefix and Suffix Factory
*
* @since 3.2.0
*/
abstract class Placefix
{
/**
* The hash prefix and suffix
*
* @var string
* @since 3.2.0
**/
private static string $hhh = '#' . '#' . '#';
/**
* The open prefix
*
* @var string
* @since 3.2.0
**/
private static string $bbb = '[' . '[' . '[';
/**
* The close suffix
*
* @var string
* @since 3.2.0
**/
private static string $ddd = ']' . ']' . ']';
/**
* Get a prefix and suffix added to given string
*
* @param string $class The class name
*
* @return string
* @since 3.2.0
*/
public static function _(string $string): string
{
return self::b() . $string . self::d();
}
/**
* Get a open prefix
*
* @return string
* @since 3.2.0
*/
public static function b(): string
{
return self::$bbb;
}
/**
* Get a close suffix
*
* @return string
* @since 3.2.0
*/
public static function d(): string
{
return self::$ddd;
}
/**
* Get a hash prefix and suffix added to given string
*
* @param string $class The class name
*
* @return string
* @since 3.2.0
*/
public static function _h(string $string): string
{
return self::h() . $string . self::h();
}
/**
* Get a hash-fix
*
* @return string
* @since 3.2.0
*/
public static function h(): string
{
return self::$hhh;
}
}

View File

@@ -0,0 +1,342 @@
<?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\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 VDM\Joomla\Componentbuilder\Compiler\Placeholder;
use VDM\Joomla\Componentbuilder\Compiler\Interfaces\Component\SettingsInterface as Settings;
use VDM\Joomla\Componentbuilder\Compiler\Utilities\Paths;
use VDM\Joomla\Componentbuilder\Compiler\Utilities\Counter;
use VDM\Joomla\Componentbuilder\Compiler\Utilities\File;
use VDM\Joomla\Componentbuilder\Compiler\Utilities\Files;
use VDM\Joomla\Utilities\ArrayHelper;
use VDM\Joomla\Utilities\StringHelper;
/**
* Compiler Utilities To Build Structure
*
* @since 3.2.0
*/
class Structure
{
/**
* 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 Paths Class.
*
* @var Paths
* @since 3.2.0
*/
protected Paths $paths;
/**
* The Counter Class.
*
* @var Counter
* @since 3.2.0
*/
protected Counter $counter;
/**
* The File Class.
*
* @var File
* @since 3.2.0
*/
protected File $file;
/**
* The Files Class.
*
* @var Files
* @since 3.2.0
*/
protected Files $files;
/**
* Database object to query local DB
*
* @var CMSApplication
* @since 3.2.0
**/
protected CMSApplication $app;
/**
* Constructor.
*
* @param Placeholder $placeholder The Placeholder Class.
* @param Settings $settings The SettingsInterface Class.
* @param Paths $paths The Paths Class.
* @param Counter $counter The Counter Class.
* @param File $file The File Class.
* @param Files $files The Files Class.
* @param CMSApplication|null $app The CMS Application object.
*
* @since 3.2.0
*/
public function __construct(Placeholder $placeholder, Settings $settings, Paths $paths,
Counter $counter, File $file, Files $files, ?CMSApplication $app = null)
{
$this->placeholder = $placeholder;
$this->settings = $settings;
$this->paths = $paths;
$this->counter = $counter;
$this->file = $file;
$this->files = $files;
$this->app = $app ?: Factory::getApplication();
}
/**
* Build Structural Needed Files & Folders
*
* @param array $target The main target and name
* @param string $type The type in the target
* @param string|null $fileName The custom file name
* @param array|null $config To add more data to the files info
*
* @return bool true on success
* @since 3.2.0
*/
public function build(array $target, string $type,
?string $fileName = null, ?array $config = null): bool
{
// did we build the files (any number)
$build_status = false;
// check that we have the target values
if (ArrayHelper::check($target))
{
// search the target
foreach ($target as $main => $name)
{
// get the key name (either file name or name)
$key = $fileName ?? $name;
// add to placeholders as Name and name
$this->placeholder->set('Name', StringHelper::safe($name, 'F'));
$this->placeholder->set('name', StringHelper::safe($name));
$this->placeholder->set('Key', StringHelper::safe($key, 'F'));
$this->placeholder->set('key', StringHelper::safe($key));
// make sure it is lower case
$name = StringHelper::safe($name);
// setup the files
foreach ($this->settings->multiple()->{$main} as $item => $details)
{
if ($details->type === $type)
{
$file_details = $this->getFileDetails(
$details,
(string) $item,
$name,
$fileName,
$config
);
if (is_array($file_details))
{
// store the new files
$this->files->appendArray('dynamic.' . $file_details['view'],
$file_details);
// we have build at least one
$build_status = true;
}
}
}
// remove the name from placeholders
$this->placeholder->remove('Name');
$this->placeholder->remove('name');
$this->placeholder->remove('Key');
$this->placeholder->remove('key');
}
}
return $build_status;
}
/**
* Get the details
*
* @param object $details The item details
* @param string $item The item name
* @param string $name The given name
* @param string|null $fileName The custom file name
* @param array|null $config To add more data to the files info
*
* @return array|null The details
* @since 3.2.0
*/
private function getFileDetails(object $details, string $item,
string $name, ?string $fileName = null, ?array $config = null): ?array
{
$zip_path = '';
if (($path = $this->getPath($details, $zip_path, $name)) === null)
{
return null;
}
// setup the folder
if (!Folder::exists($path))
{
Folder::create($path);
$this->file->html($zip_path);
// count the folder created
$this->counter->folder++;
}
$new_name = $this->getNewName($details, $item, $name, $fileName);
if (!JoomlaFile::exists($path . '/' . $new_name))
{
// move the file to its place
JoomlaFile::copy(
$this->paths->template_path . '/' . $item,
$path . '/' . $new_name
);
// count the file created
$this->counter->file++;
}
// we can't have dots in a view name
if (strpos($name, '.') !== false)
{
$name = preg_replace('/[\.]+/', '_', (string) $name);
}
// setup array for new file
$file = [
'path' => $path . '/' . $new_name,
'name' => $new_name,
'view' => $name,
'zip' => $zip_path . '/' . $new_name
];
if (ArrayHelper::check($config))
{
$file['config'] = $config;
}
return $file;
}
/**
* Get the path
*
* @param object $details The item details
* @param string $zipPath The zip path
* @param string $name The name
*
* @return string|null The path
* @since 3.2.0
*/
private function getPath(object $details, string &$zipPath, string $name): ?string
{
// set destination path
if (strpos((string) $details->path, 'VIEW') !== false)
{
$path = str_replace('VIEW', $name, (string) $details->path);
}
else
{
$path = $details->path;
}
$path = $this->placeholder->update_($path);
// make sure we have component to replace
if (strpos((string) $path, 'c0mp0n3nt') !== false)
{
$zipPath = str_replace('c0mp0n3nt/', '', (string) $path);
return str_replace(
'c0mp0n3nt/', $this->paths->component_path . '/', (string) $path
);
}
$this->app->enqueueMessage(
Text::sprintf('COM_COMPONENTBUILDER_HR_HTHREECZEROMPZERONTHREENT_ISSUE_FOUNDHTHREEPTHE_PATH_S_COULD_NOT_BE_USEDP',
$path
), 'Error'
);
return null;
}
/**
* Get the new name
*
* @param object $details The item details
* @param string $item The item name
* @param string $name The name
* @param string|null $fileName The custom file name
*
* @return string The new name
* @since 3.2.0
*/
private function getNewName(object $details, string $item,
string &$name, ?string $fileName = null): string
{
// do the file need renaming
if ($details->rename)
{
if (!empty($fileName))
{
$name = $name . '_' . $fileName;
}
if ($details->rename === 'new')
{
$item = $details->newName;
}
elseif (!empty($fileName))
{
$item = str_replace(
$details->rename, $fileName, $item
);
}
else
{
$item = str_replace(
$details->rename, $name, $item
);
}
}
return $this->placeholder->update_($item);
}
}

View File

@@ -0,0 +1,93 @@
<?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\Utilities;
/**
* Compiler Creating an Unique Code/String
*
* @since 3.2.0
*/
abstract class Unique
{
/**
* Unique Code/Strings
*
* @var array
* @since 3.2.0
*/
protected static array $unique = [];
/**
* Unique Areas Code/Strings
*
* @var array
* @since 3.2.0
*/
protected static array $areas = [];
/**
* Creating an unique local key
*
* @param int $size The key size
*
* @return string The unique local key
*
*/
public static function get($size): string
{
$unique = (isset(self::$unique[$size])) ? end(self::$unique[$size]) : null;
if(!$unique)
{
$unique = substr("vvvvvvvvvvvvvvvvvvvvvvvvvvvvvv", 0, $size);
self::$unique[$size] = [];
}
while(in_array($unique, self::$unique[$size]))
{
$unique++;
}
self::$unique[$size][] = $unique;
return $unique;
}
/**
* Creating an Unique Code
*
* @param string $code
* @param string $target
*
* @return string
* @since 3.2.0
*/
public static function code(string $code, string $target = 'both'): string
{
if (!isset(self::$areas[$target])
|| !in_array(
$code, self::$areas[$target]
))
{
self::$areas[$target][] = $code;
return $code;
}
// make sure it is unique
return self::code($code . self::get(1));
}
}

View File

@@ -0,0 +1,213 @@
<?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\Utilities;
use Joomla\CMS\Factory;
use Joomla\CMS\Application\CMSApplication;
use Joomla\CMS\Language\Text;
use VDM\Joomla\Componentbuilder\Compiler\Factory as Compiler;
use VDM\Joomla\Componentbuilder\Compiler\Config;
use VDM\Joomla\Utilities\FormHelper;
/**
* Compiler Utilities Xml
*
* @since 3.2.0
*/
final class Xml
{
/**
* Compiler Config
*
* @var Config
* @since 3.2.0
*/
protected Config $config;
/**
* Application object.
*
* @var CMSApplication
* @since 3.2.0
**/
protected CMSApplication $app;
/**
* Constructor
*
* @param Config|null $config The compiler config object.
* @param CMSApplication|null $app The CMS Application object.
*
* @since 3.2.0
*/
public function __construct(?Config $config = null, ?CMSApplication $app = null)
{
$this->config = $config ?: Compiler::_('Config');
$this->app = $app ?: Factory::getApplication();
}
/**
* get the field xml
*
* @param array $attributes The array of attributes
* @param array $options The options to apply to the XML element
*
* @return \SimpleXMLElement|null
* @since 3.2.0
*/
public function get(array $attributes, ?array $options = null): ?\SimpleXMLElement
{
return FormHelper::xml($attributes, $options);
}
/**
* xmlAppend
*
* @param \SimpleXMLElement $xml The XML element reference in which to inject a comment
* @param mixed $node A SimpleXMLElement node to append to the XML element reference,
* or a stdClass object containing a comment attribute to be injected
* before the XML node and a fieldXML attribute containing a SimpleXMLElement
*
* @return void
* @since 3.2.0
*/
public function append(\SimpleXMLElement &$xml, $node)
{
FormHelper::append($xml, $node);
}
/**
* xmlComment
*
* @param \SimpleXMLElement $xml The XML element reference in which to inject a comment
* @param string $comment The comment to inject
*
* @return void
* @since 3.2.0
*/
public function comment(\SimpleXMLElement &$xml, string $comment)
{
FormHelper::comment($xml, $comment);
}
/**
* xmlAddAttributes
*
* @param \SimpleXMLElement $xml The XML element reference in which to inject a comment
* @param array $attributes The attributes to apply to the XML element
*
* @return void
* @since 3.2.0
*/
public function attributes(\SimpleXMLElement &$xml, array $attributes = [])
{
FormHelper::attributes($xml, $attributes);
}
/**
* xmlAddOptions
*
* @param \SimpleXMLElement $xml The XML element reference in which to inject a comment
* @param array $options The options to apply to the XML element
*
* @return void
* @since 3.2.0
*/
public function options(\SimpleXMLElement &$xml, array $options = [])
{
FormHelper::options($xml, $options);
}
/**
* XML Pretty Print
*
* @param \SimpleXMLElement $xml The XML element containing a node to be output
* @param string $nodename node name of the input xml element to print out. this is done to omit the <?xml... tag
*
* @return string XML output
* @since 3.2.0
*/
public function pretty(\SimpleXMLElement $xml, string $nodename): string
{
$dom = dom_import_simplexml($xml)->ownerDocument;
$dom->formatOutput = true;
$xmlString = $dom->saveXML(
$dom->getElementsByTagName($nodename)->item(0)
);
// make sure Tidy is enabled
if ($this->config->get('tidy', false))
{
$tidy = new \Tidy();
$tidy->parseString(
$xmlString, [
'indent' => true,
'indent-spaces' => 8,
'input-xml' => true,
'output-xml' => true,
'indent-attributes' => true,
'wrap-attributes' => true,
'wrap' => false
]
);
$tidy->cleanRepair();
return $this->indent((string) $tidy, ' ', 8, true, false);
}
// set tidy warning only once
elseif ($this->config->set_tidy_warning)
{
// set the warning only once
$this->config->set('set_tidy_warning', false);
// now set the warning
$this->app->enqueueMessage(
Text::_('COM_COMPONENTBUILDER_HR_HTHREETIDY_ERRORHTHREE'), 'Error'
);
$this->app->enqueueMessage(
Text::sprintf('COM_COMPONENTBUILDER_YOU_MUST_ENABLE_THE_BTIDYB_EXTENSION_IN_YOUR_PHPINI_FILE_SO_WE_CAN_TIDY_UP_YOUR_XML_IF_YOU_NEED_HELP_PLEASE_A_SSTART_HEREA', 'href="https://github.com/vdm-io/Joomla-Component-Builder/issues/197#issuecomment-351181754" target="_blank"'), 'Error'
);
}
return $xmlString;
}
/**
* xmlIndent
*
* @param string $string The XML input
* @param string $char Character or characters to use as the repeated indent
* @param int $depth number of times to repeat the indent character
* @param bool $skipfirst Skip the first line of the input.
* @param bool $skiplast Skip the last line of the input;
*
* @return string XML output
* @since 3.2.0
*/
public function indent(string $string, string $char = ' ', int $depth = 0,
bool $skipfirst = false, bool $skiplast = false): string
{
$output = array();
$lines = explode("\n", $string);
$first = true;
$last = count($lines) - 1;
foreach ($lines as $i => $line)
{
$output[] = (($first && $skipfirst) || $i === $last && $skiplast)
? $line : str_repeat($char, $depth) . $line;
$first = false;
}
return implode("\n", $output);
}
}

View File

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