| Current Path : /home/chauffn/vuzelia/2023/tmp/install_6a8c3a0fa3a1b/src/Platform/ |
| Current File : /home/chauffn/vuzelia/2023/tmp/install_6a8c3a0fa3a1b/src/Platform/Helper.php |
<?php
/**
* @package Helix_Ultimate_Framework
* @author JoomShaper <support@joomshaper.com>
* @copyright Copyright (c) 2010 - 2025 JoomShaper
* @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPLv2 or Later
*/
namespace HelixUltimate\Framework\Platform;
use HelixUltimate\Framework\System\HelixCache;
use HelixUltimate\Framework\System\JoomlaBridge;
use Joomla\CMS\Application\ApplicationHelper;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Factory;
use Joomla\CMS\Filter\InputFilter;
use Joomla\CMS\Language\Multilanguage;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Session\Session;
use Joomla\CMS\Uri\Uri;
use Joomla\Database\DatabaseInterface;
use Joomla\Filesystem\Path;
use Joomla\Registry\Registry;
use Joomla\Utilities\ArrayHelper;
defined('_JEXEC') or die();
/**
* Helix framework helper class
*
* @since 1.0.0
*/
class Helper
{
/**
* Get template styles from Database.
*
* @param integer $id The template ID.
*
* @return object Template data object.
* @since 1.0.0
*/
public static function getTemplateStyle($id = 0)
{
static $cache = [];
if (isset($cache[$id])) {
return $cache[$id];
}
$db = Factory::getContainer()->get(DatabaseInterface::class);
$query = $db->getQuery(true);
$query->select(['*']);
$query->from($db->quoteName('#__template_styles'));
$query->where($db->quoteName('client_id') . ' = 0');
$query->where($db->quoteName('id') . ' = ' . $db->quote($id));
$db->setQuery($query);
$cache[$id] = $db->loadObject();
return $cache[$id];
}
/**
* Get template ID by template name.
*
* @param string $template Template name.
*
* @return integer Template ID.
* @since 2.0.0
*/
public static function getTemplateId($template): int
{
try
{
$db = Factory::getContainer()->get(DatabaseInterface::class);
$query = $db->getQuery(true);
$query->select('id')
->from($db->quoteName('#__template_styles'))
->where($db->quoteName('template') . ' = ' . $db->quote($template));
if (Multilanguage::isEnabled()) {
$query->where($db->quoteName('home') . ' IN(' . $db->quote(Factory::getLanguage()->getTag()) . ', ' . $db->quote('1', false));
}
$db->setQuery($query);
return (int) $db->loadResult();
} catch (\Exception $e) {
return 0;
}
}
/**
* Update Helix template styles.
*
* @param integer $id The helix template ID.
* @param object $data The updated contents.
*
* @return void
* @since 1.0.0
*/
public static function updateTemplateStyle($id = 0, $data = null)
{
if (empty($data)) {
return;
}
$keyOptions = [
'option' => 'com_ajax',
'helix' => 'ultimate',
'status' => 'init',
'id' => $id,
];
$key = self::generateKey($keyOptions);
$cache = new HelixCache($key);
// If cache contains for the $key generated before
if ($cache->contains()) {
$cachedData = $cache->loadData();
$cachedData->params = new Registry($data);
$cache->removeCache()->storeCache($cachedData);
}
$data = json_encode($data);
$db = Factory::getContainer()->get(DatabaseInterface::class);
$query = $db->getQuery(true);
$fields = [$db->quoteName('params') . ' = ' . $db->quote($data)];
$conditions = [
$db->quoteName('id') . ' = ' . $db->quote($id),
$db->quoteName('client_id') . ' = 0',
];
$query->update($db->quoteName('#__template_styles'))->set($fields)->where($conditions);
$db->setQuery($query);
return $db->execute();
}
/**
* Get Helix Template version.
*
* @return string The version number.
* @since 1.0.0
*/
public static function getVersion()
{
$db = Factory::getContainer()->get(DatabaseInterface::class);
$query = $db->getQuery(true);
$query->select(['*']);
$query->from($db->quoteName('#__extensions'));
$query->where($db->quoteName('type') . ' = ' . $db->quote('plugin'))
->where($db->quoteName('element') . ' = ' . $db->quote('helixultimate'))
->where($db->quoteName('folder') . ' = ' . $db->quote('system'));
$db->setQuery($query);
$result = $db->loadObject();
$manifest_cache = json_decode($result->manifest_cache ?? "");
if (isset($manifest_cache->version)) {
return $manifest_cache->version;
}
return;
}
/**
* Check if the data drafted or not.
*
* @return boolean True if the data is drafted, false otherwise
* @since 2.0.0
*/
public static function isDrafted()
{
$app = Factory::getApplication();
$template = $app->getTemplate(true);
$templateId = 0;
if ($app->isClient('site')) {
$templateId = $template->id;
} else {
if ($app->input->get('option') === 'com_ajax' && $app->input->get('helix') === 'ultimate') {
$templateId = $app->input->get('id', 0, 'INT');
}
}
$draftKeyOptions = [
'option' => 'com_ajax',
'helix' => 'ultimate',
'status' => 'draft',
'id' => $templateId,
];
$key = self::generateKey($draftKeyOptions);
$cache = new HelixCache($key);
return $cache->contains();
}
/**
* Generate a md5 cache key from option(s)
*
* @param mixed $options string or array.
*
* @return string Cache key.
* @since 2.0.0
*/
public static function generateKey($options)
{
if (is_array($options)) {
$string = '';
foreach ($options as $key => $option) {
$string .= $key . ':' . $option . ';';
}
} elseif (is_string($options)) {
$string = $options;
} else {
$string = 'helixultimate';
}
return md5($string);
}
private static function checkTemplateStyleValidity(int $id): bool
{
$db = Factory::getContainer()->get(DatabaseInterface::class);
$query = $db->getQuery(true);
$query->select('id')->from($db->quoteName('#__template_styles'))
->where($db->quoteName('id') . ' = ' . $id);
$db->setQuery($query);
$result = $db->loadResult();
return isset($result);
}
/**
* Load template data from cache or database.
*
* @return object Template style object
* @since 2.0.0
*/
public static function loadTemplateData()
{
$templateId = 0;
$app = Factory::getApplication();
if ($app->isClient('site')) {
$currentTemplate = $app->getTemplate(true);
$templateId = $currentTemplate->id ?? 0;
/**
* If a page/menu is assigned to a specific template
* then get the template ID.
*/
$activeMenu = $app->getMenu()->getActive();
if (! empty($activeMenu) && ! empty($activeMenu->template_style_id)) {
$templateId = $activeMenu->template_style_id;
}
} else {
if ($app->input->get('option') === 'com_ajax' && $app->input->get('helix') === 'ultimate') {
$templateId = $app->input->get('id', 0, 'INT');
}
}
if (empty($templateId)) {
$templateId = $app->input->get('helix_id', 0, 'INT');
}
if ($templateId) {
$template = [];
$draftKeyOptions = [
'option' => 'com_ajax',
'helix' => 'ultimate',
'status' => 'draft',
'id' => $templateId,
];
$draftKey = self::generateKey($draftKeyOptions);
$cache = new HelixCache($draftKey);
/**
* Check the fetch destination. If it is iframe then load the settings
* from draft, otherwise if it is document that means this request
* comes from the original site visit. So load from saved cache.
*/
$requestFromIframe = $app->input->get('helixMode', '') === 'edit';
if ($cache->contains() && $requestFromIframe) {
$template = $cache->loadData();
} else {
$keyOptions = [
'option' => 'com_ajax',
'helix' => 'ultimate',
'status' => 'init',
'id' => $templateId,
];
$key = self::generateKey($keyOptions);
$cache->setCacheKey($key);
if ($cache->contains()) {
$template = $cache->loadData();
} else {
$template = self::getTemplateStyle($templateId);
}
}
if (isset($template->template) && ! empty($template->template)) {
if (! empty($template->params) && \is_string($template->params)) {
$template->params = new Registry($template->params);
}
/**
* If params field is found empty in the database or cache then
* read the default options.json file from the template and assign
* the options as template params.
*/
elseif (empty($template->params)) {
$filePath = JPATH_ROOT . '/templates/' . $template->template . '/' . 'options.json';
if (\file_exists($filePath)) {
$defaultParams = \file_get_contents($filePath);
$template->params = new Registry($defaultParams);
} else {
$template->params = new Registry;
}
}
return $template;
}
}
$template = new \stdClass;
$template->template = 'system';
$template->params = new Registry;
return $template;
}
/**
* Flush settings data towards the javascript using addScriptOptions
*
* @return void
* @since 2.0.0
*/
public static function flushSettingsDataToJs()
{
$doc = Factory::getDocument();
$loadTemplateData = self::loadTemplateData();
$stickyOffset = $loadTemplateData->params->get('sticky_offset', '100');
$data = [
'breakpoints' => [
'tablet' => 991,
'mobile' => 480,
],
'header' => [
'stickyOffset' => $stickyOffset,
],
// 'topbarHeight' => 40
];
$doc->addScriptOptions('data', $data);
}
public static function getModules($keyword = '')
{
$modules = [];
if (! empty($keyword)) {
$keyword = preg_replace("@\s+@", ' ', trim($keyword));
$keyword = implode('|', explode(' ', $keyword));
}
try
{
$db = Factory::getDbo();
$query = $db->getQuery(true);
$query->select('DISTINCT m.id, m.title, m.module, m.position, m.params, e.manifest_cache')
->from($db->quoteName('#__modules', 'm'))
->where($db->quoteName('m.client_id') . ' = 0');
$query->join('LEFT', $db->quoteName('#__extensions', 'e') . ' ON (' . $db->quoteName('e.element') . ' = ' . $db->quoteName('m.module') . ')');
if (! empty($keyword)) {
$query->where($db->quoteName('m.title') . ' REGEXP ' . $db->quote($keyword));
}
$query->order($db->quoteName('m.title') . ' ASC');
$db->setQuery($query);
$modules = $db->loadObjectList();
} catch (\Exception $e) {
return [];
}
$lang = Factory::getApplication()->getLanguage();
$client = ApplicationHelper::getClientInfo(0);
if (! empty($modules)) {
foreach ($modules as &$module) {
$module->desc = '';
if (isset($module->manifest_cache) && \is_string($module->manifest_cache)) {
// $lang->load($module->module . '.sys', $client->path, null, false, true)
// || $lang->load($module->module . '.sys', $client->path . '/modules/' . $module->module, null, false, true);
$module->manifest_cache = \json_decode($module->manifest_cache ?? "");
if (! empty($module->manifest_cache->description)) {
// $module->desc = Text::_($module->manifest_cache->description);
} else {
// $module->desc = Text::_('COM_MODULES_NODESCRIPTION');
}
}
}
unset($module);
}
return $modules;
}
/**
* Get template position
*/
public static function getTemplatePositions()
{
$positions = [];
$template = self::loadTemplateData();
$templateBaseDir = JPATH_SITE;
$filePath = Path::clean($templateBaseDir . '/templates/' . $template->template . '/templateDetails.xml');
if (is_file($filePath)) {
// Read the file to see if it's a valid component XML file
$xml = simplexml_load_file($filePath);
if (! $xml) {
return false;
}
// Check for a valid XML root tag.
// Extensions use 'extension' as the root tag. Languages use 'metafile' instead
if ($xml->getName() != 'extension' && $xml->getName() != 'metafile') {
unset($xml);
return false;
}
$positions = (array) $xml->positions;
if (isset($positions['position'])) {
$positions = (array) $positions['position'];
} else {
$positions = [];
}
}
return $positions;
}
public static function getMenuItems($parentId, &$menuItemList)
{
$elements = [];
try
{
$db = Factory::getContainer()->get(DatabaseInterface::class);
$query = $db->getQuery(true);
$query->select('id, title, parent_id, level')
->from($db->quoteName('#__menu'))
->where($db->quoteName('parent_id') . ' = ' . (int) $parentId)
->where($db->quoteName('published') . ' = 1')
->where($db->quoteName('client_id') . ' = 0');
$db->setQuery($query);
$elements = $db->loadObjectList();
} catch (\Exception $e) {
return [];
}
if (! empty($elements)) {
foreach ($elements as $element) {
if (isset($menuItemList->$parentId)) {
$menuItemList->$parentId->children[] = $element->id;
}
$elementId = $element->id;
$temp = new \stdClass;
$temp->id = $element->id;
$temp->title = $element->title;
$temp->level = $element->level;
$temp->children = [];
$menuItemList->$elementId = $temp;
self::getMenuItems($element->id, $menuItemList);
}
}
}
/**
* Get the search module for the pre-defined headers.
*
* @return Object The module object.
* @since 2.0.0
*/
public static function getSearchModule($idSuffix = '')
{
$version = JoomlaBridge::getVersion('major');
$name = $version < 4 ? 'mod_search' : 'mod_finder';
$module = self::createModule($name, [
'title' => 'Search',
'params' => '{"show_label": 0, "label":"","width":20,"text":"","button":0,"button_pos":"right","imagebutton":0,"button_text":"","opensearch":1,"opensearch_title":"","set_itemid":0,"layout":"_:default","moduleclass_sfx":"","cache":1,"cache_time":900,"cachemode":"itemid","module_tag":"div","bootstrap_size":"0","header_tag":"h3","header_class":"","style":"0"}',
], $idSuffix);
return $module;
}
/**
* Create a module object which is not created or published.
*
* @param string $name The module name with mod_ prefixed.
* @param array $options The module options.
*
* @return object The module object.
* @since 2.0.0
*/
public static function createModule($name, $options = [], $idSuffix = '0')
{
if (empty($name)) {
throw new \Exception(\sprintf('%s method expect the module $name as first argument!', __METHOD__));
}
if (! empty($options) && \is_object($options)) {
$options = (array) $options;
}
$defaultOptions = ['id' => $idSuffix, 'title' => '', 'module' => $name, 'position' => '', 'content' => '', 'showtitle' => 0, 'control' => '', 'params' => '', 'menuid' => 0, 'style' => ''];
return ArrayHelper::toObject(\array_merge($defaultOptions, $options));
}
/**
* Check a string ends with a needle or not.
*
* @param string $haystack The main string.
* @param string $needle The needle to search at the end.
*
* @return bool True if find at the end, false otherwise.
* @since 2.0.2
*/
public static function endsWith(string $haystack, string $needle): bool
{
$isEight = \version_compare(PHP_VERSION, '8.0.0') >= 0;
$length = strlen($needle);
if ($isEight) {
return \str_ends_with($haystack, $needle);
}
return ! $length ? true : substr($haystack, -$length) === $needle;
}
/**
* Check a string starts with a needle or not.
*
* @param string $haystack The main string.
* @param string $needle The needle to search at the beginning.
*
* @return bool True if find at the starting position, false otherwise.
* @since 2.0.2
*/
public static function startsWith(string $haystack, string $needle): bool
{
$isEight = \version_compare(PHP_VERSION, '8.0.0') >= 0;
$length = strlen($needle);
if ($isEight) {
return \str_starts_with($haystack, $needle);
}
return substr($haystack, 0, $length) === $needle;
}
private static function getMenuAliasById(int $pageId)
{
$db = Factory::getContainer()->get(DatabaseInterface::class);
$query = $db->getQuery(true);
$query->select('alias')
->from($db->quoteName('#__menu'))
->where($db->quoteName('link') . ' = ' . $db->quote('index.php?option=com_sppagebuilder&view=page&id=' . $pageId));
$db->setQuery($query);
try
{
return $db->loadResult();
} catch (\Exception $e) {
Factory::getApplication()->enqueueMessage($e->getMessage());
return '404';
}
return '404';
}
public static function renderPage(string $code, int $pageId)
{
$app = Factory::getApplication();
$config = $app->getConfig();
$sef = $config->get('sef');
$sef_rewrite = $config->get('sef_rewrite');
$sef_suffix = $config->get('sef_suffix');
$redirect_url = Uri::base();
if (! $sef_rewrite) {
$redirect_url .= 'index.php/';
}
$redirect_url .= self::getMenuAliasById($pageId);
if ($sef_suffix) {
$redirect_url .= '.html';
}
// If sef is turned off
if (! $sef) {
$redirect_url = 'index.php?option=com_sppagebuilder&view=page&id=' . $pageId;
}
if ($code == '404') {
header('Location: ' . $redirect_url, true, 301);
exit;
}
}
/**
* Function to set default column
*
* @param integer $num_columns
* @param integer $default
* @return integer
*/
public static function SetColumn($num_columns, $default = 3)
{
return empty($num_columns) ? $default : $num_columns;
}
/**
* Function to check if Null then replace with empty string [for php 8.1 fix]
*
* @return string
*/
public static function CheckNull($value = null)
{
return ($value == null) ? '' : $value;
}
/**
* Count read time buy content text
*
* @param string $text
* @return string
*/
public static function getReadTime($text)
{
$words_per_minute = 200;
$word_count = str_word_count(strip_tags($text));
$read_time = ceil($word_count / $words_per_minute);
if ($read_time == 1) { //grammar conversion
$label = Text::_('HELIX_ULTIMATE_BLOG_MINUTE_READ');
} else {
$label = Text::_('HELIX_ULTIMATE_BLOG_MINUTES_READ');
}
$totalString = $read_time . " " . $label; //adds time with minute/minutes label
return $totalString;
}
/**
* Save license data
*
* @param array $inputs
* @return mixed
*/
public static function saveLicenseInfo(array $inputs)
{
if (empty($inputs)) {
return;
}
$validKeys = ['template', 'joomshaper_email', 'joomshaper_license_key'];
// check $validKeys are exist in $inputs and not empty
if (array_diff($validKeys, array_keys($inputs))) {
return;
}
$template = $inputs['template'];
$email = $inputs['joomshaper_email'] ?? '';
$license_key = $inputs['joomshaper_license_key'] ?? '';
if (! empty($template)) {
$extra_query = 'joomshaper_email=' . urlencode($email);
$extra_query .= '&joomshaper_license_key=' . urlencode($license_key);
$db = Factory::getContainer()->get(DatabaseInterface::class);
$fields = [
$db->quoteName('extra_query') . ' = ' . $db->quote($extra_query),
$db->quoteName('last_check_timestamp') . ' = 0',
];
$query = $db->getQuery(true)
->update($db->quoteName('#__update_sites'))
->set($fields)
->where($db->quoteName('name') . ' = ' . $db->quote($template));
$db->setQuery($query);
$db->execute();
}
}
/**
* Helix article attribs keys allowed to merge on frontend save.
*
* @return array<string>
* @since 2.2.3
*/
public static function getHelixAttribKeys(): array
{
return [
'helix_ultimate_image',
'helix_ultimate_image_alt_txt',
'helix_ultimate_article_format',
'helix_ultimate_audio',
'helix_ultimate_gallery',
'helix_ultimate_video',
];
}
/**
* Map Helix AJAX actions to required Joomla permissions (administrator).
*
* @return array<string, array<string, string>>
* @since 2.2.3
*/
public static function getActionPermissions(): array
{
$templateActions = [
'save-tmpl-style',
'draft-tmpl-style',
'reset-drafted-settings',
'save-layout',
'render-layout',
'remove-layout-file',
'purge-css-file',
'import-tmpl-style',
'update-font-list',
'fontVariants',
'view-media',
'delete-media',
'create-folder',
'upload-media',
];
$menuActions = [
'getMenuItems',
'parentAdoption',
'rebuildMenu',
'generateMegaMenuBody',
'saveMegaMenuSettings',
'updateRowLayout',
'generateRow',
'generatePopoverContents',
'generateNewCell',
'getModuleList',
];
$blogActions = [
'upload-blog-image',
'remove-blog-image',
];
$permissions = [];
foreach ($templateActions as $action) {
$permissions[$action] = ['com_templates' => 'core.edit'];
}
foreach ($menuActions as $action) {
$permissions[$action] = ['com_menus' => 'core.edit'];
}
foreach ($blogActions as $action) {
$permissions[$action] = ['com_content' => 'core.edit'];
}
return $permissions;
}
/**
* Site-client permission overrides for frontend AJAX actions.
*
* @return array<string, array<string, string>>
* @since 2.2.3
*/
public static function getSiteActionPermissions(): array
{
return [
'upload-blog-image' => [
'com_content' => 'core.edit',
'com_media' => 'core.create',
],
'remove-blog-image' => [
'com_content' => 'core.edit',
'com_media' => 'core.delete',
],
'view-media' => [
'com_media' => 'core.create',
],
'delete-media' => [
'com_media' => 'core.delete',
],
'upload-media' => [
'com_media' => 'core.create',
],
];
}
/**
* Check whether the current user may execute a Helix AJAX action.
*
* @param string $action Action name.
*
* @return bool
* @since 2.2.3
*/
public static function authorizeAction(string $action): bool
{
$app = Factory::getApplication();
$user = $app->getIdentity();
if (! $user || ! $user->id) {
return false;
}
$map = $app->isClient('site')
? self::getSiteActionPermissions()
: self::getActionPermissions();
if (! isset($map[$action])) {
return false;
}
foreach ($map[$action] as $asset => $permission) {
if (! $user->authorise($permission, $asset)) {
if ($asset === 'com_content' && $permission === 'core.edit'
&& $user->authorise('core.edit.own', 'com_content')) {
continue;
}
return false;
}
}
return true;
}
/**
* Enforce CSRF token and ACL for a Helix AJAX action.
*
* @param string $action Action name.
*
* @return void
* @since 2.2.3
*/
public static function guardAjaxRequest(string $action): void
{
$report = [
'status' => false,
'message' => Text::_('JINVALID_TOKEN'),
'output' => Text::_('JINVALID_TOKEN'),
];
if (! Session::checkToken()) {
die(json_encode($report));
}
$report['message'] = Text::_('JERROR_ALERTNOAUTHOR');
$report['output'] = Text::_('JERROR_ALERTNOAUTHOR');
if (! self::authorizeAction($action)) {
die(json_encode($report));
}
}
/**
* Sanitize a layout file name for template layout JSON storage.
*
* @param string $name Layout name from request data.
*
* @return string|null Safe filename including .json extension.
* @since 2.2.3
*/
public static function sanitizeLayoutName(string $name): ?string
{
$name = basename(str_replace('\\', '/', $name));
$name = preg_replace('/\.json$/i', '', $name);
if ($name === null || ! preg_match('/^[A-Za-z0-9_-]+$/', $name)) {
return null;
}
return $name . '.json';
}
/**
* Resolve and validate a media path under the configured media/image root.
*
* @param string $path Relative path (with or without leading slash).
*
* @return string|null Absolute filesystem path or null if invalid.
* @since 2.2.3
*/
public static function resolveMediaPath(string $path): ?string
{
$path = trim(str_replace('\\', '/', $path));
if ($path === '' || strpos($path, '..') !== false) {
return null;
}
$path = ltrim($path, '/');
$params = ComponentHelper::getParams('com_media');
$mediaRoot = trim($params->get('image_path', 'images'), '/');
$allowedRoots = array_unique([$mediaRoot, 'images']);
$fullPath = Path::clean(JPATH_ROOT . '/' . $path);
$isAllowed = false;
foreach ($allowedRoots as $root) {
$allowedPath = Path::clean(JPATH_ROOT . '/' . $root);
if ($fullPath === $allowedPath || strpos($fullPath, $allowedPath . '/') === 0) {
$isAllowed = true;
break;
}
}
if (! $isAllowed) {
return null;
}
try
{
Path::check($fullPath);
} catch (\Exception $e) {
return null;
}
return $fullPath;
}
/**
* Check whether the current user may edit a content article.
*
* @param int $articleId Article ID.
*
* @return bool
* @since 2.2.3
*/
public static function canEditArticle(int $articleId): bool
{
$user = Factory::getApplication()->getIdentity();
if (! $user || ! $user->id || $articleId <= 0) {
return false;
}
if ($user->authorise('core.edit', 'com_content')) {
return true;
}
if (! $user->authorise('core.edit.own', 'com_content')) {
return false;
}
$db = Factory::getContainer()->get(DatabaseInterface::class);
$query = $db->getQuery(true)
->select($db->quoteName('created_by'))
->from($db->quoteName('#__content'))
->where($db->quoteName('id') . ' = ' . (int) $articleId);
$db->setQuery($query);
return (int) $db->loadResult() === (int) $user->id;
}
/**
* Validate a base64-encoded internal redirect URL.
*
* @param string $encoded Base64-encoded URL.
*
* @return string|null Safe internal URL or null.
* @since 2.2.3
*/
public static function validateInternalRedirect(string $encoded): ?string
{
$decoded = base64_decode($encoded, true);
if ($decoded === false || $decoded === '') {
return null;
}
if (! Uri::isInternal($decoded)) {
return null;
}
return $decoded;
}
/**
* Sanitize embed HTML using an allowlist of safe tags and attributes.
*
* @param string $html Raw embed HTML.
*
* @return string
* @since 2.2.3
*/
public static function sanitizeEmbed(string $html): string
{
if ($html === '') {
return '';
}
$filter = InputFilter::getInstance(
['iframe', 'audio', 'video', 'source', 'a', 'img'],
['src', 'href', 'type', 'controls', 'width', 'height', 'allow', 'allowfullscreen', 'frameborder', 'alt', 'class', 'style'],
1,
1
);
return $filter->clean($html, 'html');
}
/**
* Sanitize mega menu settings before persisting to menu item params.
*
* @param array $settings Raw settings from request.
*
* @return array
* @since 2.2.7
*/
public static function sanitizeMegaMenuSettings(array $settings): array
{
$clean = [];
$clean['megamenu'] = ! empty($settings['megamenu']) ? 1 : 0;
$clean['showtitle'] = ! empty($settings['showtitle']) ? 1 : 0;
$clean['menualign'] = self::sanitizeMegaMenuEnum(
$settings['menualign'] ?? '',
['left', 'center', 'right', 'full'],
'full'
);
$clean['dropdown'] = self::sanitizeMegaMenuEnum(
$settings['dropdown'] ?? '',
['left', 'right'],
'right'
);
$clean['badge_position'] = self::sanitizeMegaMenuEnum(
$settings['badge_position'] ?? '',
['left', 'right'],
'right'
);
$clean['width'] = self::sanitizeMegaMenuWidth($settings['width'] ?? '600px');
$clean['customclass'] = self::sanitizeMegaMenuCustomClass($settings['customclass'] ?? '');
$clean['faicon'] = self::sanitizeMegaMenuFaIcon($settings['faicon'] ?? '');
$clean['badge'] = self::sanitizeMegaMenuBadge($settings['badge'] ?? '');
$clean['badge_bg_color'] = self::sanitizeMegaMenuColor($settings['badge_bg_color'] ?? '');
$clean['badge_text_color'] = self::sanitizeMegaMenuColor($settings['badge_text_color'] ?? '');
$layout = $settings['layout'] ?? [];
if (! \is_array($layout)) {
$layout = [];
}
$clean['layout'] = self::sanitizeMegaMenuLayout($layout);
return $clean;
}
/**
* Sanitize a mega menu CSS class string.
*
* @param mixed $value Raw custom class value.
*
* @return string
* @since 2.2.7
*/
public static function sanitizeMegaMenuCustomClass($value): string
{
$value = (string) $value;
if (preg_match('/[<>"\'=]/', $value)) {
return '';
}
$value = strip_tags($value);
$value = preg_replace('/[^a-zA-Z0-9_\-\s]/', '', $value) ?? '';
return trim(preg_replace('/\s+/', ' ', $value) ?? '');
}
/**
* Sanitize a Font Awesome icon class string.
*
* @param mixed $value Raw icon value.
*
* @return string
* @since 2.2.7
*/
public static function sanitizeMegaMenuFaIcon($value): string
{
$value = trim(strip_tags((string) $value));
if ($value === '') {
return '';
}
if (! preg_match('/^fa[sbr]?\s+fa-[a-z0-9-]+$/i', $value)) {
return '';
}
return $value;
}
/**
* Sanitize mega menu badge text.
*
* @param mixed $value Raw badge value.
*
* @return string
* @since 2.2.7
*/
public static function sanitizeMegaMenuBadge($value): string
{
return trim(strip_tags((string) $value));
}
/**
* Sanitize a hex color value.
*
* @param mixed $value Raw color value.
*
* @return string
* @since 2.2.7
*/
public static function sanitizeMegaMenuColor($value): string
{
$value = trim(strip_tags((string) $value));
if ($value === '') {
return '';
}
if (! preg_match('/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/', $value)) {
return '';
}
return $value;
}
/**
* Sanitize a mega menu width value.
*
* @param mixed $value Raw width value.
*
* @return string
* @since 2.2.7
*/
private static function sanitizeMegaMenuWidth($value): string
{
$value = trim(strip_tags((string) $value));
if (preg_match('/^[0-9]+(px|%|em|rem)$/', $value)) {
return $value;
}
return '600px';
}
/**
* Sanitize a mega menu enum field.
*
* @param mixed $value Raw value.
* @param array $allowed Allowed values.
* @param string $default Default value.
*
* @return string
* @since 2.2.7
*/
private static function sanitizeMegaMenuEnum($value, array $allowed, string $default): string
{
$value = trim(strip_tags((string) $value));
return \in_array($value, $allowed, true) ? $value : $default;
}
/**
* Recursively sanitize mega menu layout rows/columns/cells.
*
* @param array $layout Raw layout array.
*
* @return array
* @since 2.2.7
*/
private static function sanitizeMegaMenuLayout(array $layout): array
{
$clean = [];
foreach ($layout as $row) {
if (! \is_array($row) && ! \is_object($row)) {
continue;
}
$row = (array) $row;
$cleanRow = [
'type' => 'row',
'attr' => [],
];
$columns = $row['attr'] ?? [];
if (! \is_array($columns)) {
$columns = [];
}
foreach ($columns as $column) {
if (! \is_array($column) && ! \is_object($column)) {
continue;
}
$column = (array) $column;
$cleanColumn = [
'type' => 'column',
'colGrid' => self::sanitizeMegaMenuColGrid($column['colGrid'] ?? '12'),
'menuParentId' => (string) (int) ($column['menuParentId'] ?? 0),
'moduleId' => (string) (int) ($column['moduleId'] ?? 0),
'items' => [],
];
$items = $column['items'] ?? [];
if (\is_array($items)) {
foreach ($items as $cell) {
if (! \is_array($cell) && ! \is_object($cell)) {
continue;
}
$cell = (array) $cell;
$type = ($cell['type'] ?? '') === 'module' ? 'module' : 'menu_item';
$cellId = (string) (int) ($cell['item_id'] ?? $cell['id'] ?? 0);
$cleanCell = [
'type' => $type,
'item_id' => $cellId,
];
if ($type === 'module') {
$cleanCell['moduleId'] = (string) (int) ($cell['moduleId'] ?? $cell['item_id'] ?? $cell['id'] ?? 0);
}
$cleanColumn['items'][] = $cleanCell;
}
}
$cleanRow['attr'][] = $cleanColumn;
}
$clean[] = $cleanRow;
}
return $clean;
}
/**
* Sanitize a bootstrap column grid value.
*
* @param mixed $value Raw column grid value.
*
* @return string
* @since 2.2.7
*/
private static function sanitizeMegaMenuColGrid($value): string
{
$value = trim(strip_tags((string) $value));
if (preg_match('/^[0-9]{1,2}$/', $value)) {
$grid = (int) $value;
if ($grid >= 1 && $grid <= 12) {
return (string) $grid;
}
}
return '12';
}
/**
* Gets the extension of a file name
*
* @param string $file The file name
*
* @return string The file extension
*
* @since 3.0.0
*/
public static function getExt($file)
{
// String manipulation should be faster than pathinfo() on newer PHP versions.
$dot = strrpos($file, '.');
if ($dot === false) {
return '';
}
$ext = substr($file, $dot + 1);
// Extension cannot contain slashes.
if (strpos($ext, '/') !== false || (DIRECTORY_SEPARATOR === '\\' && strpos($ext, '\\') !== false)) {
return '';
}
return $ext;
}
}