File manager - Edit - /home/ferretapmx/public_html/plugins/system/helixultimate/src/Platform/Helper.php
Back
<?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; } }
| ver. 1.4 |
Github
|
.
| PHP 8.2.32 | Generation time: 0.06 |
proxy
|
phpinfo
|
Settings