771 lines
20 KiB
PHP
771 lines
20 KiB
PHP
<?php
|
|
|
|
/**
|
|
* @author Tassos Marinos <info@tassos.gr>
|
|
* @link https://www.tassos.gr
|
|
* @copyright Copyright © 2024 Tassos All Rights Reserved
|
|
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> 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 </body> tag.
|
|
*
|
|
* @param string $html The HTML to prepend to </body>
|
|
* @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 = '</body>';
|
|
|
|
if (strpos($buffer, $closingTag))
|
|
{
|
|
// If </body> exists prepend the given HTML
|
|
$buffer = str_replace($closingTag, $html . $closingTag, $buffer);
|
|
} else
|
|
{
|
|
// If </body> 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 name="' . $classname . '" type="' . $classname . '"
|
|
|
|
/>');
|
|
|
|
$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);
|
|
}
|
|
} |