acf
This commit is contained in:
39
plugins/system/nrframework/NRFramework/Helpers/CSS.php
Normal file
39
plugins/system/nrframework/NRFramework/Helpers/CSS.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?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 . '
|
||||
}
|
||||
';
|
||||
}
|
||||
}
|
||||
175
plugins/system/nrframework/NRFramework/Helpers/ChainedFields.php
Normal file
175
plugins/system/nrframework/NRFramework/Helpers/ChainedFields.php
Normal file
@ -0,0 +1,175 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,76 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,269 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,101 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
61
plugins/system/nrframework/NRFramework/Helpers/File.php
Normal file
61
plugins/system/nrframework/NRFramework/Helpers/File.php
Normal file
@ -0,0 +1,61 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
39
plugins/system/nrframework/NRFramework/Helpers/Geo.php
Normal file
39
plugins/system/nrframework/NRFramework/Helpers/Geo.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?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();
|
||||
}
|
||||
}
|
||||
45
plugins/system/nrframework/NRFramework/Helpers/License.php
Normal file
45
plugins/system/nrframework/NRFramework/Helpers/License.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
60
plugins/system/nrframework/NRFramework/Helpers/Module.php
Normal file
60
plugins/system/nrframework/NRFramework/Helpers/Module.php
Normal file
@ -0,0 +1,60 @@
|
||||
<?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;
|
||||
|
||||
class Module
|
||||
{
|
||||
/**
|
||||
* Get a module data.
|
||||
*
|
||||
* @param integer $value
|
||||
* @param string $selector
|
||||
* @param array $options
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public static function getData($value, $selector = 'id', $options = ['access' => 1])
|
||||
{
|
||||
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));
|
||||
|
||||
if (count($options))
|
||||
{
|
||||
foreach ($options as $column => $value)
|
||||
{
|
||||
$query->where($db->quoteName($column) . ' = ' . $db->quote($value));
|
||||
}
|
||||
}
|
||||
|
||||
$db->setQuery($query);
|
||||
|
||||
if (!$result = $db->loadResult())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
return new \Joomla\Registry\Registry($result);
|
||||
}
|
||||
}
|
||||
63
plugins/system/nrframework/NRFramework/Helpers/Number.php
Normal file
63
plugins/system/nrframework/NRFramework/Helpers/Number.php
Normal file
@ -0,0 +1,63 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
167
plugins/system/nrframework/NRFramework/Helpers/Responsive.php
Normal file
167
plugins/system/nrframework/NRFramework/Helpers/Responsive.php
Normal file
@ -0,0 +1,167 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
38
plugins/system/nrframework/NRFramework/Helpers/Settings.php
Normal file
38
plugins/system/nrframework/NRFramework/Helpers/Settings.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?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\Plugin\PluginHelper;
|
||||
use Joomla\Registry\Registry;
|
||||
|
||||
class Settings
|
||||
{
|
||||
/**
|
||||
* Get the value of a specific setting from the Tassos Framework plugin.
|
||||
*
|
||||
* @param string $key The key of the setting to retrieve.
|
||||
* @param string $default The default value to return if the setting is not found.
|
||||
*
|
||||
* @return string The value of the setting, or the default value if the setting is not found.
|
||||
*/
|
||||
public static function getValue($key = '', $default = '')
|
||||
{
|
||||
if (!$framework = PluginHelper::getPlugin('system', 'nrframework'))
|
||||
{
|
||||
return $this->defaultAPIKey;
|
||||
}
|
||||
|
||||
// Get plugin params
|
||||
$params = new Registry($framework->params);
|
||||
return $params->get($key, $default);
|
||||
}
|
||||
}
|
||||
53
plugins/system/nrframework/NRFramework/Helpers/Template.php
Normal file
53
plugins/system/nrframework/NRFramework/Helpers/Template.php
Normal file
@ -0,0 +1,53 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
98
plugins/system/nrframework/NRFramework/Helpers/Video.php
Normal file
98
plugins/system/nrframework/NRFramework/Helpers/Video.php
Normal file
@ -0,0 +1,98 @@
|
||||
<?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\/|shorts\/)?([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]+)/';
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,575 @@
|
||||
<?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);;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,942 @@
|
||||
<?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();
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,51 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user