| Current Path : /proc/1908984/root/proc/2603263/cwd/ |
| Current File : //proc/1908984/root/proc/2603263/cwd/Helpers.tar |
advancedmodulehelper.php 0000644 00000051075 15235511454 0011445 0 ustar 00 <?php
/**
* @package Advanced Module Manager
* @version 9.8.0PRO
*
* @author Peter van Westen <info@regularlabs.com>
* @link https://regularlabs.com
* @copyright Copyright © 2023 Regular Labs All Rights Reserved
* @license GNU General Public License version 2 or later
*/
defined('_JEXEC') or die;
use Joomla\CMS\Factory as JFactory;
use Joomla\CMS\Language\Text as JText;
use Joomla\CMS\Log\Log as JLog;
use RegularLabs\Library\ArrayHelper as RL_Array;
use RegularLabs\Library\Conditions as RL_Conditions;
use RegularLabs\Library\Document as RL_Document;
use RegularLabs\Library\ParametersNew as RL_Parameters;
use RegularLabs\Library\PluginTag as RL_PluginTag;
use RegularLabs\Library\RegEx as RL_RegEx;
use RegularLabs\Plugin\System\AdvancedModules\Params;
jimport('joomla.filesystem.file');
/*
* ModuleHelper methods
*/
class AdvancedModules
{
static $current_module = null;
public static function getCurrentModule()
{
return self::$current_module;
}
public static function setCurrentModule($module)
{
self::$current_module = $module;
}
}
class PlgSystemAdvancedModuleHelper
{
var $mirror_ids = [];
public function onPrepareModuleList(&$modules)
{
// return if is not frontend
if ( ! RL_Document::isClient('site'))
{
return;
}
jimport('joomla.filesystem.file');
$modules = is_null($modules) ? $this->getModuleList() : $modules;
if (is_array($modules) && empty($modules))
{
return;
}
[$remove_positions, $remove_modules] = $this->getRemovePositionsAndModules();
$filtered_modules = [];
foreach ($modules as $module)
{
if (empty($module->id))
{
continue;
}
if (
in_array($module->position, $remove_positions)
|| in_array($module->id, $remove_modules)
|| in_array($module->module, $remove_modules)
|| in_array($module->title, $remove_modules)
)
{
continue;
}
$module->name = substr($module->module, 4);
if (JFactory::getApplication()->input->get('option') == 'com_ajax')
{
$filtered_modules[] = $module;
continue;
}
$this->setAdvancedParams($module);
if ($module->advancedparams === 0)
{
if (isset($module->published) && ! $module->published)
{
continue;
}
$filtered_modules[] = $module;
continue;
}
$this->setExtraParams($module);
$module->reverse = 0;
$this->setMirrorParams($module);
$this->removeDisabledAssignments($module->advancedparams);
AdvancedModules::setCurrentModule($module);
$assignments = RL_Conditions::getConditionsFromParams($module->advancedparams);
$module->published = RL_Conditions::pass(
$assignments,
$module->advancedparams->match_method,
null,
$module
);
if ($module->reverse)
{
$module->published = ! $module->published;
}
if (isset($module->published) && ! $module->published)
{
continue;
}
$filtered_modules[] = $module;
}
$modules = array_values($filtered_modules);
unset($filtered_modules);
}
public function onRenderModule(&$module)
{
// Module already nulled
if (is_null($module))
{
return;
}
// Do nothing if is not frontend
if ( ! RL_Document::isClient('site'))
{
return;
}
// return true if module is empty (this will empty the content)
if ($this->isEmpty($module))
{
$module = null;
return;
}
// Add pre and post html
$this->addHTML($module);
}
private function addHTML(&$module)
{
$this->setAdvancedParams($module);
// return false if module params are not found
if (empty($module->advancedparams))
{
return;
}
$params = $module->advancedparams;
// do nothing if no advanced params are set
if (empty($params))
{
return;
}
// prepend the pre html
if (isset($params->pre_html) && $params->pre_html)
{
$module->content = $params->pre_html . $module->content;
}
// append the post html
if (isset($params->post_html) && $params->post_html)
{
$module->content .= $params->post_html;
}
}
private function getAdvancedParams($module)
{
if (empty($module->id))
{
return '{}';
}
if (isset($module->adv_params))
{
return $module->adv_params;
}
return $this->getAdvancedParamsById($module->id);
}
private function getAdvancedParamsById($id = 0)
{
if ( ! $id)
{
return '{}';
}
$db = JFactory::getDbo();
$query = $db->getQuery(true)
->select('a.params')
->from('#__advancedmodules AS a')
->where('a.moduleid = ' . (int) $id);
$db->setQuery($query);
$params = $db->loadResult();
if (empty($params))
{
$params = '{}';
}
return $params;
}
private function getConfig()
{
static $instance;
if (is_object($instance))
{
return $instance;
}
$instance = RL_Parameters::getComponent('advancedmodules');
return $instance;
}
private function getMirrorModuleId($module)
{
if (isset($module->mirror_id))
{
return $module->mirror_id;
}
return $this->getMirrorModuleIdById($module->id);
}
private function getMirrorModuleIdById($id)
{
if (isset($this->mirror_ids[$id]))
{
return $this->mirror_ids[$id];
}
$db = JFactory::getDbo();
$query = $db->getQuery(true)
->select('a.mirror_id')
->from('#__advancedmodules AS a')
->where('a.moduleid = ' . (int) $id);
$db->setQuery($query);
$this->mirror_ids[$id] = $db->loadResult();
return $this->mirror_ids[$id];
}
private function getModuleList()
{
$app = JFactory::getApplication();
$user = JFactory::getApplication()->getIdentity() ?: JFactory::getUser();
$groups = implode(',', $user->getAuthorisedViewLevels());
$lang = JFactory::getLanguage()->getTag();
$clientId = (int) $app->getClientId();
$db = JFactory::getDbo();
$query = $db->getQuery(true)
->select('m.id, m.title, m.module, m.position, m.content, m.showtitle, m.params')
->select('am.mirror_id, am.params AS advancedparams, 0 AS menuid, m.publish_up, m.publish_down')
->from('#__modules AS m')
->join('LEFT', '#__extensions AS e ON e.element = m.module AND e.client_id = m.client_id')
->join('LEFT', '#__advancedmodules as am ON am.moduleid = m.id')
->where('m.published = 1')
->where('e.enabled = 1')
->where('m.access IN (' . $groups . ')')
->where('m.client_id = ' . $clientId);
// Filter by language
if ($app->isClient('site') && $app->getLanguageFilter())
{
$query->where('m.language IN (' . $db->quote($lang) . ',' . $db->quote('*') . ')');
}
$query->order('m.position, m.ordering');
// Set the query
$db->setQuery($query);
try
{
$modules = $db->loadObjectList();
}
catch (RuntimeException $e)
{
JLog::add(JText::sprintf('JLIB_APPLICATION_ERROR_MODULE_LOAD', $e->getMessage()), JLog::WARNING, 'jerror');
return [];
}
return array_values($modules);
}
private function getRemovePositionsAndModules()
{
if ( ! $buffer = RL_Document::getComponentBuffer())
{
return [[], []];
}
$params = Params::get();
$remove_positions = [];
$remove_modules = [];
RL_RegEx::matchAll(Params::getRegex(), $buffer, $matches);
foreach ($matches as $match)
{
// Get the values from the tag
$attributes = RL_PluginTag::getAttributesFromString($match['attributes'], 'nokey');
$key_aliases = [
'modules' => ['module'],
'positions' => ['position'],
];
RL_PluginTag::replaceKeyAliases($attributes, $key_aliases);
if ( ! empty($attributes->positions))
{
$remove_positions = array_merge($remove_positions, RL_Array::toArray($attributes->positions));
}
if ( ! empty($attributes->modules))
{
$remove_modules = array_merge($remove_modules, RL_Array::toArray($attributes->modules));
}
if (empty($attributes->nokey))
{
continue;
}
if ($match['type'] == $params->tag_remove_modulepos)
{
$remove_positions[] = $attributes->nokey;
continue;
}
$remove_modules[] = $attributes->nokey;
}
return [array_unique($remove_positions), array_unique($remove_modules)];
}
private function isEmpty(&$module)
{
if ( ! isset($module->content))
{
return true;
}
$this->setAdvancedParams($module);
// return false if module params are not found
if (empty($module->advancedparams))
{
return false;
}
$params = $module->advancedparams;
// return false if hideempty is off in module params
if (empty($params)
|| (( ! isset($params->hideempty) || ! $params->hideempty) && empty($params->if_empty_html)))
{
return false;
}
$config = $this->getConfig();
// return false if use_hideempty is off in main config
if ( ! $config->use_hideempty)
{
return false;
}
$content = trim($module->content);
// remove empty tags to see if module can be considered as empty
if ( ! empty($content))
{
// remove html and hidden whitespace
$content = str_replace(chr(194) . chr(160), ' ', $content);
$content = str_replace([' ', ' '], ' ', $content);
// remove comment tags
$content = RL_RegEx::replace('<\!--.*?-->', '', $content);
// remove all closing tags
$content = RL_RegEx::replace('</[^>]+>', '', $content);
// remove tags to be ignored
$tags = 'p|div|span|strong|b|em|i|ul|font|br|h[0-9]|fieldset|label|ul|ol|li|table|thead|tbody|tfoot|tr|th|td|form';
$search = '<(?:' . $tags . ')(?:\s[^>]*)?>';
if (RL_RegEx::match($search, $content))
{
$content = RL_RegEx::replace($search, '', $content);
}
$content = trim($content);
}
if ( ! empty($content))
{
return false;
}
if ( ! empty($params->if_empty_html))
{
$module->content = $params->if_empty_html;
return false;
}
return true;
}
private function removeDisabledAssignments(&$params)
{
$config = $this->getConfig();
if ( ! $config->show_assignto_homepage)
{
$params->assignto_homepage = 0;
}
if ( ! $config->show_assignto_usergrouplevels)
{
$params->assignto_usergrouplevels = 0;
}
if ( ! $config->show_assignto_users)
{
$params->assignto_users = 0;
}
if ( ! $config->show_assignto_date)
{
$params->assignto_date = 0;
$params->assignto_seasons = 0;
$params->assignto_months = 0;
$params->assignto_days = 0;
$params->assignto_time = 0;
}
if ( ! $config->show_assignto_languages)
{
$params->assignto_languages = 0;
}
if ( ! $config->show_assignto_ips)
{
$params->assignto_ips = 0;
}
if ( ! $config->show_assignto_geo)
{
$params->assignto_geocontinents = 0;
$params->assignto_geocountries = 0;
$params->assignto_georegions = 0;
$params->assignto_geopostalcodes = 0;
}
if ( ! $config->show_assignto_templates)
{
$params->assignto_templates = 0;
}
if ( ! $config->show_assignto_urls)
{
$params->assignto_urls = 0;
}
if ( ! $config->show_assignto_devices)
{
$params->assignto_devices = 0;
}
if ( ! $config->show_assignto_os)
{
$params->assignto_os = 0;
}
if ( ! $config->show_assignto_browsers)
{
$params->assignto_browsers = 0;
}
if ( ! $config->show_assignto_components)
{
$params->assignto_components = 0;
}
if ( ! $config->show_assignto_tags)
{
$params->show_assignto_tags = 0;
}
if ( ! $config->show_assignto_content)
{
$params->assignto_contentpagetypes = 0;
$params->assignto_cats = 0;
$params->assignto_articles = 0;
}
if ( ! $config->show_assignto_easyblog)
{
$params->assignto_easyblogpagetypes = 0;
$params->assignto_easyblogcats = 0;
$params->assignto_easyblogtags = 0;
$params->assignto_easyblogitems = 0;
}
if ( ! $config->show_assignto_flexicontent)
{
$params->assignto_flexicontentpagetypes = 0;
$params->assignto_flexicontenttags = 0;
$params->assignto_flexicontenttypes = 0;
}
if ( ! $config->show_assignto_form2content)
{
$params->assignto_form2contentprojects = 0;
}
if ( ! $config->show_assignto_k2)
{
$params->assignto_k2pagetypes = 0;
$params->assignto_k2cats = 0;
$params->assignto_k2tags = 0;
$params->assignto_k2items = 0;
}
if ( ! $config->show_assignto_zoo)
{
$params->assignto_zoopagetypes = 0;
$params->assignto_zoocats = 0;
$params->assignto_zooitems = 0;
}
if ( ! $config->show_assignto_akeebasubs)
{
$params->assignto_akeebasubspagetypes = 0;
$params->assignto_akeebasubslevels = 0;
}
if ( ! $config->show_assignto_hikashop)
{
$params->assignto_hikashoppagetypes = 0;
$params->assignto_hikashopcats = 0;
$params->assignto_hikashopproducts = 0;
}
if ( ! $config->show_assignto_mijoshop)
{
$params->assignto_mijoshoppagetypes = 0;
$params->assignto_mijoshopcats = 0;
$params->assignto_mijoshopproducts = 0;
}
if ( ! $config->show_assignto_redshop)
{
$params->assignto_redshoppagetypes = 0;
$params->assignto_redshopcats = 0;
$params->assignto_redshopproducts = 0;
}
if ( ! $config->show_assignto_virtuemart)
{
$params->assignto_virtuemartpagetypes = 0;
$params->assignto_virtuemartcats = 0;
$params->assignto_virtuemartproducts = 0;
}
if ( ! $config->show_assignto_cookieconfirm)
{
$params->assignto_cookieconfirm = 0;
}
if ( ! $config->show_assignto_php)
{
$params->assignto_php = 0;
}
}
private function setAdvancedParams(&$module)
{
if (empty($module->id))
{
return;
}
if (isset($module->advancedparams) && is_object($module->advancedparams))
{
return;
}
if ( ! isset($module->advancedparams))
{
$module->advancedparams = $this->getAdvancedParams($module);
}
$module->advancedparams = json_decode($module->advancedparams);
if (is_null($module->advancedparams))
{
$module->advancedparams = (object) [];
}
if (
! isset($module->advancedparams->assignto_menuitems)
|| isset($module->advancedparams->assignto_urls_selection_sef)
|| (
! is_array($module->advancedparams->assignto_menuitems)
&& strpos($module->advancedparams->assignto_menuitems, '|') !== false
)
)
{
require_once JPATH_ADMINISTRATOR . '/components/com_advancedmodules/models/module.php';
$model = new AdvancedModulesModelModule;
$module->advancedparams = (object) $model->initAssignments($module->id, $module);
}
$xmlfile_assignments = JPATH_ADMINISTRATOR . '/components/com_advancedmodules/assignments.xml';
$use_cache = $module->use_amm_cache ?? true;
$module->advancedparams = RL_Parameters::getObjectFromRegistry($module->advancedparams, $xmlfile_assignments, '', $use_cache);
}
private function setExtraParams(&$module)
{
$extraparams = [];
for ($e = 1; $e <= 5; $e++)
{
$var = 'extra' . $e;
$extraparams[$var] = $module->advancedparams->{$var} ?? '';
}
if (empty($extraparams))
{
return;
}
if (empty($module->params))
{
$module->params = json_encode($extraparams);
return;
}
$params = (array) json_decode($module->params, true);
$params = array_merge($params, $extraparams);
$module->params = json_encode($params);
}
private function setMirrorParams(&$module)
{
$module->mirror_id = $this->getMirrorModuleId($module);
if (empty($module->mirror_id))
{
return;
}
$mirror_id = $module->mirror_id < 0 ? $module->mirror_id * -1 : $module->mirror_id;
$count = 0;
while ($count++ < 10)
{
if ( ! $test_mirrorid = $this->getMirrorModuleIdById($mirror_id))
{
break;
}
$mirror_id = $test_mirrorid;
}
if (empty($mirror_id))
{
return;
}
$xmlfile_assignments = JPATH_ADMINISTRATOR . '/components/com_advancedmodules/assignments.xml';
$module->reverse = $module->mirror_id < 0;
if ($mirror_id == $module->id)
{
$empty = (object) [];
$mirror_params = RL_Parameters::getObjectFromRegistry($empty, $xmlfile_assignments);
}
else
{
if (isset($modules[$mirror_id]))
{
if ( ! isset($modules[$mirror_id]->advancedparams))
{
$modules[$mirror_id]->advancedparams = $this->getAdvancedParamsById($mirror_id);
$modules[$mirror_id]->advancedparams = RL_Parameters::getObjectFromRegistry($modules[$mirror_id]->advancedparams, $xmlfile_assignments);
}
$mirror_params = $modules[$mirror_id]->advancedparams;
}
else
{
$mirror_params = $this->getAdvancedParamsById($mirror_id);
$mirror_params = RL_Parameters::getObjectFromRegistry($mirror_params, $xmlfile_assignments);
}
}
// Keep the advanced settings that shouldn't be mirrored
$settings_to_keep = [
'hideempty', 'color',
'pre_html', 'post_html',
'extra1', 'extra2', 'extra3', 'extra4', 'extra5'
];
foreach ($settings_to_keep as $key)
{
if ( ! isset($module->advancedparams->{$key}))
{
continue;
}
$mirror_params->{$key} = $module->advancedparams->{$key};
}
$module->advancedparams = $mirror_params;
}
}
Controls/CSS.php 0000644 00000002505 15241767254 0007527 0 ustar 00 <?php
/**
* @author Tassos Marinos <info@tassos.gr>
* @link http://www.tassos.gr
* @copyright Copyright © 2022 Tassos Marinos All Rights Reserved
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
*/
namespace NRFramework\Helpers\Controls;
defined('_JEXEC') or die;
class CSS
{
public static function generateCSS($styles = [])
{
if (!$styles || !is_array($styles))
{
return;
}
$css = '';
foreach ($styles as $breakpoint => $array)
{
if (!$selectors = self::groupCSSBySelectors($array))
{
continue;
}
$css_tmp = '';
// Get all the CSS for this breakpoint for all selectors
foreach ($selectors as $selector => $_styles)
{
$css_tmp .= $selector . '{' . implode('', $_styles) . '}';
}
// Then enapsulate all the breakpoint CSS in the breakpoint media query
$css_tmp = \NRFramework\Helpers\Responsive::renderResponsiveCSS([
$breakpoint => [$css_tmp]
]);
if (!$css_tmp)
{
continue;
}
$css .= $css_tmp;
}
return $css;
}
public static function groupCSSBySelectors($styles = [])
{
if (!$styles)
{
return;
}
$selectors = [];
foreach ($styles as $style)
{
$selectors[$style['selector']][] = $style['css'];
}
if (!$selectors)
{
return;
}
return $selectors;
}
} Controls/Control.php 0000644 00000015311 15241767256 0010520 0 ustar 00 <?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\Helpers\Controls;
defined('_JEXEC') or die;
class Control
{
/**
* Finds the value and unit in the given subject.
*
* @param string/array $subject
*
* @return array
*/
public static function findUnitInValue($subject = '')
{
if (is_null($subject))
{
return;
}
if (is_string($subject) && $subject === '')
{
return;
}
if (is_array($subject) && count($subject) === 0)
{
return;
}
if ($subject === 'auto')
{
return [
'value' => '',
'unit' => 'auto'
];
}
if (is_array($subject) && isset($subject['value']))
{
$return = [
'value' => $subject['value']
];
if (isset($subject['unit']))
{
$return['unit'] = $subject['unit'];
}
return $return;
}
$pattern = '/^([\d.]+)(\D+)?$/';
if (is_scalar($subject) && preg_match($pattern, $subject, $matches, PREG_OFFSET_CAPTURE) === 1)
{
return [
'value' => $matches[1][0],
'unit' => isset($matches[2][0]) ? $matches[2][0] : ''
];
}
return [
'value' => $subject,
'unit' => ''
];
}
/**
* Parses the given value to a CSS value.
*
* @param mixed $value
* @param string $unit
*
* @return string
*/
public static function getCSSValue($value = '', $unit = '')
{
if (is_null($value) || $value === '')
{
return;
}
// Is scalar, transform to array
if (is_scalar($value))
{
$value = array_filter(explode(' ', $value), function($value) {
return $value !== '';
});
}
if (!$value)
{
return;
}
if (is_array($value))
{
if (empty($value))
{
return;
}
// If all values are empty, abort
$isEmptyArray = array_filter($value, function($str) {
return $str === null || $str === false || $str === '' || (is_array($str) && empty($str));
});
if (count($isEmptyArray) === 4)
{
return;
}
// Apply spacing positions
if ($positions = self::findSpacingPositions($value))
{
$return = [];
foreach ($positions as $pos)
{
$return[$pos] = isset($value[$pos]) && $value[$pos] !== '' ? $value[$pos] : 0;
}
if (empty($return))
{
return;
}
$value = $return;
}
/**
* All values are duplicates, return only 1 number with their unit.
*
* Example: Given [5, 5, 5, 5] to print the margin in pixels, do not return `margin: 5px 5px 5px 5px`.
* Rather return `margin: 5px`
*/
if (count($value) === 4 && count(array_unique($value)) === 1)
{
$value = reset($value);
if ($value_data = self::findUnitInValue($value))
{
$value = $value_data['value'];
$unit = !empty($value_data['unit']) ? $value_data['unit'] : $unit;
}
if (is_array($value))
{
return;
}
return $value . ($value > 0 ? $unit : '');
}
/**
* If we were given 4 values and first/third & second/forth values are the same then return these only.
*
* Example: Given[5, 10, 5, 10] to print the margin in pixels, do not return `margin: 5px 10px 5px 10px`.
* Rather return `margin: 5px 10px`
*/
$keys = array_keys($value);
if (count($value) === 4 && $value[$keys[0]] === $value[$keys[2]] && $value[$keys[1]] === $value[$keys[3]])
{
$value1 = $value[$keys[0]];
$suffix1 = $suffix2 = $unit;
$value2 = $value[$keys[1]];
if ($value_1 = self::findUnitInValue($value1))
{
$value1 = $value_1['value'];
$suffix1 = !empty($value_1['unit']) ? $value_1['unit'] : $unit;
}
if ($value_2 = self::findUnitInValue($value2))
{
$value2 = $value_2['value'];
$suffix2 = !empty($value_2['unit']) ? $value_2['unit'] : $unit;
}
return $value1 . ($value1 > 0 ? $suffix1 : '') . ' ' . $value2 . ($value2 > 0 ? $suffix2 : '');
}
// Different values
$data = [];
foreach ($value as $key => $_value)
{
$val = $_value;
if ($value_data = self::findUnitInValue($val))
{
$val = $value_data['value'];
$unit = !empty($value_data['unit']) ? $value_data['unit'] : $unit;
}
$data[] = $val . ($val > 0 ? $unit : '');
}
return implode(' ', $data);
}
return;
}
/**
* Finds an array of positions of the given value that
* relates to margin/padding or border radius.
*
* @param array $value
*
* @return array
*/
public static function findSpacingPositions($value = [])
{
if (!is_array($value) || !count($value))
{
return;
}
$keys = array_keys($value);
// Is margin/padding
$margin_padding = self::getPositions();
if (in_array($keys[0], $margin_padding, true))
{
return $margin_padding;
}
// Is border radius
$border_radius = self::getPositions('border_radius');
if (in_array($keys[0], $border_radius, true))
{
return $border_radius;
}
return;
}
/**
* Return the position keys based on the control type.
*
* @param string $type
*
* @return array
*/
public static function getPositions($type = 'margin_padding')
{
if (!$type)
{
return [];
}
$margin_padding = [
'top',
'right',
'bottom',
'left'
];
$border_radius = [
'top_left',
'top_right',
'bottom_right',
'bottom_left'
];
return $type === 'margin_padding' ? $margin_padding : $border_radius;
}
} Controls/Spacing.php 0000644 00000005316 15241767261 0010464 0 ustar 00 <?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\Helpers\Controls;
defined('_JEXEC') or die;
use NRFramework\Helpers\Controls\Control;
class Spacing
{
/**
* Parses the given value and returns the value expected by Spacing Control.
*
* @param mixed $value
* @param string $type This can be margin_padding or border_radius.
*
* @return array
*/
public static function parseInputValue($value = '', $type = 'margin_padding')
{
if (!$value)
{
return [];
}
$linked = isset($value['linked']) ? $value['linked'] : '0';
$unit = isset($value['unit']) ? $value['unit'] : 'px';
$value = isset($value['value']) ? $value['value'] : $value;
$positions = Control::getPositions($type);
// If it's a string of values, prepare it to be an array and continue
if (is_scalar($value))
{
$value = array_filter(explode(' ', $value), function($value) {
return $value !== '';
});
// Get the unit from the first found value
foreach ($value as $val)
{
$_value = Control::findUnitInValue($val);
if (!isset($_value['unit']))
{
continue;
}
$unit = !empty($_value['unit']) ? $_value['unit'] : $unit;
break;
}
// Ensure only ints are in the array
$value = array_map('intval', $value);
// If only a single value is given, apply the value to all positions
if (count($value) === 1)
{
$value = array_merge($value, $value, $value, $value);
}
if (count($value) === 2)
{
$value = [$value[0], $value[1], $value[0], $value[1]];
}
$tmp_value = [];
foreach ($positions as $index => $pos)
{
$tmp_value[$pos] = isset($value[$index]) ? $value[$index] : '';
}
$value = $tmp_value;
}
// Return value
$return = [];
foreach ($positions as $pos)
{
$return[$pos] = isset($value[$pos]) && $value[$pos] !== '' ? intval($value[$pos]) : '';
}
if (empty($return))
{
return [];
}
$return['linked'] = $linked;
$return['unit'] = $unit;
return $return;
}
} Controls/index.php 0000644 00000000000 15241767266 0010175 0 ustar 00 Widgets/Gallery.php 0000644 00000027555 15241767270 0010313 0 ustar 00 <?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\Helpers\Widgets;
defined('_JEXEC') or die;
use NRFramework\Mimes;
use Joomla\CMS\Helper\ModuleHelper;
use Joomla\CMS\Factory;
use Joomla\CMS\Uri\Uri;
class Gallery
{
/**
* Stores all gallery parsed directories info txt file `*.gallery_info.txt` data in format:
* GALLERY DIRECTORY => ARRAY OF `*.gallery_info.txt` file data
*
* @var array
*/
static $gallery_directories_info_file = [];
/**
* Stores all galleries info file names in format:
*
* GALLERY DIRECTORY => INFO FILE NAME
*
* @var array
*/
static $gallery_directories_info_file_names = [];
/**
* The directory information file holding all gallery item details.
*
* @var string
*/
const directory_gallery_info_file = 'gallery_info.txt';
/**
* Parses the given gallery items.
*
* @param mixed $input A string to a directory/path/URL or an array of a URL item containing its information.
* @param array $allowed_file_types The allowed file types.
*
* @return mixed
*/
public static function parseGalleryItems($input, $allowed_file_types = [])
{
if (is_string($input))
{
$fullpath_input = JPATH_ROOT . DIRECTORY_SEPARATOR . ltrim($input, DIRECTORY_SEPARATOR);
// Parse Directory
if (is_dir($fullpath_input))
{
return self::parseDirectory($fullpath_input, $allowed_file_types);
}
// Skip invalid URLs
if ($url = self::parseURL($input))
{
return [$url];
}
// Parse Image
if ($image_data = self::parseImage($fullpath_input, $allowed_file_types))
{
return [$image_data];
}
}
return [self::parseURL($input)];
}
/**
* Parse the directory by finding all of its images and their information.
*
* @param string $dir
* @param array $allowed_file_types
*
* @return mixed
*/
public static function parseDirectory($dir, $allowed_file_types = [])
{
if (!is_string($dir) || !is_dir($dir) || empty($allowed_file_types))
{
return;
}
$items = [];
// Get images
$files = array_diff(scandir($dir), ['.', '..', '.DS_Store']);
foreach ($files as $key => $filename)
{
// Skip directories
if (is_dir($dir . DIRECTORY_SEPARATOR . $filename))
{
continue;
}
$image_path = rtrim($dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $filename;
if (!$image_data = self::parseImage($image_path, $allowed_file_types))
{
continue;
}
$items[] = $image_data;
}
return $items;
}
/**
* Parse the directory image and return its information.
*
* @param string $image_path
* @param string $allowed_file_types
*
* @return mixed
*/
public static function parseImage($image_path, $allowed_file_types = null)
{
if (!is_string($image_path))
{
return;
}
$data = [
'path' => $image_path,
'url' => self::directoryImageToURL($image_path)
];
if (!is_file($image_path))
{
return array_merge($data, [
'invalid' => true
]);
}
// Skip not allowed file types
if (!is_null($allowed_file_types) && !Mimes::check($allowed_file_types, Mimes::detectFileType($image_path)))
{
return;
}
// Check if there is a `*.gallery_info.txt` helper file and get any information about the image
$gallery_info_file_data = self::getGalleryInfoFileData(dirname($image_path));
if (!$gallery_info_file_data)
{
return $data;
}
$image_filename = pathinfo($image_path, PATHINFO_BASENAME);
// If no information from the text field about this image was found, stop
if (!isset($gallery_info_file_data[$image_filename]))
{
return $data;
}
$image_data = $gallery_info_file_data[$image_filename];
return array_merge($data, [
'caption' => isset($image_data['caption']) ? $image_data['caption'] : ''
]);
}
/**
* Parses a single URL either as a String or as an Array.
*
* @param mixed $item
*
* @return mixed
*/
public static function parseURL($item)
{
// URL is a string
if (is_string($item))
{
if (!filter_var($item, FILTER_VALIDATE_URL))
{
return;
}
return [
'url' => $item
];
}
// URL is an array
if (!is_array($item) || !count($item))
{
return;
}
// If a thumbnail URL is given but no URL, use it as the full image URL
if (isset($item['thumbnail_url']) && !isset($item['url']))
{
$item['url'] = $item['thumbnail_url'];
}
if (!isset($item['url']))
{
return;
}
if (!filter_var($item['url'], FILTER_VALIDATE_URL))
{
return;
}
return $item;
}
/**
* Loads a module by its ID.
*
* @param string $id
*
* @return string
*/
public static function loadModule($id)
{
$module = ModuleHelper::getModuleById($id);
$params = ['style' => 'none'];
return $module->id > 0 ? Factory::getDocument()->loadRenderer('module')->render($module, $params) : '';
}
/**
* Read the `*.gallery_info.txt` file for the given directory.
*
* @param string $dir
*
* @return mixed
*/
public static function getGalleryInfoFileData($dir)
{
if (isset(self::$gallery_directories_info_file[$dir]) && !empty(self::$gallery_directories_info_file[$dir]))
{
return self::$gallery_directories_info_file[$dir];
}
if (!$file = self::findGalleryInfoFile($dir))
{
return [];
}
// Read file
if (!$handle = fopen($file, 'r'))
{
return [];
}
$data = [];
$line_defaults = ['', '', ''];
// Loop each line
while (($line = fgets($handle)) !== false)
{
list($filename, $caption, $hash) = explode('|', $line) + $line_defaults;
// If no filename is given, continue
if (!$filename)
{
continue;
}
$data[$filename] = [
'filename' => $filename,
'caption' => trim($caption),
'hash' => trim($hash)
];
}
// Close file
fclose($handle);
self::$gallery_directories_info_file[$dir] = $data;
return $data;
}
/**
* Finds the source image and whether it has been edited.
*
* @param string $source
* @param string $destination_folder
*
* @return mixed
*/
public static function findSourceImageDetails($source, $destination_folder)
{
$source_filename = pathinfo($source, PATHINFO_BASENAME);
$data = self::getGalleryInfoFileData(dirname($source));
$image_data = isset($data[$source_filename]) ? $data[$source_filename] : false;
if (!$image_data)
{
return false;
}
if (empty($image_data['hash']))
{
return false;
}
$sourceHash = self::calculateFileHash($source);
return [
'path' => $destination_folder . $image_data['filename'],
'edited' => $image_data['hash'] !== $sourceHash
];
}
/**
* Updates or Inserts the given image information from the gallery info file.
*
* @param string $source
* @param array $image_data
*
* @return mixed
*/
public static function updateImageDataInGalleryInfoFile($source, $image_data)
{
// Source directory
$source_directory = dirname($source);
// Check whether the gallery info file exists, if not, create it
if (!$file = self::findGalleryInfoFile($source_directory))
{
$file = self::createGalleryInfoFile($source_directory);
}
// Open files
$reading = fopen($file, 'r');
$writing = fopen($file . '.tmp', 'w');
$replaced = false;
while (!feof($reading))
{
// Get each file line
$line = fgets($reading);
// Remove new line at the end
$line = trim(preg_replace('/\s\s+/', ' ', $line));
// Skip empty lines
if (empty($line))
{
continue;
}
list($filename, $caption, $hash) = explode('|', $line) + ['', '', ''];
// We need to manipulate current file
if (strtolower($filename) !== strtolower(basename($image_data['path'])))
{
fputs($writing, $line . "\n");
continue;
}
$replaced = true;
$line = $filename . '|' . $caption . '|' . self::calculateFileHash($source) . "\n";;
// Write changed line
fputs($writing, $line);
}
// Close files
fclose($reading);
fclose($writing);
// If we replaced a line, update the text file
if ($replaced)
{
rename($file . '.tmp', $file);
}
// No line was replaced, append image details
else
{
unlink($file . '.tmp');
self::appendImageDataToGalleryInfoFile($file, $source, $image_data);
}
}
/**
* Removes the image from the gallery info file.
*
* @param string $source
*
* @return boolean
*/
public static function removeImageFromGalleryInfoFile($source)
{
// Get the gallery info file from destination folder
if (!$file = self::findGalleryInfoFile(dirname($source)))
{
return false;
}
// Open files
$reading = fopen($file, 'r');
$writing = fopen($file . '.tmp', 'w');
$found = false;
while (!feof($reading))
{
// Get each file line
$line = fgets($reading);
// Remove new line at the end
$line = trim(preg_replace('/\s\s+/', ' ', $line));
// Skip empty lines
if (empty($line))
{
continue;
}
list($filename, $caption, $hash) = explode('|', $line) + ['', '', ''];
// We need to manipulate current file
if ($filename !== pathinfo($source, PATHINFO_BASENAME))
{
$found = true;
fputs($writing, $line . "\n");
continue;
}
}
// Close files
fclose($reading);
fclose($writing);
if (!$found)
{
return false;
}
// Save the changes
rename($file . '.tmp', $file);
return true;
}
/**
* Appends the image data into the info file.
*
* @param string $dir
*
* @return void
*/
public static function createGalleryInfoFile($dir)
{
$file = self::getLanguageInfoFileName($dir);
file_put_contents($file, '');
return $file;
}
/**
* Appends the image data into the info file.
*
* @param string $file
* @param string $source
* @param object $image_data
*
* @return void
*/
public static function appendImageDataToGalleryInfoFile($file, $source, $image_data)
{
$caption = isset($image_data['caption']) ? $image_data['caption'] : '';
$hash = self::calculateFileHash($source);
$line = pathinfo($source, PATHINFO_BASENAME) . '|' . $caption . '|' . $hash . "\n";
file_put_contents($file, $line, FILE_APPEND);
}
/**
* Finds the `*.gallery_info.txt` file if it exists in the given directory.
*
* @param string $dir
*
* @return mixed
*/
public static function findGalleryInfoFile($dir)
{
if (isset(self::$gallery_directories_info_file_names[$dir]))
{
return self::$gallery_directories_info_file_names[$dir];
}
// Method 1: With language prefix
$file = self::getLanguageInfoFileName($dir);
// Check if the file exists
if (file_exists($file))
{
self::$gallery_directories_info_file_names[$dir] = $file;
return $file;
}
// Method 2: Without the language prefix
$file = rtrim($dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . self::directory_gallery_info_file;
// Check if the file exists
if (file_exists($file))
{
self::$gallery_directories_info_file_names[$dir] = $file;
return $file;
}
return false;
}
/**
* Returns the info file with the language prefix.
*
* @param string $dir
*
* @return string
*/
public static function getLanguageInfoFileName($dir)
{
return rtrim($dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . Factory::getLanguage()->getTag() . '.' . self::directory_gallery_info_file;
}
/**
* Calculates the file hash of a file.
*
* Hash = md5(file path + last modified date of file)
*
* @param string $file_path
*
* @return string
*/
public static function calculateFileHash($file_path)
{
return md5($file_path . filemtime($file_path));
}
/**
* Transforms an image path to a URL.
*
* @param string $image_path
*
* @return string
*/
public static function directoryImageToURL($image_path)
{
return rtrim(Uri::root(), DIRECTORY_SEPARATOR) . mb_substr($image_path, strlen(JPATH_BASE), null);;
}
} Widgets/GalleryManager.php 0000644 00000060365 15241767272 0011604 0 ustar 00 <?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\Helpers\Widgets;
defined('_JEXEC') or die;
use NRFramework\File;
use NRFramework\Image;
use NRFramework\Functions;
use Joomla\Registry\Registry;
use Joomla\CMS\Table\Table;
use Joomla\CMS\Factory;
use Joomla\Filesystem\Path;
use Joomla\Filesystem\File as JoomlaCMSFile;
class GalleryManager
{
/**
* How long the files can stay in the temp folder.
*
* After each save a clean up is run and all files older
* than this value in days are removed.
*
* @var int
*/
private static $temp_files_cleanup_days = 1;
/**
* Upload file
*
* @param array $file The request file as posted by form
* @param string $upload_settings The upload settings
* @param array $media_uploader_file_data Media uploader related file settings
* @param array $resizeSettings The resize settings
*
* @return mixed String on success, Null on failure
*/
public static function upload($file, $upload_settings, $media_uploader_file_data, $resizeSettings)
{
// The source file name
$source = '';
// Move the image to the tmp folder
try {
$source = File::upload($file, self::getFullTempFolder(), $upload_settings['allowed_types'], $upload_settings['allow_unsafe']);
} catch (\Throwable $th)
{
return false;
}
if (!$source)
{
return false;
}
$source_file_path = $source;
$ds = DIRECTORY_SEPARATOR;
// If the file came from the Media Manager file and we are copying it, fix its filename
if ($media_uploader_file_data['is_media_uploader_file'])
{
$media_uploader_file_data['media_uploader_filename'] = self::getFilePathFromMediaUploaderFile($media_uploader_file_data['media_uploader_filename']);
}
$source_image_relative = '';
$original_image_relative = '';
// Create source image by cloning the original image
$original_image_extension = pathinfo($source, PATHINFO_EXTENSION);
$original_image_destination = str_replace('.' . $original_image_extension, '_original.' . $original_image_extension, $source);
// Thumbnail file name
$thumb_image_destination = str_replace('.' . $original_image_extension, '_thumb.' . $original_image_extension, $source);
// Check whether to copy and resize the original image
if ($resizeSettings['original_image_resize'])
{
if ($resizeSettings['original_image_resize_width'] && $resizeSettings['original_image_resize_height'])
{
$original_image_full = Image::resize($source, $resizeSettings['original_image_resize_width'], $resizeSettings['original_image_resize_height'], 70, 'crop', $original_image_destination, true);
}
else if ($resizeSettings['original_image_resize_width'])
{
$original_image_full = Image::resizeAndKeepAspectRatio($source, $resizeSettings['original_image_resize_width'], 70, $original_image_destination, true);
}
else if ($resizeSettings['original_image_resize_height'])
{
$original_image_full = Image::resizeByHeight($source, $resizeSettings['original_image_resize_height'], $original_image_destination, 70, true);
}
$original_image_relative = str_replace(JPATH_ROOT . $ds, '', $original_image_full);
// Delete raw image as not needed
JoomlaCMSFile::delete($source);
}
else
{
// Original image must always be cloned by the resized original image
$original_image_full = File::move($source, $original_image_destination);
$original_image_relative = str_replace(JPATH_ROOT . $ds, '', $original_image_full);
}
// Generate thumbnails
if (!$thumb_data = self::generateThumbnail($original_image_full, $thumb_image_destination, $resizeSettings))
{
return false;
}
// Add watermark image
if (isset($upload_settings['watermark']['type']) && $upload_settings['watermark']['type'] !== 'disabled')
{
// Clone source image from original image and hash it
$source_image_full = File::copy($original_image_full, $source_file_path, false, true);
$source_image_relative = str_replace(JPATH_ROOT . $ds, '', $source_image_full);
// Add watermark to original image
$payload = array_merge($upload_settings['watermark'], [
'source' => $original_image_full
]);
\NRFramework\Image::applyWatermark($payload);
if (isset($upload_settings['watermark']['apply_on_thumbnails']) && $upload_settings['watermark']['apply_on_thumbnails'])
{
// Add watermark to original image
$payload = array_merge($upload_settings['watermark'], [
'source' => implode($ds, [self::getFullTempFolder(), $thumb_data['resized_filename']])
]);
\NRFramework\Image::applyWatermark($payload);
}
}
$tmp_folder = self::getTempFolder();
return [
'source' => $source_image_relative ? $source_image_relative : '',
'original' => $original_image_relative,
'thumbnail' => implode($ds, [$tmp_folder, $thumb_data['resized_filename']])
];
}
/**
* Moves all given `tmp` items over to the destination folder.
*
* @param array $value
* @param object $field
* @param string $destination_folder
*
* @return void
*/
public static function moveTempItemsToDestination($value, $field, $destination_folder)
{
if (!$destination_folder)
{
return;
}
// Create destination folder if missing
if (!File::createDirs($destination_folder))
{
return;
}
// Make field params use Registry
if (!$field->fieldparams instanceof Registry)
{
$field->fieldparams = new Registry($field->fieldparams);
}
/**
* Prepare the items for backwards compatibility
*/
$items = is_string($value) ? json_decode($value, true) ?? [['value' => $value]] : $value;
$items = isset($items['items']) ? $items['items'] : $items;
if (isset($items['value']))
{
$items = [$items];
}
$limit_files = (int) $field->fieldparams->get('limit_files', 0);
// Handle single file
if ($limit_files === 1 && is_array($items))
{
$items = [reset($items)];
}
$ds = DIRECTORY_SEPARATOR;
// Move all files from `tmp` folder over to the `upload folder`
foreach ($items as $key => &$item)
{
/**
* Skip invalid files.
*
* These "files" can appear when we try to move files
* over to the destination folder when the gallery manager
* is still working to upload queueed files.
*
* Also skip any items that have no value.
*/
if ($key === 'ITEM_ID' || empty($item['thumbnail']))
{
continue;
}
$moved = false;
// Ensure thumbnail in temp folder file exists
$thumbnail_clean = pathinfo($item['thumbnail'], PATHINFO_BASENAME);
$thumbnail_path = implode($ds, [JPATH_ROOT, $item['thumbnail']]);
// Move thumbnail image
if (Functions::startsWith($item['thumbnail'], self::getTempFolder()) && file_exists($thumbnail_path))
{
// Move thumbnail
$thumb = File::move($thumbnail_path, $destination_folder . $thumbnail_clean);
// Update thumbnail file name
$item['thumbnail'] = pathinfo($thumb, PATHINFO_BASENAME);
$moved = true;
}
// Check if we have uploaded the full image as well and set it
$image_clean = pathinfo($item['image'], PATHINFO_BASENAME);
$image_path = implode($ds, [JPATH_ROOT, $item['image']]);
// Move original image
if (Functions::startsWith($item['image'], self::getTempFolder()) && file_exists($image_path))
{
// Move image
$image = File::move($image_path, $destination_folder . $image_clean);
// Update image file name
$item['image'] = pathinfo($image, PATHINFO_BASENAME);
$moved = true;
}
// Ensure source in temp folder file exists
$item['source'] = isset($item['source']) ? $item['source'] : '';
// If source does not exist, create it from the original image, only if watermark is enabled
if (!$item['source'] && $field->fieldparams->get('watermark.type', 'disabled') !== 'disabled')
{
// Create source from original image
$source = File::copy($image_path, $image_path, false, true);
// Update source file name
$item['source'] = pathinfo($source, PATHINFO_BASENAME);
$moved = true;
}
// Move source image
$source_clean = pathinfo($item['source'], PATHINFO_BASENAME);
$source_path = implode($ds, [JPATH_ROOT, $item['source']]);
if (Functions::startsWith($item['source'], self::getTempFolder()) && file_exists($source_path))
{
// Move source
$thumb = File::move($source_path, $destination_folder . $source_clean);
// Update source file name
$item['source'] = pathinfo($thumb, PATHINFO_BASENAME);
$moved = true;
}
if ($moved)
{
// Update destination path
self::updateDestinationPath($item, $destination_folder);
}
}
return $items;
}
/**
* Saves the tags for each item.
*
* @param array $value
*
* @return array
*/
public static function saveItemTags($value = [])
{
if (!is_array($value))
{
return $value;
}
foreach ($value as &$item)
{
if (!isset($item['tags']) || !is_string($item['tags']))
{
$item['tags'] = [];
continue;
}
if (!$itemTags = json_decode($item['tags'], true))
{
$item['tags'] = [];
continue;
}
if (!is_array($itemTags))
{
$item['tags'] = [];
continue;
}
if (!$itemTags)
{
$item['tags'] = [];
continue;
}
// Make $itemTags an array of strings
$itemTags = array_map(function($tag) {
return (string) $tag;
}, $itemTags);
/**
* Creates the new tags in the #__tags table.
*
* This returns an array of the new tag ids. If a tag isn't new (doesn't have #new# prefix), it will return 0 as its id.
*
* We will now store the IDs returned as the tags for the item.
*/
$item['tags'] = self::createTagsFromField($itemTags);
}
return $value;
}
/**
* Create any new tags by looking for #new# in the strings
*
* @param array $tags Tags text array from the field
*
* @return mixed If successful, metadata with new tag titles replaced by tag ids. Otherwise false.
*
* @since 3.1
*/
public static function createTagsFromField($tags)
{
if (empty($tags) || $tags[0] == '')
{
return;
}
// We will use the tags table to store them
if (defined('nrJ4'))
{
$tagTable = Factory::getApplication()->bootComponent('com_tags')->getMVCFactory()->createTable('Tag', 'Administrator');
}
else
{
Table::addIncludePath(JPATH_ADMINISTRATOR . '/components/com_tags/tables');
$tagTable = Table::getInstance('Tag', 'TagsTable');
}
$newTags = [];
foreach ($tags as $key => $tag)
{
// Remove the #new# prefix that identifies new tags
$tagText = str_replace('#new#', '', $tag);
if ($tagText === $tag)
{
$newTags[] = (int) $tag;
}
else
{
// Clear old data if exist
$tagTable->reset();
// Try to load the selected tag
if ($tagTable->load(['title' => $tagText]))
{
$newTags[] = (int) $tagTable->id;
}
else
{
// Prepare tag data
$tagTable->id = 0;
$tagTable->title = $tagText;
$tagTable->published = 1;
$tagTable->description = '';
$tagTable->language = '*';
$tagTable->access = 1;
// Make this item a child of the root tag
$tagTable->setLocation($tagTable->getRootId(), 'last-child');
// Try to store tag
if ($tagTable->check())
{
// Assign the alias as path (autogenerated tags have always level 1)
$tagTable->path = $tagTable->alias;
if ($tagTable->store())
{
$newTags[] = (int) $tagTable->id;
}
}
}
}
}
// At this point $newTags is an array of all tag ids
return $newTags;
}
/**
* Updates the destination path for the image and its thumbnail to the final destination folder.
*
* @param array $item
* @param string $destination_folder
*
* @return mixed
*/
private static function updateDestinationPath(&$item, $destination_folder)
{
$ds = DIRECTORY_SEPARATOR;
// Ensure destination folder is a relative path
$destination_folder = ltrim(rtrim(str_replace(JPATH_ROOT, '', $destination_folder), $ds), $ds);
$item = array_merge($item, [
'source' => !empty($item['source']) && !Functions::startsWith($item['source'], $destination_folder) ? implode($ds, [$destination_folder, $item['source']]) : $item['source'],
'thumbnail' => !Functions::startsWith($item['thumbnail'], $destination_folder) ? implode($ds, [$destination_folder, $item['thumbnail']]) : $item['thumbnail'],
'image' => !Functions::startsWith($item['image'], $destination_folder) ? implode($ds, [$destination_folder, $item['image']]) : $item['image']
]);
}
/**
* Sets the custom field item id > field id value "source" to given source image path for the original image path
*/
public static function setItemFieldSource($item_id, $field_id, $sourceImagePath, $originalImagePath)
{
// Get "value" column from #__fields_values where item_id = $item_id and $field_id = $field_id
$db = Factory::getDbo();
$query = $db->getQuery(true)
->select($db->qn('value'))
->from($db->qn('#__fields_values'))
->where($db->qn('item_id') . ' = ' . $db->q($item_id))
->where($db->qn('field_id') . ' = ' . $db->q($field_id));
$db->setQuery($query);
$value = $db->loadResult();
// If value is empty, return
if (!$value)
{
return;
}
// Decode value
$value = json_decode($value, true);
// If value is empty, return
if (!$value)
{
return;
}
// If value is not an array, return
if (!is_array($value))
{
return;
}
// If value has no items, return
if (!isset($value['items']))
{
return;
}
// Loop all items until we find a "image" = $originalImagePath and then set 'source' = $sourceImagePath
foreach ($value['items'] as $key => &$item)
{
if ($item['image'] !== $originalImagePath)
{
continue;
}
$item['source'] = $sourceImagePath;
}
// Update value
$query = $db->getQuery(true)
->update($db->qn('#__fields_values'))
->set($db->qn('value') . ' = ' . $db->q(json_encode($value)))
->where($db->qn('item_id') . ' = ' . $db->q($item_id))
->where($db->qn('field_id') . ' = ' . $db->q($field_id));
$db->setQuery($query);
$db->execute();
}
/**
* Media Uploader files look like: https://example.com/images/sampledata/parks/banner_cradle.png
* We remove the first part (https://example.com/images/) and keep the other part (relative path to image).
*
* @param string $filename
*
* @return string
*/
private static function getFilePathFromMediaUploaderFile($filename)
{
$filenameArray = explode('images/', $filename, 2);
unset($filenameArray[0]);
$new_filepath = join($filenameArray);
return 'images/' . $new_filepath;
}
/**
* Generates thumbnail
*
* @param string $source Source image path.
* @param string $destination Destination image path.
* @param array $resizeSettings Resize Settings.
* @param string $destination_folder Destination folder.
* @param boolean $unique_filename Whether the thumbnails will have a unique filename.
*
* @return array
*/
public static function generateThumbnail($source = '', $destination = '', $resizeSettings = [], $destination_folder = null, $unique_filename = true)
{
if (!$destination)
{
$parts = pathinfo($source);
$destination_folder = !is_null($destination_folder) ? $destination_folder : $parts['dirname'] . DIRECTORY_SEPARATOR;
$destination = $destination_folder . $parts['filename'] . '_thumb.' . $parts['extension'];
}
$resized_image = null;
$thumb_width = isset($resizeSettings['thumb_width']) ? (int) $resizeSettings['thumb_width'] : null;
$thumb_height = isset($resizeSettings['thumb_height']) ? (int) $resizeSettings['thumb_height'] : null;
// If thumbnail width is null, and we have item height set, we are resizing by height
if (is_null($thumb_width) && $thumb_height && !is_null($thumb_height))
{
$resized_image = Image::resizeByHeight($source, $thumb_height, $destination, 70, $unique_filename, true, 'resize');
}
else
{
if (is_null($thumb_width) || !$thumb_width)
{
return;
}
/**
* If height is zero, then we suppose we want to keep aspect ratio.
*
* Resize with width & height: If thumbnail height is not set
* Resize and keep aspect ratio: If thumbnail height is set
*/
$resized_image = $thumb_height && !is_null($thumb_height)
?
Image::resize($source, $thumb_width, $thumb_height, 70, $resizeSettings['thumb_resize_method'], $destination, $unique_filename, true, 'resize')
:
Image::resizeAndKeepAspectRatio($source, $thumb_width, 70, $destination, $unique_filename, true, 'resize');
}
if (!$resized_image)
{
return;
}
return [
'filename' => basename($source),
'resized_filename' => basename($resized_image)
];
}
/**
* Deletes an uploaded files: source, original, and thumbnail.
*
* @param string $source The source image path.
* @param string $original The original image path.
* @param string $thumbnail The thumbnail image path.
*
* @return bool
*/
public static function deleteFile($source = null, $original = null, $thumbnail = null)
{
return [
'deleted_source_image' => self::findAndDeleteFile($source),
'deleted_original_image' => self::findAndDeleteFile($original),
'deleted_thumbnail' => self::findAndDeleteFile($thumbnail)
];
}
/**
* Deletes the file.
*
* @param string $filepath
*
* @return mixed
*/
private static function findAndDeleteFile($filepath)
{
if (!$filepath)
{
return;
}
$file = Path::clean(implode(DIRECTORY_SEPARATOR, [JPATH_ROOT, $filepath]));
return file_exists($file) ? JoomlaCMSFile::delete($file) : false;
}
/**
* Cleans the temp folder.
*
* Removes any image that is 1 day or older.
*
* @return void
*/
public static function clean()
{
$temp_folder = self::getFullTempFolder();
if (!is_dir($temp_folder))
{
return;
}
// Get images
$files = array_diff(scandir($temp_folder), ['.', '..', '.DS_Store', 'index.html']);
$found = [];
foreach ($files as $key => $filename)
{
$file_path = implode(DIRECTORY_SEPARATOR, [$temp_folder, $filename]);
// Skip directories
if (is_dir($file_path))
{
continue;
}
$diff_in_miliseconds = time() - filemtime($file_path);
// Skip the file if it's not old enough
if ($diff_in_miliseconds < (60 * 60 * 24 * self::$temp_files_cleanup_days))
{
continue;
}
$found[] = $file_path;
}
if (!$found)
{
return;
}
// Delete found old files
foreach ($found as $file)
{
unlink($file);
}
}
/**
* Full temp directory where images are uploaded
* prior to them being saved in the final directory.
*
* @return string
*/
private static function getFullTempFolder()
{
return implode(DIRECTORY_SEPARATOR, [JPATH_ROOT, self::getTempFolder()]);
}
/**
* Temp folder where images are uploaded
* prior to them being saved in the final directory.
*
* @var string
*/
public static function getTempFolder()
{
return implode(DIRECTORY_SEPARATOR, ['media', 'tfgallerymanager', 'tmp']);
}
/**
* Deletes a specific tag from every gallery item.
*
* @param int $tag_id
* @param string $context
*
* @return void
*/
public static function deleteTagFromFieldsValues($tag_id = null, $context = '')
{
if (!$tag_id)
{
return;
}
if ($context === '')
{
self::deleteTagFromCustomFieldsByTagId($tag_id);
self::deleteTagFromSubformCustomFieldsByTagId($tag_id);
}
}
/**
* Deletes a specific tag from every gallery item custom field.
*
* @param int $tag_id
*
* @return void
*/
private static function deleteTagFromCustomFieldsByTagId($tag_id = null)
{
if (!$tag_id)
{
return;
}
$db = Factory::getDbo();
$query = $db->getQuery(true)
->select('f.id as field_id, fv.item_id as item_id, fv.value as value')
->from('#__fields as f')
->join('LEFT', '#__fields_values AS fv ON fv.field_id = f.id')
->where('f.type = ' . $db->quote('acfgallery'));
$db->setQuery($query);
$fields = $db->loadAssocList();
if (!$fields)
{
return;
}
foreach ($fields as $field)
{
if (!$decoded_value = json_decode($field['value'], true))
{
continue;
}
if (!isset($decoded_value['items']))
{
continue;
}
$update = false;
foreach ($decoded_value['items'] as &$item)
{
if (!isset($item['tags']))
{
continue;
}
if (!is_array($item['tags']))
{
continue;
}
if (!count($item['tags']))
{
continue;
}
$item['tags'] = array_values($item['tags']);
if (($key = array_search($tag_id, $item['tags'])) !== false)
{
$update = true;
unset($item['tags'][$key]);
}
$item['tags'] = array_values($item['tags']);
}
if (!$update)
{
continue;
}
$field['value'] = json_encode($decoded_value);
// Update field value
$query->clear()
->update('#__fields_values')
->set($db->quoteName('value') . ' = ' . $db->quote($field['value']))
->where($db->quoteName('field_id') . ' = ' . $db->quote($field['field_id']))
->where($db->quoteName('item_id') . ' = ' . $db->quote($field['item_id']));
$db->setQuery($query);
$db->execute();
}
}
/**
* Deletes a specific tag from every gallery item that exists in a subform custom field.
*
* @param int $tag_id
*
* @return void
*/
private static function deleteTagFromSubformCustomFieldsByTagId($tag_id = null)
{
if (!$tag_id)
{
return;
}
if (!$tag_id)
{
return;
}
$db = Factory::getDbo();
// Get all ACF Gallery custom field IDs
$query = $db->getQuery(true)
->select('distinct f.id')
->from('#__fields as f')
->join('LEFT', '#__fields_values AS fv ON fv.field_id = f.id')
->where('f.type = ' . $db->quote('acfgallery'));
$db->setQuery($query);
$gallery_field_ids = array_keys($db->loadAssocList('id'));
if (!$gallery_field_ids)
{
return;
}
// Get all Subform custom fields
$query->clear()
->select('f.id as field_id, fv.item_id as item_id, fv.value as value')
->from('#__fields as f')
->join('LEFT', '#__fields_values AS fv ON fv.field_id = f.id')
->where('f.type = ' . $db->quote('subform'));
$db->setQuery($query);
$subform_fields = $db->loadAssocList();
foreach ($subform_fields as $subform_field)
{
if (!$subform_field_items = json_decode($subform_field['value'], true))
{
continue;
}
$update = false;
foreach ($subform_field_items as $row => &$row_items)
{
if (!is_array($row_items))
{
continue;
}
foreach ($row_items as $field_name => &$field_value)
{
// Get the field id
$field_id = str_replace('field', '', $field_name);
// Check if its a gallery field
if (!in_array($field_id, $gallery_field_ids))
{
continue;
}
if (!isset($field_value['items']))
{
continue;
}
foreach ($field_value['items'] as &$item)
{
if (!isset($item['tags']))
{
continue;
}
if (!is_array($item['tags']))
{
continue;
}
if (!count($item['tags']))
{
continue;
}
$item['tags'] = array_values($item['tags']);
if (($key = array_search($tag_id, $item['tags'])) !== false)
{
$update = true;
unset($item['tags'][$key]);
}
$item['tags'] = array_values($item['tags']);
}
}
}
if (!$update)
{
continue;
}
$subform_field['value'] = json_encode($subform_field_items);
// Update subform field value
$query->clear()
->update('#__fields_values')
->set($db->quoteName('value') . ' = ' . $db->quote($subform_field['value']))
->where($db->quoteName('field_id') . ' = ' . $db->quote($subform_field['field_id']))
->where($db->quoteName('item_id') . ' = ' . $db->quote($subform_field['item_id']));
$db->setQuery($query);
$db->execute();
}
}
} Widgets/GalleryManager2.php 0000644 00000077532 15241767277 0011677 0 ustar 00 <?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\Helpers\Widgets;
defined('_JEXEC') or die;
use NRFramework\File;
use NRFramework\Image;
use NRFramework\Functions;
use Joomla\Registry\Registry;
use Joomla\CMS\Table\Table;
use Joomla\CMS\Factory;
use Joomla\Filesystem\Path;
use Joomla\CMS\Language\Text;
use Joomla\Filesystem\File as JoomlaCMSFile;
class GalleryManager2
{
/**
* How long the files can stay in the temp folder.
*
* After each save a clean up is run and all files older
* than this value in days are removed.
*
* @var int
*/
private static $temp_files_cleanup_days = 1;
/**
* Uploads the source image to the temp folder.
*
* @param array $file The request file as posted by form
* @param string $upload_settings The upload settings
*
* @return array|bool The uploaded image paths or false on failure
*/
public static function upload($file, $uploadSettings)
{
$fullTempFolder = self::getFullTempFolder($uploadSettings['context'], $uploadSettings['field_id'], $uploadSettings['item_id']);
// Create source folder if not exists
File::createDirs($fullTempFolder);
// Move the image to the tmp folder
try {
$source = File::upload($file, $fullTempFolder, $uploadSettings['allowed_types'], $uploadSettings['allow_unsafe'], null, $uploadSettings['random_suffix']);
} catch (\Throwable $th)
{
return false;
}
if (!$source)
{
return false;
}
return str_replace(JPATH_ROOT . DIRECTORY_SEPARATOR, '', $source);
}
public static function maybeRegenerateImages($context = 'default', $items = [], $field_id = null, $item_id = null, $oldData = [])
{
if (!$oldData)
{
return false;
}
if (!$field_data = self::getSettings($context, $field_id, $item_id))
{
return false;
}
/**
* In order to proceed, there must be changes in the following data:
*
* - provider
* - full size image
* - slideshow image
* - thumbnails
* - watermark
*/
$images = self::canRegenerateImages($field_data, $oldData);
$items = is_string($items) ? json_decode($items, true) : $items;
foreach ($items as &$item)
{
self::generateFromSource($item, $images, $field_data, false);
}
return $items;
}
/**
* Checks if the images need to be regenerated.
*
* @param object $new_data
* @param object $old_data
*
* @return array
*/
public static function canRegenerateImages($new_data = [], $old_data = [])
{
$images = [
'thumb' => false,
'slideshow' => false,
'full' => false
];
// On provider change
if ($old_data->get('provider') != $new_data->get('provider'))
{
if ($new_data->get('provider') === 'slideshow')
{
$images['slideshow'] = true;
if ($new_data->get('show_thumbnails') === '1')
{
$images['thumb'] = true;
}
}
else
{
$images['thumb'] = true;
}
}
// On thumbnails dimensions change
else if ($old_data->get('thumbnail_size') != $new_data->get('thumbnail_size') || $old_data->get('justified_item_height') != $new_data->get('justified_item_height') || $old_data->get('show_thumbnails') != $new_data->get('show_thumbnails') || $old_data->get('masonry_thumbnails_width') != $new_data->get('masonry_thumbnails_width') || $old_data->get('slideshow_thumbnail_size') != $new_data->get('slideshow_thumbnail_size'))
{
$images['thumb'] = true;
}
// Check if full image has changed
if ($old_data->get('full_image') != $new_data->get('full_image') || ($old_data->get('lightbox') != $new_data->get('lightbox') && $new_data->get('lightbox') === '1'))
{
$images['full'] = true;
}
// Check if slideshow image has changed
if ($new_data->get('provider') === 'slideshow' && $old_data->get('slideshow_image') != $new_data->get('slideshow_image'))
{
$images['slideshow'] = true;
}
// Check if watermark settings have changed
if ($old_data->get('watermark') != $new_data->get('watermark'))
{
if ($new_data->get('lightbox') === '1')
{
$images['full'] = true;
}
if ($old_data->get('slideshow_image') != $new_data->get('slideshow_image') || $new_data->get('provider') === 'slideshow')
{
$images['slideshow'] = true;
}
if ($old_data->get('provider') !== 'slideshow' || ($new_data->get('provider') === 'slideshow' && $new_data->get('show_thumbnails') === '1'))
{
$images['thumb'] = true;
}
}
return $images;
}
/**
* Returns the settings for the given context.
*
* @param string $context
* @param int $field_id
* @param int $item_id
*
* @return mixed
*/
public static function getSettings($context = 'default', $field_id = null, $item_id = null)
{
// Make sure we have a valid context
if (!$context)
{
return false;
}
$field_data = [];
if ($context === 'default')
{
// Make sure we have a valid field id
if (!$field_id)
{
return Text::_('NR_GALLERY_MANAGER_FIELD_ID_ERROR');
}
if (!$field_data = \NRFramework\Helpers\CustomField::getData($field_id))
{
return Text::_('NR_GALLERY_MANAGER_INVALID_FIELD_DATA');
}
}
else if ($context === 'module')
{
// Make sure we have a valid item id
if (!$item_id)
{
return Text::_('NR_GALLERY_MANAGER_ITEM_ID_ERROR');
}
if (!$field_data = \NRFramework\Helpers\Module::getData($item_id))
{
return Text::_('NR_GALLERY_MANAGER_INVALID_FIELD_DATA');
}
}
return $field_data;
}
/**
* Moves all given temp items over to the destination folder.
*
* @param array $value
* @param object $field
* @param string $destination_folder
*
* @return void
*/
public static function moveTempItemsToDestination($value, $field, $destination_folder)
{
if (!$destination_folder)
{
return;
}
// Create destination folder if missing
if (!File::createDirs($destination_folder))
{
return;
}
// Make field params use Registry
if (!$field->fieldparams instanceof Registry)
{
$field->fieldparams = new Registry($field->fieldparams);
}
/**
* Prepare the items for backwards compatibility
*/
$items = is_string($value) ? json_decode($value, true) ?? [['value' => $value]] : $value;
$items = isset($items['items']) ? $items['items'] : $items;
if (isset($items['value']))
{
$items = [$items];
}
$limit_files = (int) $field->fieldparams->get('limit_files', 0);
// Handle single file
if ($limit_files === 1 && is_array($items))
{
$items = [reset($items)];
}
// Compatibility Start: Migrate old items to new folder structure
self::maybeMigrateOldItems($items, $field->fieldparams);
// Compatibility End
$ds = DIRECTORY_SEPARATOR;
$images = [
'thumb' => in_array($field->fieldparams->get('provider', 'grid'), ['grid', 'masonry', 'justified']) || ($field->fieldparams->get('provider', 'grid') === 'slideshow' && $field->fieldparams->get('show_thumbnails', '0') === '1'),
'slideshow' => $field->fieldparams->get('provider', 'grid') === 'slideshow',
'full' => $field->fieldparams->get('lightbox', '0') === '1'
];
$tmpdir = Factory::getConfig()->get('tmp_path');
$tmpRelativeDirectory = ltrim(str_replace(JPATH_ROOT, '', $tmpdir), $ds);
// Move all files from the temp folder over to the `upload folder`
foreach ($items as $key => &$item)
{
/**
* Skip invalid files.
*
* These "files" can appear when we try to move files
* over to the destination folder when the gallery manager
* is still working to upload queueed files.
*/
if ($key === 'ITEM_ID')
{
continue;
}
// Skip if source does not start with the temp relative directory
$testSourcePath = ltrim(rtrim($item['source'], $ds), $ds) . $ds;
if (!Functions::startsWith($testSourcePath, $tmpRelativeDirectory . $ds))
{
continue;
}
// Move source image to final directory
try {
$source_clean = pathinfo($item['source'], PATHINFO_BASENAME);
$source_path = implode($ds, [JPATH_ROOT, $item['source']]);
$new_source_path = implode($ds, [rtrim($destination_folder, $ds), md5('source'), $source_clean]);
$new_source_path = File::move($source_path, $new_source_path);
$item['source'] = ltrim(rtrim(str_replace(JPATH_ROOT, '', $new_source_path), $ds), $ds);
} catch (\Throwable $th) {}
// Generate the rest of images from the source image
self::generateFromSource($item, $images, $field->fieldparams);
}
return $items;
}
/**
* Check and migrate old items to the new folder structure.
*
* @param array $items
* @param object $field_data
*
* @return void
*/
public static function maybeMigrateOldItems(&$items = [], $field_data = [])
{
if (!is_array($items) || empty($items))
{
return;
}
$ds = DIRECTORY_SEPARATOR;
$tmpdir = Factory::getConfig()->get('tmp_path');
// Migrate images to new folders and create source images from the full image if source is missing
foreach ($items as &$value)
{
// We skip this process if the "source" directory exists in the uploaded files path.
// This means that the new structure is present, so don't do anything.
if (!empty($value['source']))
{
$fullSourcePath = implode($ds, [JPATH_ROOT, $value['source']]);
$checkDirectory = dirname($fullSourcePath);
if (is_dir($checkDirectory) && (Functions::startsWith($checkDirectory, $tmpdir) || Functions::endsWith($checkDirectory, '/' . md5('source'))))
{
continue;
}
}
$sourceNewPath = null;
$fullImageCurrentPath = implode($ds, [JPATH_ROOT, $value['image']]);
$fullImageData = pathinfo($fullImageCurrentPath);
// Create source if missing
if (empty($value['source']))
{
$sourceNewPath = implode($ds, [dirname($fullImageCurrentPath), md5('source'), $fullImageData['basename']]);
$sourceNewPath = File::copy($fullImageCurrentPath, $sourceNewPath);
$value['source'] = ltrim(rtrim(str_replace(JPATH_ROOT, '', $sourceNewPath), $ds), $ds);
}
else
{
// Move source image
$sourceOldPath = implode($ds, [JPATH_ROOT, $value['source']]);
$sourceNewPath = implode($ds, [JPATH_ROOT, dirname($value['source']), md5('source'), $fullImageData['basename']]);
$sourceNewPath = File::move($sourceOldPath, $sourceNewPath);
$value['source'] = ltrim(rtrim(str_replace(JPATH_ROOT, '', $sourceNewPath), $ds), $ds);
}
// Delete thumbnail and recreate from source
$needThumb = false;
$thumbOldPath = implode($ds, [JPATH_ROOT, $value['thumbnail']]);
if (is_file($thumbOldPath))
{
$needThumb = true;
JoomlaCMSFile::delete($thumbOldPath);
$value['thumbnail'] = '';
if ($field_data->get('provider', 'grid') === 'slideshow' && $field_data->get('show_thumbnails', '0') === '0')
{
$needThumb = false;
}
}
// Delete full image and recreate from source
$needFull = false;
$fullImageOldPath = implode($ds, [JPATH_ROOT, $value['image']]);
if (is_file($fullImageOldPath))
{
JoomlaCMSFile::delete($fullImageOldPath);
$value['image'] = '';
if ($field_data->get('lightbox', '0') === '1')
{
$needFull = true;
}
}
self::generateFromSource($value, [
'thumb' => $needThumb,
'slideshow' => $field_data->get('provider', 'grid') === 'slideshow',
'full' => $needFull
], $field_data);
}
}
/**
* Clean up unneeded images.
*
* @param array $item
* @param object $field_data
*/
public static function cleanUpImages(&$item = [], $field_data = [])
{
$ds = DIRECTORY_SEPARATOR;
$provider = $field_data->get('provider', 'grid');
/**
* Clean up of unneeded images.
*/
/**
* Delete thumbs if:
* 1) We're using the slideshow provider and show_thumbnails is disabled and the item has a thumbnail
* 2) We're not using the slideshow provider and the item has a thumbnail and a slideshow image
*/
if (($provider === 'slideshow' && $field_data->get('show_thumbnails', '0') === '0' && $item['thumbnail']) || ($provider !== 'slideshow' && $item['thumbnail'] && $item['slideshow']))
{
$thumbnails_path = implode($ds, [JPATH_ROOT, $item['thumbnail']]);
if (is_file($thumbnails_path))
{
JoomlaCMSFile::delete($thumbnails_path);
}
$item['thumbnail'] = null;
}
// Delete full image if lightbox is disabled and the item has a full image
if ($field_data->get('lightbox') === '0' && $item['image'])
{
$full_image_path = implode($ds, [JPATH_ROOT, $item['image']]);
if (is_file($full_image_path))
{
JoomlaCMSFile::delete($full_image_path);
}
$item['image'] = null;
}
// Delete slideshow image if exists and not in slideshow provider
if ($provider !== 'slideshow' && $item['slideshow'])
{
$slideshow_image_path = implode($ds, [JPATH_ROOT, $item['slideshow']]);
if (is_file($slideshow_image_path))
{
JoomlaCMSFile::delete($slideshow_image_path);
}
$item['slideshow'] = null;
}
}
/**
* This method generates the full image, thumbnail, and slideshow images from the sources.
*
* @param array $item
* @param array $images
* @param object $field_data
* @param bool $unique_filename
*
* @return void
*/
public static function generateFromSource(&$item, $images = [], $field_data = [], $unique_filename = true)
{
$ds = DIRECTORY_SEPARATOR;
$provider = $field_data->get('provider', 'grid');
/**
* Clean up of unneeded images.
*/
self::cleanUpImages($item, $field_data);
$full_source_path = implode($ds, [JPATH_ROOT, $item['source']]);
// Create full image
if ($images['full'])
{
$full_image_path = implode($ds, [JPATH_ROOT, dirname(dirname($item['source'])), 'full', basename($item['source'])]);
$full_image_resizing_dimensions = self::getImageResizingDimensions('full_image', $field_data);
if (!empty(array_filter($full_image_resizing_dimensions)))
{
// Create the full image directory if not exists
File::createDirs(dirname($full_image_path));
// Resize the full image
$full_image_path = Image::resizeByWidthOrHeight($full_source_path, $full_image_resizing_dimensions['width'], $full_image_resizing_dimensions['height'], 70, $full_image_path, 'crop', $unique_filename);
$item['image'] = ltrim(rtrim(str_replace(JPATH_ROOT, '', $full_image_path), $ds), $ds);
}
else if ($field_data->get('full_image.by', '') === 'disabled')
{
// Create the full image directory if not exists
File::createDirs(dirname($full_image_path));
// Copy image to full folder
$full_image_path = File::copy($full_source_path, $full_image_path, !$unique_filename);
$item['image'] = ltrim(rtrim(str_replace(JPATH_ROOT, '', $full_image_path), $ds), $ds);
}
}
// Create thumbnail image
if ($images['thumb'])
{
$thumbs_folder_name = $provider === 'slideshow' ? 'thumb' : '';
$thumbnail_image_path = implode($ds, array_filter([JPATH_ROOT, dirname(dirname($item['source'])), $thumbs_folder_name, basename($item['source'])]));
$thumbnail_resizing_dimensions = [
'width' => null,
'height' => null
];
if ($provider === 'justified')
{
$thumbnail_resizing_dimensions['height'] = $field_data->get('justified_item_height', 200);
}
else if ($provider === 'masonry')
{
$thumbnail_resizing_dimensions['width'] = $field_data->get('masonry_thumbnails_width', 200);
}
else
{
$thumbnail_size = $provider === 'slideshow' ? $field_data->get('slideshow_thumbnail_size', 200) : $field_data->get('thumbnail_size', 200);
$thumbnail_resizing_dimensions = [
'width' => $thumbnail_size,
'height' => $thumbnail_size
];
// If slideshow, require show_thumbnails to be enabled
if ($provider === 'slideshow' && $field_data->get('show_thumbnails', '0') === '0')
{
$thumbnail_resizing_dimensions = null;
}
}
if ($thumbnail_resizing_dimensions)
{
// Create the full image directory if not exists
File::createDirs(dirname($thumbnail_image_path));
// Resize the full image
$thumbnail_image_path = Image::resizeByWidthOrHeight($full_source_path, $thumbnail_resizing_dimensions['width'], $thumbnail_resizing_dimensions['height'], 70, $thumbnail_image_path, 'crop', $unique_filename);
$item['thumbnail'] = ltrim(rtrim(str_replace(JPATH_ROOT, '', $thumbnail_image_path), $ds), $ds);
}
}
// Create slideshow image
if ($images['slideshow'] && $provider === 'slideshow')
{
$slideshow_image_path = implode($ds, [JPATH_ROOT, dirname(dirname($item['source'])), basename($item['source'])]);
if ($slideshow_resizing_dimensions = self::getImageResizingDimensions('slideshow_image', $field_data))
{
// Create the full image directory if not exists
File::createDirs(dirname($slideshow_image_path));
// Resize the full image
$slideshow_image_path = Image::resizeByWidthOrHeight($full_source_path, $slideshow_resizing_dimensions['width'], $slideshow_resizing_dimensions['height'], 70, $slideshow_image_path, 'crop', $unique_filename);
$item['slideshow'] = ltrim(rtrim(str_replace(JPATH_ROOT, '', $slideshow_image_path), $ds), $ds);
}
}
self::applyWatermarkOnImages($item, $images, $field_data);
}
/**
* Applies the watermark on the images.
*
* @param array $payload
* @param array $images
* @param object $field_data
*
* @return void
*/
public static function applyWatermarkOnImages($payload = [], $images = [], $field_data = [])
{
if ($field_data->get('watermark.type', 'disabled') === 'disabled')
{
return;
}
$ds = DIRECTORY_SEPARATOR;
$watermarkSettings = (array) $field_data->get('watermark', []);
$apply_on_thumbnails = $watermarkSettings['apply_on_thumbnails'] === '1';
$watermarkSettings = array_merge($watermarkSettings, [
'image' => !empty($watermarkSettings['image']) ? explode('#', JPATH_SITE . DIRECTORY_SEPARATOR . $watermarkSettings['image'])[0] : null,
]);
if ($images['full'])
{
// Add watermark to full image
$watermarkPayload = array_merge($watermarkSettings, [
'source' => implode($ds, [JPATH_ROOT, $payload['image']])
]);
\NRFramework\Image::applyWatermark($watermarkPayload);
}
if ($images['slideshow'])
{
// Add watermark to slideshow image
if ($field_data->get('provider', 'grid') === 'slideshow')
{
$watermarkPayload = array_merge($watermarkSettings, [
'source' => implode($ds, [JPATH_ROOT, $payload['slideshow']])
]);
\NRFramework\Image::applyWatermark($watermarkPayload);
}
}
if ($apply_on_thumbnails && $images['thumb'])
{
// Add watermark to thumbnail
$watermarkPayload = array_merge($watermarkSettings, [
'source' => implode($ds, [JPATH_ROOT, $payload['thumbnail']])
]);
\NRFramework\Image::applyWatermark($watermarkPayload);
}
}
public static function getImageResizingDimensions($key, $field_data)
{
$width = $height = null;
if (in_array($field_data->get($key . '.by', ''), ['width', 'custom']))
{
$width = $field_data->get($key . '.width', null);
}
if (in_array($field_data->get($key . '.by', ''), ['height', 'custom']))
{
$height = $field_data->get($key . '.height', null);
}
$data = array_filter([
'width' => $width,
'height' => $height
]);
if (!isset($data['width']))
{
$data['width'] = null;
}
if (!isset($data['height']))
{
$data['height'] = null;
}
return $data;
}
/**
* Saves the tags for each item.
*
* @param array $value
*
* @return array
*/
public static function saveItemTags($value = [])
{
if (!is_array($value))
{
return $value;
}
foreach ($value as &$item)
{
if (!isset($item['tags']) || !is_string($item['tags']))
{
$item['tags'] = [];
continue;
}
if (!$itemTags = json_decode($item['tags'], true))
{
$item['tags'] = [];
continue;
}
if (!is_array($itemTags))
{
$item['tags'] = [];
continue;
}
if (!$itemTags)
{
$item['tags'] = [];
continue;
}
// Make $itemTags an array of strings
$itemTags = array_map(function($tag) {
return (string) $tag;
}, $itemTags);
/**
* Creates the new tags in the #__tags table.
*
* This returns an array of the new tag ids. If a tag isn't new (doesn't have #new# prefix), it will return 0 as its id.
*
* We will now store the IDs returned as the tags for the item.
*/
$item['tags'] = self::createTagsFromField($itemTags);
}
return $value;
}
/**
* Create any new tags by looking for #new# in the strings
*
* @param array $tags Tags text array from the field
*
* @return mixed If successful, metadata with new tag titles replaced by tag ids. Otherwise false.
*
* @since 3.1
*/
public static function createTagsFromField($tags)
{
if (empty($tags) || $tags[0] == '')
{
return;
}
// We will use the tags table to store them
if (defined('nrJ4'))
{
$tagTable = Factory::getApplication()->bootComponent('com_tags')->getMVCFactory()->createTable('Tag', 'Administrator');
}
else
{
Table::addIncludePath(JPATH_ADMINISTRATOR . '/components/com_tags/tables');
$tagTable = Table::getInstance('Tag', 'TagsTable');
}
$newTags = [];
foreach ($tags as $key => $tag)
{
// Remove the #new# prefix that identifies new tags
$tagText = str_replace('#new#', '', $tag);
if ($tagText === $tag)
{
$newTags[] = (int) $tag;
}
else
{
// Clear old data if exist
$tagTable->reset();
// Try to load the selected tag
if ($tagTable->load(['title' => $tagText]))
{
$newTags[] = (int) $tagTable->id;
}
else
{
// Prepare tag data
$tagTable->id = 0;
$tagTable->title = $tagText;
$tagTable->published = 1;
$tagTable->description = '';
$tagTable->language = '*';
$tagTable->access = 1;
// Make this item a child of the root tag
$tagTable->setLocation($tagTable->getRootId(), 'last-child');
// Try to store tag
if ($tagTable->check())
{
// Assign the alias as path (autogenerated tags have always level 1)
$tagTable->path = $tagTable->alias;
if ($tagTable->store())
{
$newTags[] = (int) $tagTable->id;
}
}
}
}
}
// At this point $newTags is an array of all tag ids
return $newTags;
}
/**
* Sets the custom field item id > field id value "source" to given source image path for the original image path
*/
public static function setItemFieldSource($item_id, $field_id, $sourceImagePath, $originalImagePath)
{
// Get "value" column from #__fields_values where item_id = $item_id and $field_id = $field_id
$db = Factory::getDbo();
$query = $db->getQuery(true)
->select($db->qn('value'))
->from($db->qn('#__fields_values'))
->where($db->qn('item_id') . ' = ' . $db->q($item_id))
->where($db->qn('field_id') . ' = ' . $db->q($field_id));
$db->setQuery($query);
$value = $db->loadResult();
// If value is empty, return
if (!$value)
{
return;
}
// Decode value
$value = json_decode($value, true);
// If value is empty, return
if (!$value)
{
return;
}
// If value is not an array, return
if (!is_array($value))
{
return;
}
// If value has no items, return
if (!isset($value['items']))
{
return;
}
foreach ($value['items'] as $key => &$item)
{
if ($item['image'] !== $originalImagePath)
{
continue;
}
$item['source'] = $sourceImagePath;
}
// Update value
$query = $db->getQuery(true)
->update($db->qn('#__fields_values'))
->set($db->qn('value') . ' = ' . $db->q(json_encode($value)))
->where($db->qn('item_id') . ' = ' . $db->q($item_id))
->where($db->qn('field_id') . ' = ' . $db->q($field_id));
$db->setQuery($query);
$db->execute();
}
/**
* Media Uploader files look like: https://example.com/images/sampledata/parks/banner_cradle.png
* We remove the first part (https://example.com/images/) and keep the other part (relative path to image).
*
* @param string $filename
*
* @return string
*/
private static function getFilePathFromMediaUploaderFile($filename)
{
$filenameArray = explode('images/', $filename, 2);
unset($filenameArray[0]);
$new_filepath = join($filenameArray);
return 'images/' . $new_filepath;
}
/**
* Deletes an uploaded files: source, slideshow, original, and thumbnail.
*
* @param string $source The source image path.
* @param string $slideshow The slideshow image path.
* @param string $original The original image path.
* @param string $thumbnail The thumbnail image path.
*
* @return bool
*/
public static function deleteFile($source = null, $slideshow = null, $original = null, $thumbnail = null)
{
return [
'deleted_source_image' => self::findAndDeleteFile($source),
'deleted_slideshow_image' => self::findAndDeleteFile($slideshow),
'deleted_full_image' => self::findAndDeleteFile($original),
'deleted_thumbnail' => self::findAndDeleteFile($thumbnail)
];
}
/**
* Deletes the file.
*
* @param string $filepath
*
* @return mixed
*/
private static function findAndDeleteFile($filepath)
{
if (!$filepath)
{
return;
}
$file = Path::clean(implode(DIRECTORY_SEPARATOR, [JPATH_ROOT, $filepath]));
return is_file($file) ? JoomlaCMSFile::delete($file) : false;
}
/**
* Cleans the temp folder.
*
* Removes any image that is 1 day or older.
*
* @return void
*/
public static function clean()
{
$temp_folder = self::getFullTempFolder();
if (!is_dir($temp_folder))
{
return;
}
// Get images
$files = array_diff(scandir($temp_folder), ['.', '..', '.DS_Store', 'index.html']);
$found = [];
foreach ($files as $key => $filename)
{
$file_path = implode(DIRECTORY_SEPARATOR, [$temp_folder, $filename]);
// Skip directories
if (is_dir($file_path))
{
continue;
}
$diff_in_miliseconds = time() - filemtime($file_path);
// Skip the file if it's not old enough
if ($diff_in_miliseconds < (60 * 60 * 24 * self::$temp_files_cleanup_days))
{
continue;
}
$found[] = $file_path;
}
if (!$found)
{
return;
}
// Delete found old files
foreach ($found as $file)
{
unlink($file);
}
}
/**
* Full temp directory where images are uploaded
* prior to them being saved in the final directory.
*
* @param string $context
* @param string $field_id
* @param string $item_id
*
* @return string
*/
public static function getFullTempFolder($context = 'default', $field_id = '', $item_id = '')
{
$tmpdir = Factory::getConfig()->get('tmp_path');
$paths = [
$tmpdir,
'tassos',
\NRFramework\VisitorToken::getInstance()->get(),
$context === 'module' ? 'smilepack' : 'acf',
'gallery',
$item_id,
$field_id
];
$paths = array_filter($paths);
return implode(DIRECTORY_SEPARATOR, $paths);
}
/**
* Deletes a specific tag from every gallery item.
*
* @param int $tag_id
* @param string $context
*
* @return void
*/
public static function deleteTagFromFieldsValues($tag_id = null, $context = '')
{
if (!$tag_id)
{
return;
}
if ($context === '')
{
self::deleteTagFromCustomFieldsByTagId($tag_id);
self::deleteTagFromSubformCustomFieldsByTagId($tag_id);
}
}
/**
* Deletes a specific tag from every gallery item custom field.
*
* @param int $tag_id
*
* @return void
*/
private static function deleteTagFromCustomFieldsByTagId($tag_id = null)
{
if (!$tag_id)
{
return;
}
$db = Factory::getDbo();
$query = $db->getQuery(true)
->select('f.id as field_id, fv.item_id as item_id, fv.value as value')
->from('#__fields as f')
->join('LEFT', '#__fields_values AS fv ON fv.field_id = f.id')
->where('f.type = ' . $db->quote('acfgallery'));
$db->setQuery($query);
$fields = $db->loadAssocList();
if (!$fields)
{
return;
}
foreach ($fields as $field)
{
if (!$decoded_value = json_decode($field['value'], true))
{
continue;
}
if (!isset($decoded_value['items']))
{
continue;
}
$update = false;
foreach ($decoded_value['items'] as &$item)
{
if (!isset($item['tags']))
{
continue;
}
if (!is_array($item['tags']))
{
continue;
}
if (!count($item['tags']))
{
continue;
}
$item['tags'] = array_values($item['tags']);
if (($key = array_search($tag_id, $item['tags'])) !== false)
{
$update = true;
unset($item['tags'][$key]);
}
$item['tags'] = array_values($item['tags']);
}
if (!$update)
{
continue;
}
$field['value'] = json_encode($decoded_value);
// Update field value
$query->clear()
->update('#__fields_values')
->set($db->quoteName('value') . ' = ' . $db->quote($field['value']))
->where($db->quoteName('field_id') . ' = ' . $db->quote($field['field_id']))
->where($db->quoteName('item_id') . ' = ' . $db->quote($field['item_id']));
$db->setQuery($query);
$db->execute();
}
}
/**
* Deletes a specific tag from every gallery item that exists in a subform custom field.
*
* @param int $tag_id
*
* @return void
*/
private static function deleteTagFromSubformCustomFieldsByTagId($tag_id = null)
{
if (!$tag_id)
{
return;
}
if (!$tag_id)
{
return;
}
$db = Factory::getDbo();
// Get all ACF Gallery custom field IDs
$query = $db->getQuery(true)
->select('distinct f.id')
->from('#__fields as f')
->join('LEFT', '#__fields_values AS fv ON fv.field_id = f.id')
->where('f.type = ' . $db->quote('acfgallery'));
$db->setQuery($query);
$gallery_field_ids = array_keys($db->loadAssocList('id'));
if (!$gallery_field_ids)
{
return;
}
// Get all Subform custom fields
$query->clear()
->select('f.id as field_id, fv.item_id as item_id, fv.value as value')
->from('#__fields as f')
->join('LEFT', '#__fields_values AS fv ON fv.field_id = f.id')
->where('f.type = ' . $db->quote('subform'));
$db->setQuery($query);
$subform_fields = $db->loadAssocList();
foreach ($subform_fields as $subform_field)
{
if (!$subform_field_items = json_decode($subform_field['value'], true))
{
continue;
}
$update = false;
foreach ($subform_field_items as $row => &$row_items)
{
if (!is_array($row_items))
{
continue;
}
foreach ($row_items as $field_name => &$field_value)
{
// Get the field id
$field_id = str_replace('field', '', $field_name);
// Check if its a gallery field
if (!in_array($field_id, $gallery_field_ids))
{
continue;
}
if (!isset($field_value['items']))
{
continue;
}
foreach ($field_value['items'] as &$item)
{
if (!isset($item['tags']))
{
continue;
}
if (!is_array($item['tags']))
{
continue;
}
if (!count($item['tags']))
{
continue;
}
$item['tags'] = array_values($item['tags']);
if (($key = array_search($tag_id, $item['tags'])) !== false)
{
$update = true;
unset($item['tags'][$key]);
}
$item['tags'] = array_values($item['tags']);
}
}
}
if (!$update)
{
continue;
}
$subform_field['value'] = json_encode($subform_field_items);
// Update subform field value
$query->clear()
->update('#__fields_values')
->set($db->quoteName('value') . ' = ' . $db->quote($subform_field['value']))
->where($db->quoteName('field_id') . ' = ' . $db->quote($subform_field['field_id']))
->where($db->quoteName('item_id') . ' = ' . $db->quote($subform_field['item_id']));
$db->setQuery($query);
$db->execute();
}
}
} Widgets/MapAddress.php 0000644 00000002056 15241767312 0010721 0 ustar 00 <?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\Helpers\Widgets;
defined('_JEXEC') or die;
use Joomla\CMS\Language\Text;
class MapAddress
{
/**
* Returns the default address details layout.
*
* @param array $address
* @param array $showAddressDetails
*
* @return string
*/
public static function getDefaultAddressDetailsLayout($address = [], $showAddressDetails = [])
{
if (empty($address) || empty($showAddressDetails))
{
return;
}
$html = '';
$template = '<div class="nrf-mapaddress-field-address-detail-item"><strong>%s</strong>: %s</div>';
foreach ($showAddressDetails as $key)
{
$value = isset($address[$key]) ? $address[$key] : '';
if (empty($value))
{
continue;
}
$html .= sprintf($template, Text::_('NR_' . strtoupper($key)), $value);
}
return $html;
}
} CSS.php 0000644 00000001504 15241767317 0005722 0 ustar 00 <?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\Helpers;
defined('_JEXEC') or die;
class CSS
{
/**
* Transforms an array of CSS variables (key, value) to
* a CSS output.
*
* @param array $cssVars
* @param string $namespace
*
* @return string
*/
public static function cssVarsToString($cssVars, $namespace)
{
$output = '';
foreach (array_filter($cssVars) as $key => $value)
{
$output .= '--' . $key . ': ' . $value . ';' . "\n";
}
return $namespace . ' {
' . $output . '
}
';
}
} ChainedFields.php 0000644 00000006124 15241767320 0007751 0 ustar 00 <?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\Helpers;
defined('_JEXEC') or die;
class ChainedFields
{
/**
* Loads a combined array of the inputs and choices of the CSV file.
*
* @param string $path
* @param string $data_source
* @param string $separator
* @param string $id_prefix
* @param string $name_prefix
*
* @return array
*/
public static function loadCSV($input, $data_source = 'custom', $separator = ',', $id_prefix = '', $name_prefix = '')
{
if (!$separator)
{
return [];
}
if ($data_source === 'csv_file')
{
if (!file_exists($input))
{
return [];
}
if (!$input = file_get_contents($input))
{
return [];
}
}
if (!$data = self::getData($input, $separator, $id_prefix, $name_prefix))
{
return [];
}
return $data;
}
/**
* Iterates over the given data and returns the inputs and choices.
*
* @param string $data
* @param string $separator
* @param string $id_prefix
* @param string $name_prefix
*
* @return array
*/
public static function getData($data = '', $separator = ',', $id_prefix = '', $name_prefix = '')
{
if (!$data || !is_string($data))
{
return;
}
if (!$rows = explode(PHP_EOL, $data))
{
return;
}
$choices = [];
$inputs = [];
foreach ($rows as $row)
{
$row = explode($separator, $row);
$row = array_filter($row, 'strlen');
// if an empty row was found, skip it
if (empty($row))
{
continue;
}
if (empty($inputs))
{
$i = 1;
foreach ($row as $index => $item)
{
if ($i % 10 == 0)
{
$i++;
}
$inputs[] = [
'id' => $id_prefix . $i,
'name' => $name_prefix . '[' . $i . ']',
'label' => trim($item),
];
$i++;
}
continue;
}
$parent = null;
foreach($row as $item)
{
$item = trim($item);
if ($parent === null)
{
$parent = &$choices;
}
if (!isset($parent[$item]))
{
$item = trim($item);
$parent[$item] = [
'text' => $item,
'value' => $item,
'isSelected' => false,
'choices' => []
];
}
$parent = &$parent[$item]['choices'];
}
}
self::array_values_recursive($choices);
if (!isset($inputs) || !isset($choices))
{
return;
}
return compact('inputs', 'choices');
}
/**
* Transforms an array to using as key an index value instead of a alphanumeric.
*
* @param array $choices
* @param string $property
*
* @return array
*/
public static function array_values_recursive(&$choices, $property = 'choices')
{
$choices = array_values($choices);
for($i = 0; $i <= count($choices); $i++)
{
if(empty($choices[$i][$property]))
{
continue;
}
$choices[$i][$property] = self::array_values_recursive($choices[$i][$property], $property);
}
return $choices;
}
} CustomField.php 0000644 00000002113 15241767321 0007500 0 ustar 00 <?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\Helpers;
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use \Joomla\Registry\Registry;
class CustomField
{
/**
* Get a custom field's data.
*
* @param integer $value
* @param string $selector
*
* @return object
*/
public static function getData($value, $selector = 'id')
{
if (!$value)
{
return;
}
$db = Factory::getDbo();
$query = $db->getQuery(true);
$query
->select($db->quoteName(['fieldparams']))
->from($db->quoteName('#__fields'))
->where($db->quoteName($selector) . ' = ' . $db->quote($value))
->where($db->quoteName('state') . ' = 1');
$db->setQuery($query);
if (!$result = $db->loadResult())
{
return;
}
return new Registry($result);
}
} File.php 0000644 00000002622 15241767321 0006146 0 ustar 00 <?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\Helpers;
defined('_JEXEC') or die;
use Joomla\CMS\Uri\Uri;
class File
{
public static function getFileSources($sources, $allowedExtensions = null)
{
if (!$sources)
{
return;
}
// Support comma separated values
$sources = is_array($sources) ? $sources : explode(',', $sources);
$result = [];
$ds = DIRECTORY_SEPARATOR;
foreach ($sources as $source)
{
if (!$pathinfo = pathinfo($source))
{
continue;
}
if (!isset($pathinfo['extension']))
{
continue;
}
if ($allowedExtensions && !in_array($pathinfo['extension'], $allowedExtensions))
{
continue;
}
// Add root path to local source
if (strpos($source, 'http') === false)
{
$source = Uri::root() . ltrim($source, '/');
}
$result[] = [
'ext' => $pathinfo['extension'],
'file' => $source
];
}
return $result;
}
} Geo.php 0000644 00000001516 15241767321 0006002 0 ustar 00 <?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\Helpers;
defined('_JEXEC') or die;
class Geo
{
/**
* Detect and return the visitor's country.
*
* @return string The visitor's country code (GR)
*/
public static function getVisitorCountryCode()
{
$path = JPATH_PLUGINS . '/system/tgeoip/';
if (!is_dir($path))
{
return '';
}
if (!class_exists('TGeoIP'))
{
@include_once $path . 'vendor/autoload.php';
@include_once $path . 'helper/tgeoip.php';
}
$geo = new \TGeoIP();
return $geo->getCountryCode();
}
} License.php 0000644 00000001614 15241767323 0006653 0 ustar 00 <?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\Helpers;
defined('_JEXEC') or die;
use Joomla\CMS\Http\HttpFactory;
class License
{
/**
* Returns the remote license data from the server for the given download key.
*
* @return array
*/
public static function getRemoteLicenseData($download_key = null)
{
if (!$download_key)
{
return;
}
// License Check Endpoint
$url = TF_CHECK_LICENSE;
// Set Download Key
$url = str_replace('{{DOWNLOAD_KEY}}', $download_key, $url);
$response = HttpFactory::getHttp()->get($url);
// No response, abort
if (!$response = $response->body)
{
return;
}
return json_decode($response, true);
}
} Module.php 0000644 00000002055 15241767325 0006520 0 ustar 00 <?php
/**
* @author Tassos Marinos <info@tassos.gr>
* @link https://www.tassos.gr
* @copyright Copyright © 2023 Tassos All Rights Reserved
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
*/
namespace NRFramework\Helpers;
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
class Module
{
/**
* Get a module data.
*
* @param integer $value
* @param string $selector
*
* @return object
*/
public static function getData($value, $selector = 'id')
{
if (!$value)
{
return;
}
$db = Factory::getDbo();
$query = $db->getQuery(true);
$query
->select($db->quoteName(['params']))
->from($db->quoteName('#__modules'))
->where($db->quoteName($selector) . ' = ' . $db->quote($value))
->where($db->quoteName('access') . ' = 1');
$db->setQuery($query);
if (!$result = $db->loadResult())
{
return;
}
return new \Joomla\Registry\Registry($result);
}
} Number.php 0000644 00000002746 15241767327 0006534 0 ustar 00 <?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\Helpers;
defined('_JEXEC') or die;
class Number
{
/**
* Converts a number into a short version, eg: 1000 -> 1k
*
* @param Number $n The number to create the shorter version
* @param integer $precision
*
* @return string The shorter version of the given number
*/
public static function toShortFormat($n, $precision = 1)
{
if ($n < 900)
{
// 0 - 900
$n_format = number_format($n, $precision);
$suffix = '';
} else if ($n < 900000)
{
// 0.9k-850k
$n_format = number_format($n / 1000, $precision);
$suffix = 'K';
} else if ($n < 900000000)
{
// 0.9m-850m
$n_format = number_format($n / 1000000, $precision);
$suffix = 'M';
} else if ($n < 900000000000)
{
// 0.9b-850b
$n_format = number_format($n / 1000000000, $precision);
$suffix = 'B';
} else
{
// 0.9t+
$n_format = number_format($n / 1000000000000, $precision);
$suffix = 'T';
}
// Remove unecessary zeroes after decimal. "1.0" -> "1"; "1.00" -> "1"
// Intentionally does not affect partials, eg "1.50" -> "1.50"
if ($precision > 0)
{
$dotzero = '.' . str_repeat( '0', $precision );
$n_format = str_replace( $dotzero, '', $n_format );
}
return $n_format . $suffix;
}
} Responsive.php 0000644 00000017262 15241767331 0007433 0 ustar 00 <?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\Helpers;
defined('_JEXEC') or die;
use NRFramework\Cache;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Plugin\PluginHelper;
class Responsive
{
/**
* Renders the given CSS.
*
* @param array $css
* @param string $selector
*
* @return string
*/
public static function renderResponsiveCSS($css, $selector = '')
{
if (!$css || !is_array($css))
{
return;
}
$output = '';
foreach (self::getBreakpoints() as $breakpoint => $breakpoint_data)
{
if (!isset($css[$breakpoint]) || empty($css[$breakpoint]))
{
continue;
}
/**
* If we were given an array of strings of CSS, transform them to a string so we can output it.
*
* i.e. transform
* [
* 'color: #fff;',
* 'background: #000;'
* ]
*
* to:
*
* 'color: #fff;background: #000;'
*/
if (!is_string($css[$breakpoint]))
{
$css[$breakpoint] = implode('', $css[$breakpoint]);
}
$max_width = isset($breakpoint_data['max_width']) ? $breakpoint_data['max_width'] : '';
$output .= self::getGenericTemplate($css[$breakpoint], $max_width, $selector);
}
return $output;
}
/**
* Returns the responsive output of a specific media query size.
*
* @param string $css The Custom CSS
* @param int $size This is the max-width in pixels
* @param string $selector The CSS Selector to apply the CSS
*
* @return string
*/
public static function getGenericTemplate($css, $size = '', $selector = '')
{
if (!is_string($css) || !is_scalar($size) || !is_string($selector))
{
return '';
}
$selector_prefix = $selector_suffix = $size_prefix = $size_suffix = '';
if (!empty($size))
{
$size_prefix = '@media screen and (max-width: ' . $size . 'px){';
$size_suffix = '}';
}
if (!empty($selector))
{
$selector_prefix = $selector . '{';
$selector_suffix = '}';
}
return $size_prefix . $selector_prefix . $css . $selector_suffix . $size_suffix;
}
/**
* Returns all breakpoints.
*
* @return array
*/
public static function getBreakpoints()
{
$breakpointsSettings = self::getBreakpointsSettings();
$tablet_max_width = isset($breakpointsSettings['tablet']) && !empty($breakpointsSettings['tablet']) ? $breakpointsSettings['tablet'] : 1024;
$mobile_max_width = isset($breakpointsSettings['mobile']) && !empty($breakpointsSettings['mobile']) ? $breakpointsSettings['mobile'] : 575;
return [
'desktop' => [
'icon' => '<svg width="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><mask id="mask0_112_458" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="24" height="24"><rect width="24" height="24" fill="#D9D9D9"/></mask><g mask="url(#mask0_112_458)"><path d="M8.5 20.5V19H10.5V17H4.3077C3.80257 17 3.375 16.825 3.025 16.475C2.675 16.125 2.5 15.6974 2.5 15.1923V5.3077C2.5 4.80257 2.675 4.375 3.025 4.025C3.375 3.675 3.80257 3.5 4.3077 3.5H19.6923C20.1974 3.5 20.625 3.675 20.975 4.025C21.325 4.375 21.5 4.80257 21.5 5.3077V15.1923C21.5 15.6974 21.325 16.125 20.975 16.475C20.625 16.825 20.1974 17 19.6923 17H13.5V19H15.5V20.5H8.5ZM4.3077 15.5H19.6923C19.7692 15.5 19.8397 15.468 19.9038 15.4039C19.9679 15.3398 20 15.2692 20 15.1923V5.3077C20 5.23077 19.9679 5.16024 19.9038 5.09613C19.8397 5.03203 19.7692 4.99998 19.6923 4.99998H4.3077C4.23077 4.99998 4.16024 5.03203 4.09613 5.09613C4.03202 5.16024 3.99998 5.23077 3.99998 5.3077V15.1923C3.99998 15.2692 4.03202 15.3398 4.09613 15.4039C4.16024 15.468 4.23077 15.5 4.3077 15.5Z" fill="currentColor"/></g></svg>',
'label' => Text::_('NR_DESKTOP'),
'desc' => Text::_('NR_DESKTOPS_WITH_BREAKPOINT_INFO')
],
'tablet' => [
'icon' => '<svg width="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><mask id="mask0_112_446" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="24" height="24"><rect width="24" height="24" fill="#D9D9D9"/></mask><g mask="url(#mask0_112_446)"><path d="M12 20.2692C12.2448 20.2692 12.4535 20.183 12.6259 20.0105C12.7984 19.8381 12.8846 19.6294 12.8846 19.3846C12.8846 19.1397 12.7984 18.9311 12.6259 18.7586C12.4535 18.5862 12.2448 18.5 12 18.5C11.7551 18.5 11.5465 18.5862 11.374 18.7586C11.2016 18.9311 11.1154 19.1397 11.1154 19.3846C11.1154 19.6294 11.2016 19.8381 11.374 20.0105C11.5465 20.183 11.7551 20.2692 12 20.2692ZM5.3077 22.5C4.80898 22.5 4.38302 22.3233 4.02982 21.9701C3.67661 21.6169 3.5 21.191 3.5 20.6923V3.3077C3.5 2.80898 3.67661 2.38302 4.02982 2.02982C4.38302 1.67661 4.80898 1.5 5.3077 1.5H18.6923C19.191 1.5 19.6169 1.67661 19.9701 2.02982C20.3233 2.38302 20.5 2.80898 20.5 3.3077V20.6923C20.5 21.191 20.3233 21.6169 19.9701 21.9701C19.6169 22.3234 19.191 22.5 18.6923 22.5L5.3077 22.5ZM4.99997 17.7692V20.6923C4.99997 20.782 5.02883 20.8557 5.08653 20.9134C5.14423 20.9711 5.21795 21 5.3077 21H18.6923C18.782 21 18.8557 20.9711 18.9134 20.9134C18.9711 20.8557 19 20.782 19 20.6923V17.7692H4.99997ZM4.99997 16.2692H19V5.74995H4.99997V16.2692ZM4.99997 4.25H19V3.3077C19 3.21795 18.9711 3.14423 18.9134 3.08652C18.8557 3.02882 18.782 2.99998 18.6923 2.99998H5.3077C5.21795 2.99998 5.14423 3.02882 5.08653 3.08652C5.02883 3.14423 4.99997 3.21795 4.99997 3.3077V4.25Z" fill="currentColor"/></g></svg>',
'label' => Text::_('NR_TABLET'),
'desc' => Text::sprintf('NR_TABLETS_WITH_BREAKPOINT_INFO', $tablet_max_width),
'max_width' => $tablet_max_width
],
'mobile' => [
'icon' => '<svg width="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><mask id="mask0_112_452" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="24" height="24"><rect width="24" height="24" fill="#D9D9D9"/></mask><g mask="url(#mask0_112_452)"><path d="M7.3077 22.4999C6.80257 22.4999 6.375 22.3249 6.025 21.9749C5.675 21.6249 5.5 21.1974 5.5 20.6923V3.3077C5.5 2.80257 5.675 2.375 6.025 2.025C6.375 1.675 6.80257 1.5 7.3077 1.5H16.6922C17.1974 1.5 17.625 1.675 17.975 2.025C18.325 2.375 18.5 2.80257 18.5 3.3077V20.6923C18.5 21.1974 18.325 21.625 17.975 21.975C17.625 22.325 17.1974 22.5 16.6922 22.5L7.3077 22.4999ZM6.99997 19.75V20.6923C6.99997 20.7692 7.03202 20.8397 7.09613 20.9039C7.16024 20.968 7.23077 21 7.3077 21H16.6922C16.7692 21 16.8397 20.968 16.9038 20.9039C16.9679 20.8397 17 20.7692 17 20.6923V19.75H6.99997ZM6.99997 18.25H17V5.74998H6.99997V18.25ZM6.99997 4.25003H17V3.30773C17 3.23079 16.9679 3.16027 16.9038 3.09615C16.8397 3.03205 16.7692 3 16.6922 3H7.3077C7.23077 3 7.16024 3.03205 7.09613 3.09615C7.03202 3.16027 6.99997 3.23079 6.99997 3.30773V4.25003Z" fill="currentColor"/></g></svg>',
'label' => Text::_('NR_MOBILE'),
'desc' => Text::sprintf('NR_MOBILES_WITH_BREAKPOINT_INFO', $mobile_max_width),
'max_width' => $mobile_max_width
]
];
}
public static function getBreakpointsSettings()
{
$hash = 'tassosResponsiveBreakpoints';
if (Cache::has($hash))
{
return Cache::get($hash);
}
$settings = PluginHelper::getPlugin('system', 'nrframework');
$default = [
'desktop' => 'any',
'tablet' => 1024,
'mobile' => 575
];
if (!isset($settings->params))
{
return $default;
}
if (!$params = json_decode($settings->params, true))
{
return $default;
}
$data = isset($params['breakpoints']) ? $params['breakpoints'] : [];
return Cache::set($hash, $data);
}
} Template.php 0000644 00000002152 15241767333 0007043 0 ustar 00 <?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\Helpers;
defined('_JEXEC') or die;
use NRFramework\Cache;
use Joomla\CMS\Factory;
class Template
{
/**
* Returns the current template name.
*
* @return string
*/
public static function getTemplateName()
{
$hash = 'TFGetTemplateName';
if (Cache::has($hash))
{
return Cache::get($hash);
}
$template = null;
if (Factory::getApplication()->isClient('site'))
{
$template = Factory::getApplication()->getTemplate();
}
else
{
$db = Factory::getDbo();
$query = $db->getQuery(true)
->select($db->quoteName('template'))
->from($db->quoteName('#__template_styles'))
->where($db->quoteName('client_id') . ' = 0')
->where($db->quoteName('home') . ' = 1');
$db->setQuery($query);
$template = $db->loadResult();
}
return Cache::set($hash, $template);
}
} Video.php 0000644 00000003756 15241767335 0006353 0 ustar 00 <?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\Helpers;
defined('_JEXEC') or die;
class Video
{
/**
* Returns the Video URL details.
*
* Supported platforms:
* - YouTube
* - Vimeo
*
* @param string $url
*
* @return array
*/
public static function getDetails($url)
{
$id = '';
$provider = '';
if (preg_match(self::getYouTubePattern(), $url, $matches))
{
$id = !empty($matches[1]) ? $matches[1] : $matches[2];
$provider = 'youtube';
}
else if (preg_match(self::getVimeoPattern(), $url, $matches))
{
$id = !empty($matches[1]) ? $matches[1] : null;
$provider = 'vimeo';
}
else if (preg_match(self::getFacebookVideoPattern(), $url))
{
$id = $url;
$provider = 'facebookvideo';
}
else if (preg_match(self::getDailymotionPattern(), $url, $matches))
{
$id = end($matches);
$provider = 'dailymotion';
}
return [
'id' => $id,
'provider' => $provider
];
}
/**
* Get YouTube Pattern.
*
* @return string
*/
public static function getYouTubePattern()
{
return '/^https?:\/\/(?:m\.|www\.)?youtube\.com\/(?:watch\?v=|embed\/)?([a-zA-Z0-9_-]{11})|^https?:\/\/youtu\.be\/([a-zA-Z0-9_-]{11})/';
}
/**
* Get Vimeo Pattern.
*
* @return string
*/
public static function getVimeoPattern()
{
return '/^https?:\/\/(?:www\.)?(?:player\.)?vimeo\.com\/(\d+)/';
}
/**
* Get Facebook Video Pattern.
*
* @return string
*/
public static function getFacebookVideoPattern()
{
return '/^(?:(?:https?:)?\/\/)?(?:www\.)?facebook\.com\/(?:watch\/\?v=|[\w\.]+\/videos\/(?:[\w\.]+\/)?)?(\d+)/';
}
/**
* Get Dailymotion Pattern.
*
* @return string
*/
public static function getDailymotionPattern()
{
return '/(?:dailymotion\.com\/(?:video|hub)\/|dai\.ly\/)([a-zA-Z0-9]+)/';
}
}