This commit is contained in:
2024-12-31 11:07:09 +01:00
parent df7915205d
commit e089172b15
1916 changed files with 165422 additions and 271 deletions

View File

@ -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);;
}
}

View File

@ -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

View File

@ -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;
}
}