Release of v5.1.1-alpha2

Adds the ModalSelect fieldtype to Joomla Component Builder - J5. Adds the Data Import Function to the Demo Component. Adds new country related tables and fields to the Demo Component. Resolves the Database Updating issue in the compiler. #1212,#1209. Adds the Component Commands Plugin to the CLI for Import of spreadsheet data-sets.
This commit is contained in:
2025-03-28 13:15:25 +00:00
parent 06185f8c3a
commit 8342c5bb9f
536 changed files with 16369 additions and 2067 deletions

View File

@@ -315,6 +315,11 @@ class Data
*/
public function get($view): ?object
{
if (empty($view))
{
return null;
}
if (isset($this->index[$view]))
{
$id = $this->index[$view];
@@ -478,6 +483,13 @@ class Data
'jcb_ce_onBeforeModelViewData', [&$view]
);
// should we add sql?
if ($view->add_sql !== 1)
{
unset($view->addtables);
unset($view->sql);
}
// add the tables
$view->addtables = (isset($view->addtables) && JsonHelper::check($view->addtables))
? json_decode((string) $view->addtables, true)

View File

@@ -397,7 +397,7 @@ final class Structuresingle
return false;
}
// take action based on type
elseif ($details->type === 'file' && !File::exists($this->currentFullPath))
elseif ($details->type === 'file' && !is_file($this->currentFullPath))
{
$this->app->enqueueMessage(
Text::_('COM_COMPONENTBUILDER_HR_HTHREEFILE_PATH_ERRORHTHREE'), 'Error'

View File

@@ -689,6 +689,8 @@ final class Builders
// to identify the field
$this->databasetables->set($nameSingleCode . '.' . $name . '.ID',
$field['settings']->id);
$this->databasetables->set($nameSingleCode . '.' . $name . '.GUID',
$field['settings']->guid);
$this->databasetables->set($nameSingleCode . '.' . $name . '.null_switch',
$field['settings']->null_switch);
// set index types
@@ -912,6 +914,7 @@ final class Builders
&& $typeName != 'repeatable'
&& $typeName != 'subform')
{
// do not add if extends is empty
$this->customfield->add($nameListCode, [
'type' => $typeName,
'code' => $name,

View File

@@ -15,6 +15,7 @@ namespace VDM\Joomla\Componentbuilder\Compiler\Creator;
use VDM\Joomla\Componentbuilder\Compiler\Field\Name;
use VDM\Joomla\Componentbuilder\Compiler\Field\TypeName;
use VDM\Joomla\Componentbuilder\Compiler\Field\Attributes;
use VDM\Joomla\Componentbuilder\Compiler\Field\ModalSelect;
use VDM\Joomla\Componentbuilder\Compiler\Field\Groups;
use VDM\Joomla\Componentbuilder\Compiler\Builder\FieldNames;
use VDM\Joomla\Componentbuilder\Compiler\Interfaces\Creator\Fieldtypeinterface as Field;
@@ -56,6 +57,14 @@ final class FieldDynamic implements Fielddynamicinterface
*/
protected Attributes $attributes;
/**
* The ModalSelect Class.
*
* @var ModalSelect
* @since 5.2.1
*/
protected ModalSelect $modalselect;
/**
* The Groups Class.
*
@@ -102,6 +111,7 @@ final class FieldDynamic implements Fielddynamicinterface
* @param Name $name The Name Class.
* @param TypeName $typename The TypeName Class.
* @param Attributes $attributes The Attributes Class.
* @param ModalSelect $modalselect The ModalSelect Class.
* @param Groups $groups The Groups Class.
* @param FieldNames $fieldnames The FieldNames Class.
* @param Field $field The Fieldtypeinterface Class.
@@ -111,12 +121,14 @@ final class FieldDynamic implements Fielddynamicinterface
* @since 3.2.0
*/
public function __construct(Name $name, TypeName $typename,
Attributes $attributes, Groups $groups, FieldNames $fieldnames,
Attributes $attributes, ModalSelect $modalselect,
Groups $groups, FieldNames $fieldnames,
Field $field, Builders $builders, Layout $layout)
{
$this->name = $name;
$this->typename = $typename;
$this->attributes = $attributes;
$this->modalselect = $modalselect;
$this->groups = $groups;
$this->fieldnames = $fieldnames;
$this->field = $field;
@@ -172,6 +184,12 @@ final class FieldDynamic implements Fielddynamicinterface
// set options as null
$optionArray = null;
// special treatment for Modal Select
if ($typeName === 'ModalSelect')
{
$fieldAttributes['custom'] = $this->modalselect->extract($fieldAttributes);
}
if ($this->groups->check($typeName, 'option'))
{
// set options array
@@ -253,6 +271,7 @@ final class FieldDynamic implements Fielddynamicinterface
// set the custom array
$custom = $fieldAttributes['custom'];
unset($fieldAttributes['custom']);
// set db key
$custom['db'] = $dbkey;
// increment the db key

View File

@@ -19,6 +19,7 @@ use VDM\Joomla\Componentbuilder\Compiler\Field\Groups;
use VDM\Joomla\Componentbuilder\Compiler\Field\Name;
use VDM\Joomla\Componentbuilder\Compiler\Field\TypeName;
use VDM\Joomla\Componentbuilder\Compiler\Field\Attributes;
use VDM\Joomla\Componentbuilder\Compiler\Field\ModalSelect;
use VDM\Joomla\Componentbuilder\Compiler\Creator\CustomFieldTypeFile;
use VDM\Joomla\Componentbuilder\Compiler\Utilities\Counter;
use VDM\Joomla\Utilities\ArrayHelper;
@@ -94,6 +95,14 @@ final class FieldString implements Fieldtypeinterface
*/
protected Attributes $attributes;
/**
* The ModalSelect Class.
*
* @var ModalSelect
* @since 5.2.1
*/
protected ModalSelect $modalselect;
/**
* The CustomFieldTypeFile Class.
*
@@ -120,6 +129,7 @@ final class FieldString implements Fieldtypeinterface
* @param Name $name The Name Class.
* @param TypeName $typename The TypeName Class.
* @param Attributes $attributes The Attributes Class.
* @param ModalSelect $modalselect The ModalSelect Class.
* @param CustomFieldTypeFile $customfieldtypefile The CustomFieldTypeFile Class.
* @param Counter $counter The Counter Class.
*
@@ -127,7 +137,7 @@ final class FieldString implements Fieldtypeinterface
*/
public function __construct(Config $config, Language $language, Field $field,
Groups $groups, Name $name, TypeName $typename,
Attributes $attributes,
Attributes $attributes, ModalSelect $modalselect,
CustomFieldTypeFile $customfieldtypefile,
Counter $counter)
{
@@ -138,6 +148,7 @@ final class FieldString implements Fieldtypeinterface
$this->name = $name;
$this->typename = $typename;
$this->attributes = $attributes;
$this->modalselect = $modalselect;
$this->customfieldtypefile = $customfieldtypefile;
$this->counter = $counter;
}
@@ -591,6 +602,13 @@ final class FieldString implements Fieldtypeinterface
{
//reset options array
$r_optionArray = array();
// special treatment for Modal Select
if ($r_typeName === 'ModalSelect')
{
$r_fieldValues['custom'] = $this->modalselect->extract($r_fieldValues);
}
if ($this->groups->check(
$r_typeName, 'option'
))
@@ -740,7 +758,7 @@ final class FieldString implements Fieldtypeinterface
$fieldData['settings']
))
{
$r_name = $this->name->get(
$r_name = $this->name->get(
$fieldData, $nameListCode, $_resolverKey
);
$r_typeName = $this->typename->get($fieldData);
@@ -763,6 +781,13 @@ final class FieldString implements Fieldtypeinterface
{
//reset options array
$r_optionArray = array();
// special treatment for Modal Select
if ($r_typeName === 'ModalSelect')
{
$r_fieldValues['custom'] = $this->modalselect->extract($r_fieldValues);
}
if ($this->groups->check(
$r_typeName, 'option'
))

View File

@@ -19,6 +19,7 @@ use VDM\Joomla\Componentbuilder\Compiler\Field\Groups;
use VDM\Joomla\Componentbuilder\Compiler\Field\Name;
use VDM\Joomla\Componentbuilder\Compiler\Field\TypeName;
use VDM\Joomla\Componentbuilder\Compiler\Field\Attributes;
use VDM\Joomla\Componentbuilder\Compiler\Field\ModalSelect;
use VDM\Joomla\Componentbuilder\Compiler\Utilities\Xml;
use VDM\Joomla\Componentbuilder\Compiler\Creator\CustomFieldTypeFile;
use VDM\Joomla\Componentbuilder\Compiler\Utilities\Counter;
@@ -93,6 +94,14 @@ final class FieldXML implements Fieldtypeinterface
*/
protected Attributes $attributes;
/**
* The ModalSelect Class.
*
* @var ModalSelect
* @since 5.2.1
*/
protected ModalSelect $modalselect;
/**
* The Xml Class.
*
@@ -127,6 +136,7 @@ final class FieldXML implements Fieldtypeinterface
* @param Name $name The Name Class.
* @param TypeName $typename The TypeName Class.
* @param Attributes $attributes The Attributes Class.
* @param ModalSelect $modalselect The ModalSelect Class.
* @param Xml $xml The Xml Class.
* @param CustomFieldTypeFile $customfieldtypefile The CustomFieldTypeFile Class.
* @param Counter $counter The Counter Class.
@@ -135,8 +145,8 @@ final class FieldXML implements Fieldtypeinterface
*/
public function __construct(Config $config, Language $language, Field $field,
Groups $groups, Name $name, TypeName $typename,
Attributes $attributes, Xml $xml,
CustomFieldTypeFile $customfieldtypefile,
Attributes $attributes, ModalSelect $modalselect,
Xml $xml, CustomFieldTypeFile $customfieldtypefile,
Counter $counter)
{
$this->config = $config;
@@ -146,6 +156,7 @@ final class FieldXML implements Fieldtypeinterface
$this->name = $name;
$this->typename = $typename;
$this->attributes = $attributes;
$this->modalselect = $modalselect;
$this->xml = $xml;
$this->customfieldtypefile = $customfieldtypefile;
$this->counter = $counter;
@@ -571,6 +582,13 @@ final class FieldXML implements Fieldtypeinterface
{
//reset options array
$r_optionArray = array();
// special treatment for Modal Select
if ($r_typeName === 'ModalSelect')
{
$r_fieldValues['custom'] = $this->modalselect->extract($r_fieldValues);
}
if ($this->groups->check(
$r_typeName, 'option'
))
@@ -735,7 +753,7 @@ final class FieldXML implements Fieldtypeinterface
$fieldData['settings']
))
{
$r_name = $this->name->get(
$r_name = $this->name->get(
$fieldData, $nameListCode, $_resolverKey
);
$r_typeName = $this->typename->get($fieldData);
@@ -756,6 +774,13 @@ final class FieldXML implements Fieldtypeinterface
{
//reset options array
$r_optionArray = array();
// special treatment for Modal Select
if ($r_typeName === 'ModalSelect')
{
$r_fieldValues['custom'] = $this->modalselect->extract($r_fieldValues);
}
if ($this->groups->check(
$r_typeName, 'option'
))
@@ -763,11 +788,11 @@ final class FieldXML implements Fieldtypeinterface
// now add to the field set
$this->xml->append(
$form, $this->get(
'option', $r_fieldValues, $r_name,
$r_typeName, $langView,
$nameSingleCode, $nameListCode,
$placeholders, $r_optionArray
)
'option', $r_fieldValues, $r_name,
$r_typeName, $langView,
$nameSingleCode, $nameListCode,
$placeholders, $r_optionArray
)
);
}
elseif ($r_typeName === 'subform')
@@ -789,12 +814,12 @@ final class FieldXML implements Fieldtypeinterface
// now add to the field set
$this->xml->append(
$form, $this->get(
'special', $r_fieldValues,
$r_name, $r_typeName, $langView,
$nameSingleCode,
$nameListCode, $placeholders,
$r_optionArray
)
'special', $r_fieldValues,
$r_name, $r_typeName, $langView,
$nameSingleCode,
$nameListCode, $placeholders,
$r_optionArray
)
);
}
@@ -810,11 +835,11 @@ final class FieldXML implements Fieldtypeinterface
// now add to the field set
$this->xml->append(
$form, $this->get(
'custom', $r_fieldValues, $r_name,
$r_typeName, $langView,
$nameSingleCode, $nameListCode,
$placeholders, $r_optionArray
)
'custom', $r_fieldValues, $r_name,
$r_typeName, $langView,
$nameSingleCode, $nameListCode,
$placeholders, $r_optionArray
)
);
// set lang (just incase)
$r_listLangName = $langView . '_'
@@ -851,11 +876,11 @@ final class FieldXML implements Fieldtypeinterface
// now add to the field set
$this->xml->append(
$form, $this->get(
'plain', $r_fieldValues, $r_name,
$r_typeName, $langView,
$nameSingleCode, $nameListCode,
$placeholders, $r_optionArray
)
'plain', $r_fieldValues, $r_name,
$r_typeName, $langView,
$nameSingleCode, $nameListCode,
$placeholders, $r_optionArray
)
);
}
}

View File

@@ -151,6 +151,11 @@ class Data
*/
public function get($field, ?string $singleViewName = null, ?string $listViewName = null): ?object
{
if (empty($field))
{
return null;
}
if (isset($this->index[$field]))
{
$id = $this->index[$field];

View File

@@ -0,0 +1,150 @@
<?php
/**
* @package Joomla.Component.Builder
*
* @created 4th September, 2022
* @author Llewellyn van der Merwe <https://dev.vdm.io>
* @git Joomla Component Builder <https://git.vdm.dev/joomla/Component-Builder>
* @copyright Copyright (C) 2015 Vast Development Method. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace VDM\Joomla\Componentbuilder\Compiler\Field;
use VDM\Joomla\Componentbuilder\Compiler\Utilities\Structure;
use VDM\Joomla\Componentbuilder\Compiler\Builder\ContentMulti;
/**
* Compiler Field Modal Select
*
* @since 5.2.1
*/
final class ModalSelect
{
/**
* The Structure Class.
*
* @var Structure
* @since 5.2.1
*/
protected Structure $structure;
/**
* The ContentMulti Class.
*
* @var ContentMulti
* @since 5.2.1
*/
protected ContentMulti $contentmulti;
/**
* The switch to ensure the fix is just added once
*
* @var bool
* @since 5.2.1
*/
protected bool $addedFix = false;
/**
* Constructor.
*
* @param Structure $structure The Structure Class.
* @param ContentMulti $contentmulti The ContentMulti Class.
*
* @since 5.2.1
*/
public function __construct(Structure $structure, ContentMulti $contentmulti)
{
$this->structure = $structure;
$this->contentmulti = $contentmulti;
}
/**
* Extracts component and view details from field attributes for a Modal Select field.
*
* @param array $fieldAttributes The field attributes containing URLs and SQL table details.
*
* @return array An associative array with extracted component, view, views, table, id, and text.
* @since 5.1.2
*/
public function extract(array $fieldAttributes): array
{
$component = null;
$views = null;
$view = null;
// Extract parameters from the given URL
$extractParams = function ($url) {
if (empty($url)) {
return ['option' => null, 'view' => null];
}
$query = parse_url($url, PHP_URL_QUERY);
parse_str($query, $params);
return [
'option' => $params['option'] ?? $params['amp;option'] ?? null,
'view' => $params['view'] ?? $params['amp;view'] ?? null,
];
};
// Process URL attributes
foreach (['urlSelect', 'urlEdit', 'urlNew'] as $urlKey)
{
$params = $extractParams($fieldAttributes[$urlKey] ?? '');
$component ??= $params['option'];
if ($urlKey === 'urlSelect')
{
$views ??= $params['view'];
}
else
{
$view ??= $params['view'];
}
}
// Determine the target table and extract the view name
$field_target_table = $fieldAttributes['sql_title_table'] ?? '';
if (!$view && !empty($field_target_table))
{
$clean_table = str_replace('__', '', $field_target_table);
$view = substr($clean_table, strpos($clean_table, '_') + 1);
}
$sql_title_key = $fieldAttributes['sql_title_key'] ?? 'id';
// if one field is not id, we add an override to the ModalSelectField as a FIX
if (!$this->addedFix && $sql_title_key !== 'id')
{
$this->structure->build(
['admin' => 'fieldmodalselect_override'],
'fieldmodalselect_override'
);
$this->structure->build(
['site' => 'fieldmodalselect_override'],
'fieldmodalselect_override'
);
// to make sure the file is updated TODO
$this->contentmulti->set('fieldmodalselect_override|BLABLA', 'blabla');
$this->addedFix = true;
}
return [
'modal_select' => true,
'urlSelect' => $fieldAttributes['urlSelect'] ?? '',
'hint' => $fieldAttributes['hint'] ?? '',
'titleSelect' => $fieldAttributes['titleSelect'] ?? '',
'iconSelect' => $fieldAttributes['iconSelect'] ?? '',
'table' => $field_target_table,
'id' => $sql_title_key,
'text' => $fieldAttributes['sql_title_column'] ?? '',
'component' => $component ?? 'error',
'view' => $view ?? 'error',
'views' => $views ?? 'error',
'button' => false,
'extends' => ''
];
}
}

View File

@@ -418,7 +418,7 @@ class Compiler extends Infusion
// first we do the static files
foreach (CFactory::_('Utilities.Files')->get('static') as $static)
{
if (File::exists($static['path']))
if (is_file($static['path']))
{
$this->setFileContent(
$static['name'], $static['path'], $bom
@@ -434,7 +434,7 @@ class Compiler extends Infusion
{
if ($file['view'] == $view)
{
if (File::exists($file['path']))
if (is_file($file['path']))
{
$this->setFileContent(
$file['name'], $file['path'], $bom,
@@ -513,7 +513,7 @@ class Compiler extends Infusion
// update the module files
foreach (CFactory::_('Utilities.Files')->get($module->key) as $module_file)
{
if (File::exists($module_file['path']))
if (is_file($module_file['path']))
{
$this->setFileContent(
$module_file['name'], $module_file['path'],
@@ -591,7 +591,7 @@ class Compiler extends Infusion
// update the plugin files
foreach (CFactory::_('Utilities.Files')->get($plugin->key) as $plugin_file)
{
if (File::exists($plugin_file['path']))
if (is_file($plugin_file['path']))
{
$this->setFileContent(
$plugin_file['name'], $plugin_file['path'],
@@ -625,7 +625,7 @@ class Compiler extends Infusion
// update the power files
foreach (CFactory::_('Utilities.Files')->get($power->key) as $power_file)
{
if (File::exists($power_file['path']))
if (is_file($power_file['path']))
{
$this->setFileContent(
$power_file['name'], $power_file['path'],
@@ -650,7 +650,7 @@ class Compiler extends Infusion
// update the power files
foreach (CFactory::_('Utilities.Files')->get($key) as $power_file)
{
if (File::exists($power_file['path']))
if (is_file($power_file['path']))
{
$this->setFileContent(
$power_file['name'], $power_file['path'],
@@ -775,7 +775,7 @@ class Compiler extends Infusion
$update_server_xml_path = CFactory::_('Utilities.Paths')->component_path . '/'
. $this->updateServerFileName . '.xml';
// make sure we have the correct file
if (File::exists($update_server_xml_path)
if (is_file($update_server_xml_path)
&& ($update_server = CFactory::_('Component')->get('update_server')) !== null)
{
// move to server
@@ -811,7 +811,7 @@ class Compiler extends Infusion
&& is_numeric($module->update_server)
&& $module->update_server > 0
&& isset($module->update_server_xml_path)
&& File::exists($module->update_server_xml_path)
&& is_file($module->update_server_xml_path)
&& isset($module->update_server_xml_file_name)
&& StringHelper::check(
$module->update_server_xml_file_name
@@ -852,7 +852,7 @@ class Compiler extends Infusion
&& is_numeric($plugin->update_server)
&& $plugin->update_server > 0
&& isset($plugin->update_server_xml_path)
&& File::exists($plugin->update_server_xml_path)
&& is_file($plugin->update_server_xml_path)
&& isset($plugin->update_server_xml_file_name)
&& StringHelper::check(
$plugin->update_server_xml_file_name
@@ -952,7 +952,7 @@ class Compiler extends Infusion
if (('README.md' === $static['name']
|| 'README.txt' === $static['name'])
&& CFactory::_('Component')->get('addreadme')
&& File::exists($static['path']))
&& is_file($static['path']))
{
$this->setReadMe($static['path']);
$two++;
@@ -1467,7 +1467,7 @@ class Compiler extends Infusion
}
$counter = 0;
// check if file exist
if (File::exists($file))
if (is_file($file))
{
foreach (
new \SplFileObject($file) as $lineNumber => $lineContent

View File

@@ -1326,6 +1326,30 @@ class Fields extends Structure
. $filter['type'] . '"';
// set css classname of this field
$filter_class = ucfirst((string) $filter['type']);
// if this is a modal_select field
$modal_select = $filter['custom']['modal_select'] ?? null;
if ($modal_select)
{
$field_filter_sets[] = Indent::_(3) . 'sql_title_table="'
. $filter['custom']['table'] . '"';
$field_filter_sets[] = Indent::_(3) . 'sql_title_column="'
. $filter['custom']['text'] . '"';
$field_filter_sets[] = Indent::_(3) . 'sql_title_key="'
. $filter['custom']['id'] . '"';
$field_filter_sets[] = Indent::_(3) . 'urlSelect="'
. $filter['custom']['urlSelect'] . '"';
$field_filter_sets[] = Indent::_(3) . 'hint="'
. $filter['custom']['hint'] . '"';
$field_filter_sets[] = Indent::_(3) . 'titleSelect="'
. $filter['custom']['titleSelect'] . '"';
$field_filter_sets[] = Indent::_(3) . 'iconSelect="'
. $filter['custom']['iconSelect'] . '"';
$field_filter_sets[] = Indent::_(3) . 'select="true"';
$field_filter_sets[] = Indent::_(3) . 'edit="false"';
$field_filter_sets[] = Indent::_(3) . 'clear="true"';
$field_filter_sets[] = Indent::_(3) . 'onchange="form.submit()"';
}
}
else
{

View File

@@ -445,6 +445,11 @@ class Infusion extends Interpretation
$this->setAddToolBar($view)
);
// ADDMODALTOOLBAR <<<DYNAMIC>>>
CFactory::_('Compiler.Builder.Content.Multi')->set($nameSingleCode . '|ADDMODALTOOLBAR',
$this->setAddModalToolBar($view)
);
// set the script for this view
$this->buildTheViewScript($view);
@@ -945,6 +950,14 @@ class Infusion extends Interpretation
)
);
// VIEWS_MODAL_BODY <<<DYNAMIC>>>
CFactory::_('Compiler.Builder.Content.Multi')->set($nameListCode . '|VIEWS_MODAL_BODY',
$this->setModalViewsBody(
$nameSingleCode,
$nameListCode
)
);
// LISTHEAD <<<DYNAMIC>>>
CFactory::_('Compiler.Builder.Content.Multi')->set($nameListCode . '|LISTHEAD',
$this->setListHead(
@@ -1046,6 +1059,13 @@ class Infusion extends Interpretation
)
);
// ADMIN_VIEWS_MODAL_HEADER <<<DYNAMIC>>> add the header details for the views
CFactory::_('Compiler.Builder.Content.Multi')->set($nameListCode . '|ADMIN_VIEWS_MODAL_HEADER',
CFactory::_('Header')->get(
'admin.views.modal', $nameListCode
)
);
// API_VIEWS_CONTROLLER_HEADER <<<DYNAMIC>>> add the header details for the controller
CFactory::_('Compiler.Builder.Content.Multi')->set($nameListCode . '|API_VIEWS_CONTROLLER_HEADER',
CFactory::_('Header')->get(

View File

@@ -679,7 +679,7 @@ class Structure extends Get
!isset($this->extentionTrackingFilesMoved[$check]))
{
// check files exist
if (File::exists(
if (is_file(
CFactory::_('Utilities.Paths')->component_path . '/admin/models/fields/'
. $field['type_name'] . '.php'
))
@@ -707,7 +707,7 @@ class Structure extends Get
!isset($this->extentionTrackingFilesMoved[$check]))
{
// check files exist
if (File::exists(
if (is_file(
CFactory::_('Utilities.Paths')->component_path . '/admin/models/rules/'
. CFactory::_('Registry')->get('validation.linked.' . $field['field'])
. '.php'

View File

@@ -426,6 +426,8 @@ final class Header implements HeaderInterface
$headers[] = 'use Joomla\Input\Input;';
break;
case 'admin.views.modal':
$headers[] = 'use Joomla\CMS\Session\Session;';
case 'admin.views':
$headers[] = 'use Joomla\CMS\HTML\HTMLHelper as Html;';
$headers[] = 'use Joomla\CMS\Layout\LayoutHelper;';

View File

@@ -426,6 +426,8 @@ final class Header implements HeaderInterface
$headers[] = 'use Joomla\Input\Input;';
break;
case 'admin.views.modal':
$headers[] = 'use Joomla\CMS\Session\Session;';
case 'admin.views':
$headers[] = 'use Joomla\CMS\HTML\HTMLHelper as Html;';
$headers[] = 'use Joomla\CMS\Layout\LayoutHelper;';

View File

@@ -63,9 +63,9 @@ class Sql
*/
public function set(object &$item)
{
if (isset($item->add_sql) && $item->add_sql == 1 && isset($item->source))
if (isset($item->add_sql) && (int) $item->add_sql === 1 && isset($item->source))
{
if ($item->source == 1 && isset($item->tables) &&
if ((int) $item->source === 1 && isset($item->tables) &&
($string = $this->dump->get(
$item->tables, $item->name_single_code, $item->guid
)) !== null)
@@ -77,7 +77,7 @@ class Sql
$this->dispenser->hub['sql'][$item->name_single_code]
= $string;
}
elseif ($item->source == 2 && isset($item->sql))
elseif ((int) $item->source === 2 && isset($item->sql))
{
// add the SQL dump string
$this->dispenser->set(

View File

@@ -199,82 +199,88 @@ class Sqldump
else
{
// see where
// var_dump($view);
// jexit();
// var_dump($view, $view_guid);
// exit;
}
}
// check if we should run query
if ($run_query)
{
// now get the data
$this->db->setQuery($query);
$this->db->execute();
if ($this->db->getNumRows())
{
// get the data
$data = $this->db->loadObjectList();
// start building the MySql dump
$dump = "--";
$dump .= PHP_EOL . "-- Dumping data for table `#__"
. Placefix::_("component") . "_" . $view
. "`";
$dump .= PHP_EOL . "--";
$dump .= PHP_EOL . PHP_EOL . "INSERT INTO `#__" . Placefix::_("component") . "_" . $view . "` (";
foreach ($data as $line)
try{
// now get the data
$this->db->setQuery($query);
$this->db->execute();
if ($this->db->getNumRows())
{
$comaSet = 0;
foreach ($line as $fieldName => $fieldValue)
// get the data
$data = $this->db->loadObjectList();
// start building the MySql dump
$dump = "--";
$dump .= PHP_EOL . "-- Dumping data for table `#__"
. Placefix::_("component") . "_" . $view
. "`";
$dump .= PHP_EOL . "--";
$dump .= PHP_EOL . PHP_EOL . "INSERT INTO `#__" . Placefix::_("component") . "_" . $view . "` (";
foreach ($data as $line)
{
if ($comaSet == 0)
$comaSet = 0;
foreach ($line as $fieldName => $fieldValue)
{
$dump .= $this->db->quoteName($fieldName);
if ($comaSet == 0)
{
$dump .= $this->db->quoteName($fieldName);
}
else
{
$dump .= ", " . $this->db->quoteName(
$fieldName
);
}
$comaSet++;
}
break;
}
$dump .= ") VALUES";
$coma = 0;
foreach ($data as $line)
{
if ($coma == 0)
{
$dump .= PHP_EOL . "(";
}
else
{
$dump .= ", " . $this->db->quoteName(
$fieldName
);
$dump .= "," . PHP_EOL . "(";
}
$comaSet++;
}
break;
}
$dump .= ") VALUES";
$coma = 0;
foreach ($data as $line)
{
if ($coma == 0)
{
$dump .= PHP_EOL . "(";
}
else
{
$dump .= "," . PHP_EOL . "(";
}
$comaSet = 0;
foreach ($line as $fieldName => $fieldValue)
{
if ($comaSet == 0)
$comaSet = 0;
foreach ($line as $fieldName => $fieldValue)
{
$dump .= $this->escape($fieldValue);
if ($comaSet == 0)
{
$dump .= $this->escape($fieldValue);
}
else
{
$dump .= ", " . $this->escape(
$fieldValue
);
}
$comaSet++;
}
else
{
$dump .= ", " . $this->escape(
$fieldValue
);
}
$comaSet++;
$dump .= ")";
$coma++;
}
$dump .= ")";
$coma++;
}
$dump .= ";";
$dump .= ";";
// return build dump query
return $dump;
// return build dump query
return $dump;
}
} catch (\Throwable $e) {
// see where
// var_dump($view, $view_guid);
// exit;
}
}
}

View File

@@ -78,13 +78,13 @@ class Updatesql
{
$newItem = true;
// check if this is an id to ignore
// check if this is an id/guid to ignore
if (ArrayHelper::check($ignore)
&& in_array(
$item, $ignore
))
{
// don't add ignored ids
// don't add ignored ids/guids
$newItem = false;
}
// check if this is old repeatable field
@@ -142,11 +142,11 @@ class Updatesql
// search to see if this is a new value
$newItem = true;
// check if this is an id to ignore
// check if this is an id/guid to ignore
if (ArrayHelper::check($ignore)
&& in_array($item[$type], $ignore))
{
// don't add ignored ids
// don't add ignored ids/guids
$newItem = false;
}
// check if this is old repeatable field

View File

@@ -773,6 +773,7 @@ class Creator implements ServiceProviderInterface
$container->get('Field.Name'),
$container->get('Field.Type.Name'),
$container->get('Field.Attributes'),
$container->get('Field.ModalSelect'),
$container->get('Utilities.Xml'),
$container->get('Compiler.Creator.Custom.Field.Type.File'),
$container->get('Utilities.Counter')
@@ -797,6 +798,7 @@ class Creator implements ServiceProviderInterface
$container->get('Field.Name'),
$container->get('Field.Type.Name'),
$container->get('Field.Attributes'),
$container->get('Field.ModalSelect'),
$container->get('Compiler.Creator.Custom.Field.Type.File'),
$container->get('Utilities.Counter')
);
@@ -816,6 +818,7 @@ class Creator implements ServiceProviderInterface
$container->get('Field.Name'),
$container->get('Field.Type.Name'),
$container->get('Field.Attributes'),
$container->get('Field.ModalSelect'),
$container->get('Field.Groups'),
$container->get('Compiler.Builder.Field.Names'),
$container->get('Compiler.Creator.Field.Type'),

View File

@@ -20,6 +20,7 @@ use VDM\Joomla\Componentbuilder\Compiler\Field\Data;
use VDM\Joomla\Componentbuilder\Compiler\Field\Groups;
use VDM\Joomla\Componentbuilder\Compiler\Field\Attributes;
use VDM\Joomla\Componentbuilder\Compiler\Field\Name;
use VDM\Joomla\Componentbuilder\Compiler\Field\ModalSelect;
use VDM\Joomla\Componentbuilder\Compiler\Field\TypeName;
use VDM\Joomla\Componentbuilder\Compiler\Field\UniqueName;
use VDM\Joomla\Componentbuilder\Compiler\Field\Rule;
@@ -83,6 +84,9 @@ class Field implements ServiceProviderInterface
$container->alias(Name::class, 'Field.Name')
->share('Field.Name', [$this, 'getName'], true);
$container->alias(ModalSelect::class, 'Field.ModalSelect')
->share('Field.ModalSelect', [$this, 'getModalSelect'], true);
$container->alias(TypeName::class, 'Field.Type.Name')
->share('Field.Type.Name', [$this, 'getTypeName'], true);
@@ -214,6 +218,22 @@ class Field implements ServiceProviderInterface
);
}
/**
* Get The ModalSelect Class.
*
* @param Container $container The DI container.
*
* @return ModalSelect
* @since 5.2.1
*/
public function getModalSelect(Container $container): ModalSelect
{
return new ModalSelect(
$container->get('Utilities.Structure'),
$container->get('Compiler.Builder.Content.Multi')
);
}
/**
* Get The TypeName Class.
*

View File

@@ -50,7 +50,7 @@ final class RowDataArray implements RowDataProcessorInterface
foreach ($cellIterator as $cell)
{
$rowData['values'][$cell->getColumn()] = $cell->getValueString();
$rowData['values'][$cell->getColumn()] = (string) $cell->getValue();
}
return $rowData;

View File

@@ -101,7 +101,7 @@ final class MultiSubform implements MultiSubformInterface
* $items,
* $setMap = [
* '_core' => [
* 'table' =>'data',
* 'table' => 'data',
* 'indexKey' => 'guid',
* 'linkKey' => 'look',
* 'linkValue' => $data['guid'] ?? ''

View File

@@ -147,36 +147,43 @@ abstract class FileHelper
}
/**
* Write a file to the server
* Write a file to the server safely and efficiently.
* This function will always overwrite the existing file with new data.
*
* @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
* @param string $path The full path and file name where to save the data.
* @param string $data The data to save.
*
* @return bool Returns true on success, false on failure.
* @since 3.0.9
*/
public static function write($path, $data): bool
{
$klaar = false;
if (StringHelper::check($data))
if (!is_string($path) || !is_string($data) || trim($path) === '')
{
// open the file
$fh = fopen($path, "w");
if (!is_resource($fh))
{
return $klaar;
}
// write to the file
if (fwrite($fh, $data))
{
// has been done
$klaar = true;
}
// close file.
fclose($fh);
return false;
}
return $klaar;
$handle = null;
$success = false;
try {
$handle = fopen($path, "wb"); // Open in write-binary mode to ensure full overwrite
if (!is_resource($handle))
{
return false;
}
$success = fwrite($handle, $data) !== false;
} catch (\Throwable $e) {
return false;
} finally {
if (is_resource($handle))
{
fclose($handle);
}
}
return $success;
}
/**

View File

@@ -29,7 +29,14 @@ abstract class TypeHelper
*
* @since 3.0.9
*/
protected static $builder = false;
protected static int $builder = 0;
/**
* Cache for processed field type names to avoid redundant computations.
*
* @since 5.1.1
*/
protected static array $cache = [];
/**
* Making field type name safe
@@ -41,42 +48,144 @@ abstract class TypeHelper
*
* @since 3.0.9
*/
public static function safe($string, $option = null)
public static function safe($string, $option = null): string
{
// get global value
if (self::$builder === false)
// Check if result is already cached
if (isset(self::$cache[$string]))
{
self::$builder = Helper::getParams($option)->get('type_name_builder', 1);
return self::$cache[$string];
}
// use the new convention
// get global value
if (self::$builder == 0)
{
self::$builder = (int) Helper::getParams($option)->get('type_name_builder', 2);
}
// Apply new naming convention
if (2 == self::$builder)
{
// 0nly continue if we have a string
// Ensure input is a valid string
if (StringHelper::check($string))
{
// check that the first character is not a number
// Ensure the first character is not a number
if (is_numeric(substr($string, 0, 1)))
{
$string = StringHelper::numbers($string);
}
// Transliterate string
$string = StringHelper::transliterate($string);
// Detect if camel case is used
$has_camel_case = preg_match('/[a-z][A-Z]/', $string);
// remove all and keep only characters and numbers and point (TODO just one point)
$string = trim(preg_replace("/[^A-Za-z0-9_\.]/", '', (string) $string));
// Detect non-ASCII characters (extended Unicode)
$has_non_ascii = preg_match('/[^\x20-\x7E]/', $string); // Anything outside printable ASCII
// best is to return lower (for all string equality in compiler)
return strtolower($string);
// Detect non-standard symbols (anything that is not a letter, number, underscore, or dot)
$has_non_standard_symbols = preg_match('/[^a-zA-Z0-9_.]/', $string);
// Only transliterate if necessary:
// - If it contains non-ASCII characters, or
// - If it has non-standard symbols that may affect safe usage
if ($has_non_ascii || $has_non_standard_symbols)
{
// Store original casing before transliteration
$original_casing = $string;
// Run transliteration
$string = StringHelper::transliterate($string);
// If original had camel case and transliterate altered casing, restore it
if ($has_camel_case)
{
// Use original casing where possible, as Joomla's transliteration might enforce lowercase
$string = self::restoreCamelCase($original_casing, $string);
}
}
// Clean the string while preserving camelCase
$cleaned = self::clean($string);
// Cache the result
self::$cache[$string] = $cleaned;
return $cleaned;
}
// not a string
return '';
}
// use the default (original behaviour/convention)
return StringHelper::safe($string);
// Default behaviour for legacy convention
$result = StringHelper::safe($string);
// Cache the result
self::$cache[$string] = $result;
return $result;
}
/**
* Restores the camel case pattern from the original string after transliteration.
*
* @param string $original The original string before transliteration.
* @param string $modified The string after transliteration.
*
* @return string The string with restored camel case.
* @since 5.1.1
*/
protected static function restoreCamelCase(string $original, string $modified): string
{
// Split original and modified strings into character arrays
$origChars = preg_split('//u', $original, -1, PREG_SPLIT_NO_EMPTY);
$modChars = preg_split('//u', $modified, -1, PREG_SPLIT_NO_EMPTY);
// Ensure lengths match before processing
if (count($origChars) !== count($modChars))
{
return $modified; // Return as-is if lengths differ (indicating complex transformation)
}
// Restore camel case where applicable
foreach ($origChars as $index => $char)
{
if (ctype_upper($char))
{
$modChars[$index] = strtoupper($modChars[$index]);
}
}
return implode('', $modChars);
}
/**
* Cleans type name string by:
* - Removing all characters except letters, numbers, underscores, and periods.
* - Allowing only one period (the first period in the string).
*
* @param string $string Type name string to clean.
*
* @return string Cleaned string with only one period (first occurrence).
* @since 5.1.1
*/
protected static function clean(string $string): string
{
// Step 1: Remove unwanted characters (allow letters, numbers, underscores, and periods)
$string = preg_replace('/[^A-Za-z0-9_.]/', '', $string);
// Step 2: Keep only the first period, remove all subsequent periods
$parts = explode('.', $string, 2); // Split on first period only
if (count($parts) === 2)
{
// Reconstruct string: first part + '.' + remove any further periods from second part
$cleaned_string = $parts[0] . '.' . str_replace('.', '', $parts[1]);
}
else
{
// No period or just one part, no further action needed
$cleaned_string = $string;
}
return trim($cleaned_string);
}
}

View File

@@ -12,8 +12,10 @@
namespace VDM\Joomla\Utilities;
use Joomla\CMS\Factory;
use Joomla\Filter\InputFilter;
use Joomla\CMS\Language\Language;
use Joomla\CMS\Language\LanguageFactoryInterface;
use Joomla\CMS\Language\LanguageFactory;
use VDM\Joomla\Utilities\Component\Helper;
@@ -27,188 +29,219 @@ abstract class StringHelper
/**
* The Main Active Language
*
* @var string
*
* @var string
* @since 3.0.9
*/
public static $langTag;
/**
* Check if we have a string with a length
* Validate that input is a non-empty, non-whitespace-only string.
*
* @input string $string The string to check
* @param mixed $input The input value to validate.
*
* @returns bool true on success
*
* @returns bool True if input is a non-empty, non-whitespace-only string, otherwise false.
* @since 3.0.9
*/
public static function check($string): bool
public static function check($input): bool
{
return is_string($string) && strlen($string) > 0;
return is_string($input) && trim($input) !== '';
}
/**
* Shorten a string
* Shortens a string to a specified length, optionally adding a tooltip with the full text.
*
* @input string The sting that you would like to shorten
* This method safely shortens the input string without cutting words abruptly. If the string
* exceeds the specified length, ellipses (...) are added. Optionally, a tooltip containing the
* longer original string can be included.
*
* @returns string on success
*
* @since 3.2.0
* @param mixed $string The string you would like to shorten.
* @param int $length The maximum length for the shortened string. Default is 40.
* @param bool $addTip Whether to add a tooltip with the original longer string. Default true.
*
* @return string|mixed The shortened string, optionally with a tooltip. Or original value passed
* @since 3.2.1
*/
public static function shorten($string, $length = 40, $addTip = true)
public static function shorten($string, int $length = 40, bool $addTip = true)
{
if (self::check($string))
// Validate string input and return original if invalid or short enough.
if (!self::check($string) || mb_strlen($string) <= $length)
{
$initial = strlen((string) $string);
$words = preg_split('/([\s\n\r]+)/', (string) $string, -1, PREG_SPLIT_DELIM_CAPTURE);
$words_count = count((array)$words);
$word_length = 0;
$last_word = 0;
for (; $last_word < $words_count; ++$last_word)
{
$word_length += strlen($words[$last_word]);
if ($word_length > $length)
{
break;
}
}
$newString = implode(array_slice($words, 0, $last_word));
$final = strlen($newString);
if ($initial !== $final && $addTip)
{
$title = self::shorten($string, 400 , false);
return '<span class="hasTip" title="' . $title . '" style="cursor:help">' . trim($newString) . '...</span>';
}
elseif ($initial !== $final && !$addTip)
{
return trim($newString) . '...';
}
return $string;
}
return $string;
// Truncate string to nearest word boundary
$shortened = mb_substr($string, 0, $length);
// Find the last space to avoid cutting off a word
$lastSpace = mb_strrpos($shortened, ' ');
if ($lastSpace !== false)
{
$shortened = mb_substr($shortened, 0, $lastSpace);
}
// Prepare trimmed and shortened output with ellipses
$shortened = trim($shortened) . '...';
// Add tooltip if requested
if ($addTip)
{
// Safely escape output for HTML
$title = self::shorten($string, 400 , false);
$escapedTitle = htmlspecialchars($title, ENT_QUOTES, 'UTF-8');
$escapedShort = htmlspecialchars($shortened, ENT_QUOTES, 'UTF-8');
return '<span class="hasTip" title="' . $escapedTitle . '" style="cursor:help">'
. $escapedShort
. '</span>';
}
// Return shortened version without tooltip
return $shortened;
}
/**
* Making strings safe (various ways)
* Makes a string safe by sanitizing and formatting it according to the specified type.
*
* @input string The you would like to make safe
* This method can remove unwanted characters, transliterate text, replace numbers with
* their English equivalents, and apply different case formatting styles.
*
* @returns string on success
*
* @param string $string The string to sanitize and format.
* @param string $type The formatting type to apply. Supported values:
* - 'filename' : Removes special characters and extra spaces.
* - 'L' : Converts to lowercase with underscores replacing spaces.
* - 'strtolower': Alias for 'L'.
* - 'W' : Capitalizes the first letter of each word.
* - 'w' : Converts to lowercase (spaces remain).
* - 'word' : Alias for 'w'.
* - 'Ww' : Capitalizes only the first word.
* - 'Word' : Alias for 'Ww'.
* - 'WW' : Converts the entire string to uppercase.
* - 'WORD' : Alias for 'WW'.
* - 'U' : Converts to uppercase with underscores replacing spaces.
* - 'strtoupper': Alias for 'U'.
* - 'F' : Capitalizes only the first letter of the entire string.
* - 'ucfirst' : Alias for 'F'.
* - 'cA' : Converts to camelCase.
* - 'cAmel' : Alias for 'cA'.
* - 'camelcase' : Alias for 'cA'.
* @param string $spacer The character to replace spaces with (default: '_').
* @param bool $replaceNumbers Whether to replace numbers with their English text equivalents (default: true).
* @param bool $keepOnlyCharacters Whether to remove all non-alphabetic characters (default: true).
*
* @return string The sanitized and formatted string.
* @since 3.0.9
*/
public static function safe($string, $type = 'L', $spacer = '_', $replaceNumbers = true, $keepOnlyCharacters = true)
public static function safe($string, string $type = 'L', string $spacer = '_', bool $replaceNumbers = true, bool $keepOnlyCharacters = true): string
{
if ($replaceNumbers === true)
if ($replaceNumbers)
{
// remove all numbers and replace with English text version (works well only up to millions)
$string = self::numbers($string);
}
// 0nly continue if we have a string
if (self::check($string))
{
// create file name without the extension that is safe
if ($type === 'filename')
{
// make sure VDM is not in the string
$string = str_replace('VDM', 'vDm', (string) $string);
// Remove anything which isn't a word, whitespace, number
// or any of the following caracters -_()
// If you don't need to handle multi-byte characters
// you can use preg_replace rather than mb_ereg_replace
// Thanks @Łukasz Rysiak!
// $string = mb_ereg_replace("([^\w\s\d\-_\(\)])", '', $string);
$string = preg_replace("([^\w\s\d\-_\(\)])", '', $string);
// http://stackoverflow.com/a/2021729/1429677
return preg_replace('/\s+/', ' ', (string) $string);
}
// remove all other characters
$string = trim((string) $string);
$string = preg_replace('/'.$spacer.'+/', ' ', $string);
$string = preg_replace('/\s+/', ' ', $string);
// Transliterate string
$string = self::transliterate($string);
// remove all and keep only characters
if ($keepOnlyCharacters)
{
$string = preg_replace("/[^A-Za-z ]/", '', (string) $string);
}
// keep both numbers and characters
else
{
$string = preg_replace("/[^A-Za-z0-9 ]/", '', (string) $string);
}
// select final adaptations
if ($type === 'L' || $type === 'strtolower')
{
// replace white space with underscore
$string = preg_replace('/\s+/', (string) $spacer, (string) $string);
// default is to return lower
return strtolower($string);
}
elseif ($type === 'W')
{
// return a string with all first letter of each word uppercase(no underscore)
return ucwords(strtolower($string));
}
elseif ($type === 'w' || $type === 'word')
{
// return a string with all lowercase(no underscore)
return strtolower($string);
}
elseif ($type === 'Ww' || $type === 'Word')
{
// return a string with first letter of the first word uppercase and all the rest lowercase(no underscore)
return ucfirst(strtolower($string));
}
elseif ($type === 'WW' || $type === 'WORD')
{
// return a string with all the uppercase(no underscore)
return strtoupper($string);
}
elseif ($type === 'U' || $type === 'strtoupper')
{
// replace white space with underscore
$string = preg_replace('/\s+/', (string) $spacer, $string);
// return all upper
return strtoupper($string);
}
elseif ($type === 'F' || $type === 'ucfirst')
{
// replace white space with underscore
$string = preg_replace('/\s+/', (string) $spacer, $string);
// return with first character to upper
return ucfirst(strtolower($string));
}
elseif ($type === 'cA' || $type === 'cAmel' || $type === 'camelcase')
{
// convert all words to first letter uppercase
$string = ucwords(strtolower($string));
// remove white space
$string = preg_replace('/\s+/', '', $string);
// now return first letter lowercase
return lcfirst($string);
}
// return string
return $string;
// Only continue if we have a string
if (!self::check($string))
{
// not a string
return '';
}
// not a string
return '';
// create file name without the extension that is safe
if ($type === 'filename')
{
// make sure VDM is not in the string
$string = str_replace('VDM', 'vDm', (string) $string);
// Remove anything which isn't a word, whitespace, number
// or any of the following caracters -_()
// If you don't need to handle multi-byte characters
// you can use preg_replace rather than mb_ereg_replace
// Thanks @Łukasz Rysiak!
// $string = mb_ereg_replace("([^\w\s\d\-_\(\)])", '', $string);
$string = preg_replace("([^\w\s\d\-_\(\)])", '', $string);
// http://stackoverflow.com/a/2021729/1429677
return preg_replace('/\s+/', ' ', (string) $string);
}
// remove all other characters
$string = trim((string) $string);
$string = preg_replace('/'.$spacer.'+/', ' ', $string);
$string = preg_replace('/\s+/', ' ', $string);
// Transliterate string
$string = self::transliterate($string);
// remove all and keep only characters
if ($keepOnlyCharacters)
{
$string = preg_replace("/[^A-Za-z ]/", '', (string) $string);
}
// keep both numbers and characters
else
{
$string = preg_replace("/[^A-Za-z0-9 ]/", '', (string) $string);
}
// select final adaptations
if ($type === 'L' || $type === 'strtolower')
{
// replace white space with underscore
$string = preg_replace('/\s+/', (string) $spacer, (string) $string);
// default is to return lower
return strtolower($string);
}
elseif ($type === 'W')
{
// return a string with all first letter of each word uppercase(no underscore)
return ucwords(strtolower($string));
}
elseif ($type === 'w' || $type === 'word')
{
// return a string with all lowercase(no underscore)
return strtolower($string);
}
elseif ($type === 'Ww' || $type === 'Word')
{
// return a string with first letter of the first word uppercase and all the rest lowercase(no underscore)
return ucfirst(strtolower($string));
}
elseif ($type === 'WW' || $type === 'WORD')
{
// return a string with all the uppercase(no underscore)
return strtoupper($string);
}
elseif ($type === 'U' || $type === 'strtoupper')
{
// replace white space with underscore
$string = preg_replace('/\s+/', (string) $spacer, $string);
// return all upper
return strtoupper($string);
}
elseif ($type === 'F' || $type === 'ucfirst')
{
// replace white space with underscore
$string = preg_replace('/\s+/', (string) $spacer, $string);
// return with first character to upper
return ucfirst(strtolower($string));
}
elseif ($type === 'cA' || $type === 'cAmel' || $type === 'camelcase')
{
// convert all words to first letter uppercase
$string = ucwords(strtolower($string));
// remove white space
$string = preg_replace('/\s+/', '', $string);
// now return first letter lowercase
return lcfirst($string);
}
// return string
return $string;
}
/**
* Convert none English strings to code usable string
*
* @input an string
* @input $string an string
*
* @returns a string
*
* @since 3.0.9
* @returns string
* @since 3.0.9
*/
public static function transliterate($string)
public static function transliterate($string): string
{
// set tag only once
if (!self::check(self::$langTag))
@@ -217,22 +250,31 @@ abstract class StringHelper
self::$langTag = Helper::getParams()->get('language', 'en-GB');
}
// Transliterate on the language requested
$lang = Language::getInstance(self::$langTag);
/** @var $langFactory LanguageFactory **/
$langFactory = Factory::getContainer()->get(LanguageFactoryInterface::class);
$lang = $langFactory->createLanguage(self::$langTag);
// Transliterate on the language requested
return $lang->transliterate($string);
}
/**
* make sure a string is HTML save
* Ensures a string is safe for HTML output by encoding entities and applying an input filter.
*
* @input an html string
* This method sanitizes the input string, converting special characters to HTML entities
* and applying Joomla's `InputFilter` to remove potentially unsafe HTML.
* Optionally, it can also shorten the string while preserving word integrity.
*
* @returns a string
*
* @since 3.0.9
* @param string $var The input string containing HTML content.
* @param string $charset The character set to use for encoding (default: 'UTF-8').
* @param bool $shorten Whether to shorten the string to a specified length (default: false).
* @param int $length The maximum length for shortening, if enabled (default: 40).
* @param bool $addTip Whether to append a tooltip (ellipsis) when shortening (default: true).
*
* @return string The sanitized and optionally shortened HTML-safe string.
* @since 3.0.9
*/
public static function html($var, $charset = 'UTF-8', $shorten = false, $length = 40, $addTip = true)
public static function html($var, $charset = 'UTF-8', $shorten = false, $length = 40, $addTip = true): string
{
if (self::check($var))
{
@@ -262,13 +304,12 @@ abstract class StringHelper
/**
* Convert all int in a string to an English word string
*
* @input an string with numbers
* @input $string an string with numbers
*
* @returns a string
*
* @returns string|null
* @since 3.0.9
*/
public static function numbers($string)
public static function numbers($string): ?string
{
// set numbers array
$numbers = [];
@@ -300,19 +341,19 @@ abstract class StringHelper
* Convert an integer into an English word string
* Thanks to Tom Nicholson <http://php.net/manual/en/function.strval.php#41988>
*
* @input an int
* @returns a string
* @input $x an int
*
* @returns string
* @since 3.0.9
*/
public static function number($x)
{
$nwords = array( "zero", "one", "two", "three", "four", "five", "six", "seven",
$nwords = ["zero", "one", "two", "three", "four", "five", "six", "seven",
"eight", "nine", "ten", "eleven", "twelve", "thirteen",
"fourteen", "fifteen", "sixteen", "seventeen", "eighteen",
"nineteen", "twenty", 30 => "thirty", 40 => "forty",
50 => "fifty", 60 => "sixty", 70 => "seventy", 80 => "eighty",
90 => "ninety" );
90 => "ninety"];
if(!is_numeric($x))
{
@@ -372,7 +413,7 @@ abstract class StringHelper
}
}
else // millions
{
{
$w .= self::number(floor($x/1000000)) .' million';
$r = fmod($x, 1000000);
if($r > 0)
@@ -392,9 +433,9 @@ abstract class StringHelper
/**
* Random Key
*
* @input int $size The size of the random string
* @input int $size The size of the random string
*
* @returns a string
* @returns string
* @since 3.0.9
*/
public static function random(int $size): string