* @link https://www.tassos.gr * @copyright Copyright © 2024 Tassos All Rights Reserved * @license GNU GPLv3 or later */ namespace NRFramework; defined('_JEXEC') or die; use Joomla\Registry\Registry; use \NRFramework\Cache; use Joomla\CMS\Factory; use Joomla\CMS\Date\Date; use Joomla\CMS\Uri\Uri; use Joomla\Filesystem\File; use Joomla\CMS\Installer\Installer; use Joomla\CMS\Helper\ModuleHelper; class Functions { /** * Add HTML before the closing tag. * * @param string $html The HTML to prepend to * @param boolean $once If true, the HTML will be added only once. * * @return void */ public static function appendToBody($html, $once = false) { if ($once) { $hash = md5($html); if (Cache::has($hash)) { return; } Cache::set($hash, true); } $app = Factory::getApplication(); $app->registerEvent('onAfterRender', function() use ($app, $html) { $buffer = $app->getBody(); $closingTag = ''; if (strpos($buffer, $closingTag)) { // If exists prepend the given HTML $buffer = str_replace($closingTag, $html . $closingTag, $buffer); } else { // If does not exist append to document's end $buffer .= $html; } $app->setBody($buffer); }); } /** * Fix arrays, remove duplicate items, null items and whitespace around item values. * * @param array $subject * * @return array The new cleaned array */ public static function cleanArray($subject) { if (!is_array($subject)) { return $subject; } $subject = array_map(function($str) { return is_null($str) ? '' : trim($str); }, $subject); $subject = array_unique($subject); // Remove empty items. We use a custom callback here because the default behavior of array_filter removes 0 values as well. $subject = array_filter($subject, function($value) { return ($value !== null && $value !== false && $value !== ''); }); return $subject; } /** * Attempt to convert a subject to array * * @param mixed $subject * * @return array */ public static function makeArray($subject) { if (empty($subject)) { return []; } if (is_object($subject)) { return (array) $subject; } if (!is_array($subject)) { // replace newlines with commas $subject = str_replace(PHP_EOL, ',', $subject); // split keywords on commas $subject = explode(',', $subject); } // Now that we have an array, run some housekeeping. $arr = $subject; $arr = self::cleanArray($arr); // Reset keys $arr = array_values($arr); return $arr; } /** * Return the real site base URL by ignoring the live_site configuration option. * * @param bool $ignore_admin If enabled and we are browsing administrator, we will get the front-end site root URL. * * @return string */ public static function getRootURL($ignore_admin = true) { $factory = Factory::getConfig(); // Store the original live_site value $live_site_original = $factory->get('live_site', ''); // If we live_site is not set, do not proceed further. Return the default website base URL. if (empty($live_site_original)) { return $ignore_admin ? Uri::root() : Uri::base(); } // Remove the live site $factory->set('live_site', ''); // Remove all cached Uri instances Uri::reset(); // Get a new URL. The live_site option should be ignored. $base_url = $ignore_admin ? Uri::root() : Uri::base(); // Set back the original live_site $factory->set('live_site', $live_site_original); Uri::reset(); return $base_url; } /** * Insert an associative array into a specific position in an array * * @param $original array The original array to add to * @param $new array The new array of values to insert into the original * @param $offset int The position in the array ( 0 index ) where the new array should go * * @return array The new combined array */ public static function array_splice_assoc($original,$new,$offset) { return array_slice($original, 0, $offset, true) + $new + array_slice($original, $offset, NULL, true); } public static function renderField($fieldname) { $fieldname = strtolower($fieldname); require_once JPATH_PLUGINS . '/system/nrframework/fields/' . $fieldname . '.php'; $classname = '\JFormField' . $fieldname; $field = new $classname(); $element = new \SimpleXMLElement(' '); $field->setup($element, null); return $field->__get('input'); } /** * Checks if an array of values (needle) exists in a text (haystack) * * @param array $needle The searched array of values. * @param string $haystack The text * @param bool $case_insensitive Indicates whether the letter case plays any role * * @return bool */ public static function strpos_arr($needles, $haystack, $case_insensitive = false) { $needles = !is_array($needles) ? (array) $needles : $needles; $haystack = $case_insensitive ? strtolower($haystack) : $haystack; foreach ($needles as $needle) { $needle = $case_insensitive ? strtolower($needle) : $needle; if (strpos($haystack, $needle) !== false) { // stop on first true result return true; } } return false; } /** * Log message to framework's log file * * @param mixed $data Log message * * @return void * * @deprecated Stop using method */ public static function log($data) { } /** * Return's a URL with the Google Analytics Campaign Parameters appended to the end * * @param string $url The URL * @param string $medium Campaign Medium * @param string $campaign Campaign Name * * @return string */ public static function getUTMURL($url, $medium = 'upgradebutton', $campaign = 'freeversion') { if (!$url) { return; } $utm = 'utm_source=CustomerBackend&utm_medium=' . $medium . '&utm_campaign=' . $campaign; $char = strpos($url, '?') === false ? '?' : '&'; return $url . $char . $utm; } /** * Returns user's Download Key * * @return string */ public static function getDownloadKey() { $class = new Updatesites(); return $class->getDownloadKey(); } /** * Adds a script or a stylesheet to the document * * @param Mixed $files The files to be to added to the document * @param boolean $appendVersion Adds file versioning based on extension's version * * @return void */ public static function addMedia($files, $extension = "plg_system_nrframework", $appendVersion = true) { $doc = Factory::getDocument(); $version = self::getExtensionVersion($extension); $mediaPath = Uri::root(true) . "/media/" . $extension; if (!is_array($files)) { $files = array($files); } foreach ($files as $key => $file) { $fileExt = File::getExt($file); $filename = $mediaPath . "/" . $fileExt . "/" . $file; $filename = ($appendVersion) ? $filename . "?v=" . $version : $filename; if ($fileExt == "js") { $doc->addScript($filename); } if ($fileExt == "css") { $doc->addStylesheet($filename); } } } /** * Get the Framework version * * @return string The framework version */ public static function getVersion() { return self::getExtensionVersion("plg_system_nrframework"); } /** * Checks if document is a feed document (xml, rss, atom) * * @return boolean */ public static function isFeed() { return ( Factory::getDocument()->getType() == 'feed' || Factory::getDocument()->getType() == 'xml' || Factory::getApplication()->input->getWord('format') == 'feed' || Factory::getApplication()->input->getWord('type') == 'rss' || Factory::getApplication()->input->getWord('type') == 'atom' ); } public static function loadLanguage($extension = 'plg_system_nrframework', $basePath = '') { if ($basePath && Factory::getLanguage()->load($extension, $basePath)) { return true; } $basePath = self::getExtensionPath($extension, $basePath, 'language'); return Factory::getLanguage()->load($extension, $basePath); } /** * Returns extension ID * * @param string $extension Extension name * * @return integer * * @deprecated Use \NRFramework\Extension::getID instead */ public static function getExtensionID($extension, $folder = null) { $type = is_null($folder) ? 'component' : 'plugin'; return \NRFramework\Extension::getID($extension, $type, $folder); } /** * Checks if extension is installed * * @param string $extension The extension element name * @param string $type The extension's type * @param string $folder Plugin folder * * * @return boolean Returns true if extension is installed * * @deprecated Use \NRFramework\Extension::isInstalled instead */ public static function extensionInstalled($extension, $type = 'component', $folder = 'system') { return \NRFramework\Extension::isInstalled($extension, $type, $folder); } /** * Returns the version number from the extension's xml file * * @param string $extension The extension element name * * @return string Extension's version number */ public static function getExtensionVersion($extension, $type = false) { $hash = MD5($extension . "_" . ($type ? "1" : "0")); $cache = Cache::read($hash); if ($cache) { return $cache; } $xml = self::getExtensionXMLFile($extension); if (!$xml) { return false; } $xml = Installer::parseXMLInstallFile($xml); if (!$xml || !isset($xml['version'])) { return ''; } $version = $xml['version']; if ($type) { $extType = Extension::isPro($extension) ? 'Pro' : 'Free'; $version = $xml["version"] . " " . $extType; } return Cache::set($hash, $version); } public static function getExtensionXMLFile($extension, $basePath = JPATH_ADMINISTRATOR) { $alias = explode("_", $extension); $alias = end($alias); $filename = (strpos($extension, 'mod_') === 0) ? "mod_" . $alias : $alias; $file = self::getExtensionPath($extension, $basePath) . "/" . $filename . ".xml"; if (file_exists($file)) { return $file; } return false; } /** * @deprecated // Use Extension::isPro(); */ public static function extensionHasProInstalled($extension) { return Extension::isPro($extension); } public static function getExtensionPath($extension = 'plg_system_nrframework', $basePath = JPATH_ADMINISTRATOR, $check_folder = '') { $path = ''; switch (true) { case (strpos($extension, 'com_') === 0): $path = 'components/' . $extension; break; case (strpos($extension, 'mod_') === 0): $path = 'modules/' . $extension; break; case (strpos($extension, 'plg_system_') === 0): $path = 'plugins/system/' . substr($extension, strlen('plg_system_')); break; case (strpos($extension, 'plg_editors-xtd_') === 0): $path = 'plugins/editors-xtd/' . substr($extension, strlen('plg_editors-xtd_')); break; } if (empty($path)) { return; } $check_folder = $check_folder ? '/' . $check_folder : ''; $basePath = empty($basePath) ? JPATH_ADMINISTRATOR : $basePath; if (is_dir($basePath . '/' . $path . $check_folder)) { return $basePath . '/' . $path; } if (is_dir(JPATH_ADMINISTRATOR . '/' . $path . $check_folder)) { return JPATH_ADMINISTRATOR . '/' . $path; } if (is_dir(JPATH_SITE . '/' . $path . $check_folder)) { return JPATH_SITE . '/' . $path; } return $basePath; } public static function renderModulePosition($position, $style = 'custom') { $modules = ModuleHelper::getModules($position); $attribs['style'] = $style; foreach ($modules as $module) { echo ModuleHelper::renderModule($module, $attribs); } } public static function loadModule($id, $moduleStyle = null) { // Return if no module id passed if (!$id) { return; } // Fetch module from db $db = Factory::getDBO(); $query = $db->getQuery(true) ->select('*') ->from('#__modules') ->where('id='.$db->q($id)); $db->setQuery($query); // Return if no modules found if (!$module = $db->loadObject()) { return; } // Success! Return module's html return ModuleHelper::renderModule($module, $moduleStyle); } public static function fixDate(&$date) { if (!$date) { $date = null; return; } $date = trim($date); // Check if date has correct syntax: 00-00-00 00:00:00 if (preg_match('#^[0-9]+-[0-9]+-[0-9]+( [0-9][0-9]:[0-9][0-9]:[0-9][0-9])$#', $date)) { return; } // Check if date has syntax: 00-00-00 00:00 // If so, add :00 (seconds) if (preg_match('#^[0-9]+-[0-9]+-[0-9]+ [0-9][0-9]:[0-9][0-9]$#', $date)) { $date .= ':00'; return; } // Check if date has a prepending date syntax: 00-00-00 ... // If so, add 00:00:00 (hours:mins;secs) if (preg_match('#^([0-9]+-[0-9]+-[0-9]+)#', $date, $match)) { $date = $match[1] . ' 00:00:00'; return; } // Date format is not correct, so return null // $date = null; } /** * Change date's timezone to UTC by modyfing the offset * * @param string $date The date in timezone other than UTC * * @return string The date in UTC */ public static function dateToUTC($date) { $date = is_string($date) ? trim($date) : $date; if (empty($date) || is_null($date) || $date == '0000-00-00 00:00:00') { return $date; } $timezone = Factory::getUser()->getParam('timezone', Factory::getConfig()->get('offset')); $date = new Date($date, $timezone); $date->setTimezone(new \DateTimeZone('UTC')); $dateUTC = $date->format('Y-m-d H:i:s', true, false); return $dateUTC; } /** * Applies the site's or the user's timezone to a given date. * * @param string $date * @param string $format * * @return string */ public static function applySiteTimezoneToDate($date, $format = 'Y-m-d H:i:s') { $timezone = new \DateTimeZone(Factory::getUser()->getParam('timezone', Factory::getConfig()->get('offset'))); return Factory::getDate($date)->setTimezone($timezone)->format($format, true); } /** * Change date's timezone to UTC by modyfing the offset * * @param string $date The date in timezone other than UTC * * @return string The date in UTC * * @deprecated Use dateToUTC() */ public static function fixDateOffset(&$date) { $date = self::dateToUTC($date); } // Text public static function clean($string) { $string = str_replace(' ', '-', $string); // Replaces all spaces with hyphens. return preg_replace('/[^A-Za-z0-9\-]/', '', $string); // Removes special chars. } public static function dateTimeNow() { return Factory::getDate()->format("Y-m-d H:i:s"); } /** * Get framework plugin's parameters * * @return Registry The plugin parameters */ public static function params() { $hash = md5('frameworkParams'); if (Cache::has($hash)) { return Cache::read($hash); } $db = Factory::getDBO(); $result = $db->setQuery( $db->getQuery(true) ->select('params') ->from('#__extensions') ->where('element = ' . $db->quote('nrframework')) )->loadResult(); return Cache::set($hash, new Registry($result)); } /** * Checks whether string starts with substring. * * @param string $string * @param string $query * * @return bool */ public static function startsWith($string, $query) { return substr($string, 0, strlen($query)) === $query; } /** * Checks whether string end with substring. * * @param string $string * @param string $substring * * @return bool */ public static function endsWith($string, $substring) { $length = strlen($substring); return substr((string) $string, -$length) === $substring; } /** * Updates the Download Key in the framework plugin. * * @param string $key * * @return bool */ public static function updateDownloadKey($key) { if (empty($key)) { return false; } // Update params $db = Factory::getDBO(); // Get params $query = $db->getQuery(true) ->select($db->quoteName('params')) ->from($db->quoteName('#__extensions')) ->where($db->quoteName('type') . ' = ' . $db->quote('plugin')) ->where($db->quoteName('element') . ' = ' . $db->quote('nrframework')); $db->setQuery($query); $params = $db->loadResult(); $params = json_decode($params, true); // Set Download Key $params['key'] = $key; // Update params $query->clear() ->update('#__extensions') ->set($db->quoteName('params') . ' = ' . $db->quote(json_encode($params))) ->where($db->quoteName('type') . ' = ' . $db->quote('plugin')) ->where($db->quoteName('element') . ' = ' . $db->quote('nrframework')); $db->setQuery($query); $db->execute(); return true; } /** * Return the current page's body regardless the system event fires the method. * * @return string */ public static function getBuffer() { $app = Factory::getApplication(); // Try to get the whole body first. This will only work if the method is executed in the onAfterRender event. if ($body = $app->getBody()) { return $body; } // We got an empty body. Probably, the method runs before the onAfterRender event. Let's give it another try. $buffer = $app->getDocument()->getBuffer(); if (!is_array($buffer)) { return; } $flatted = \Joomla\Utilities\ArrayHelper::flatten($buffer); return implode(' ', $flatted); } }