acf
This commit is contained in:
460
plugins/system/nrframework/NRFramework/Widgets/Accordion.php
Normal file
460
plugins/system/nrframework/NRFramework/Widgets/Accordion.php
Normal file
@ -0,0 +1,460 @@
|
||||
<?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\Widgets;
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\Registry\Registry;
|
||||
use Joomla\CMS\HTML\HTMLHelper;
|
||||
|
||||
class Accordion extends Widget
|
||||
{
|
||||
/**
|
||||
* Widget default options
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $widget_options = [
|
||||
/**
|
||||
* Accordion Settings
|
||||
*/
|
||||
|
||||
/**
|
||||
* The titles and contents of the accordion items.
|
||||
*
|
||||
* Format:
|
||||
*
|
||||
* [
|
||||
* [
|
||||
* 'title' => 'Title 1',
|
||||
* 'content' => 'Content 1'
|
||||
* ],
|
||||
* [
|
||||
* 'title' => 'Title 2',
|
||||
* 'content' => 'Content 2'
|
||||
* ],
|
||||
* ]
|
||||
*/
|
||||
'value' => '',
|
||||
|
||||
/**
|
||||
* Choose how spacious or compact you'd like to list accordion.
|
||||
* Consider this the padding of each accordion item.
|
||||
*
|
||||
* Available values:
|
||||
* - none
|
||||
* - default
|
||||
* - comfortable
|
||||
* - compact
|
||||
*/
|
||||
'density' => 'default',
|
||||
|
||||
// Set the font size.
|
||||
'font_size' => '16px',
|
||||
|
||||
/**
|
||||
* Set the gap between the items.
|
||||
*
|
||||
* Available values:
|
||||
* - none
|
||||
* - small
|
||||
* - large
|
||||
*/
|
||||
'gap' => 'none',
|
||||
|
||||
// Set the background color of the panel.
|
||||
'panel_background_color' => '#fff',
|
||||
|
||||
// Set the color of the text and the icon.
|
||||
'text_color' => '#333',
|
||||
|
||||
// Set the color of the 1px border that affects both item and container. Set to 'none' for no border color.
|
||||
'border_color' => '#ddd',
|
||||
|
||||
/**
|
||||
* Set the rounded corners of the items.
|
||||
*
|
||||
* Available values:
|
||||
* - none
|
||||
* - small
|
||||
* - large
|
||||
*/
|
||||
'rounded_corners' => 'small',
|
||||
|
||||
/**
|
||||
* Item Icon Settings
|
||||
*/
|
||||
/**
|
||||
* Set whether to display a toggle icon next to the title, or not.
|
||||
*
|
||||
* Available values:
|
||||
* - none
|
||||
* - left
|
||||
* - right
|
||||
*/
|
||||
'show_icon' => 'left',
|
||||
|
||||
/**
|
||||
* Set the icon URL.
|
||||
*/
|
||||
'icon' => '',
|
||||
|
||||
/**
|
||||
* Behavior
|
||||
*/
|
||||
/**
|
||||
* Define the initial state of the accordion.
|
||||
* By default all items are initially shown collapsed.
|
||||
*
|
||||
* Available values:
|
||||
*
|
||||
* - collapsed: All panels appear as collapsed.
|
||||
* - expanded: All panels appear as expanded.
|
||||
* - expanded-first: Expand the first panel only.
|
||||
*/
|
||||
'initial_state' => 'collapsed',
|
||||
|
||||
// Set whether to allow only one panel to be expanded at a time.
|
||||
'only_one_panel_expanded' => false,
|
||||
|
||||
// Set whether to generate the FAQ Schema on the page.
|
||||
'generate_faq' => false,
|
||||
|
||||
// Custom Panel CSS Class
|
||||
'panel_css_class' => ''
|
||||
];
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct($options = [])
|
||||
{
|
||||
parent::__construct($options);
|
||||
|
||||
$this->prepare();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the FAQ.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function prepare()
|
||||
{
|
||||
$this->validateValue();
|
||||
|
||||
$this->generateFAQ();
|
||||
|
||||
// Set density
|
||||
switch ($this->options['density'])
|
||||
{
|
||||
case 'none':
|
||||
$this->options['density'] = 0;
|
||||
break;
|
||||
case 'default':
|
||||
$this->options['density'] = '1em 1.25em';
|
||||
break;
|
||||
case 'comfortable':
|
||||
$this->options['density'] = '1.25em 1.75em';
|
||||
break;
|
||||
case 'compact':
|
||||
$this->options['density'] = '.65em 1.25em';
|
||||
break;
|
||||
}
|
||||
|
||||
// Set gap
|
||||
switch ($this->options['gap'])
|
||||
{
|
||||
case 'none':
|
||||
$this->options['gap'] = 0;
|
||||
break;
|
||||
case 'small':
|
||||
$this->options['gap'] = '.3em';
|
||||
break;
|
||||
case 'large':
|
||||
$this->options['gap'] = '.7em';
|
||||
break;
|
||||
}
|
||||
|
||||
// Set rounded corners
|
||||
switch ($this->options['rounded_corners'])
|
||||
{
|
||||
case 'none':
|
||||
$this->options['rounded_corners'] = 0;
|
||||
break;
|
||||
case 'small':
|
||||
$this->options['rounded_corners'] = '.3em';
|
||||
break;
|
||||
case 'large':
|
||||
$this->options['rounded_corners'] = '.7em';
|
||||
break;
|
||||
}
|
||||
|
||||
if ($this->options['only_one_panel_expanded'])
|
||||
{
|
||||
$this->options['css_class'] .= ' only-one-panel-expanded';
|
||||
}
|
||||
|
||||
if ($this->options['load_css_vars'])
|
||||
{
|
||||
$this->options['custom_css'] = $this->getWidgetCSS();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the value.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function validateValue()
|
||||
{
|
||||
if (!is_array($this->options['value']))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$st = new \NRFramework\SmartTags\SmartTags();
|
||||
|
||||
foreach ($this->options['value'] as $key => &$val)
|
||||
{
|
||||
if ((isset($val['title']) && empty($val['title'])) && (isset($val['content'])) && empty($val['content']))
|
||||
{
|
||||
unset($this->options['value'][$key]);
|
||||
}
|
||||
|
||||
if ($this->options['pro'])
|
||||
{
|
||||
$val['title'] = HTMLHelper::_('content.prepare', $val['title']);
|
||||
$val['content'] = HTMLHelper::_('content.prepare', $val['content']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the FAQ.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function generateFAQ()
|
||||
{
|
||||
// Ensure "generate_faq" is enabled
|
||||
if (!$this->options['generate_faq'])
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure we have value
|
||||
if (!is_array($this->options['value']) && !count($this->options['value']))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Abort if FAQ cannot be compiled
|
||||
if (!$faq = $this->getFAQ())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Hook into GSD to add the FAQ
|
||||
Factory::getApplication()->registerEvent('onGSDBeforeRender', function(&$data) use ($faq)
|
||||
{
|
||||
try
|
||||
{
|
||||
// get the data
|
||||
$tmpData = $data;
|
||||
if (defined('nrJ4'))
|
||||
{
|
||||
$tmpData = $data->getArgument('0');
|
||||
}
|
||||
|
||||
// Append the FAQ Schema
|
||||
$tmpData[] = $faq;
|
||||
|
||||
// Ensure unique FAQ
|
||||
$tmpData = array_unique($tmpData);
|
||||
|
||||
// Set back the new value to $data object
|
||||
if (defined('nrJ4'))
|
||||
{
|
||||
$data->setArgument(0, $tmpData);
|
||||
}
|
||||
else
|
||||
{
|
||||
$data = $tmpData;
|
||||
}
|
||||
|
||||
} catch (\Throwable $th)
|
||||
{
|
||||
$this->throwError($th->getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the FAQ JSON/LD code.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getFAQ()
|
||||
{
|
||||
$autoload_file = JPATH_ADMINISTRATOR . '/components/com_gsd/autoload.php';
|
||||
if (!file_exists($autoload_file))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
require_once $autoload_file;
|
||||
|
||||
$value = $this->options['value'];
|
||||
if (is_array($value))
|
||||
{
|
||||
foreach ($value as $key => &$val)
|
||||
{
|
||||
if (isset($val['title']))
|
||||
{
|
||||
$val['question'] = $val['title'];
|
||||
unset($val['title']);
|
||||
}
|
||||
|
||||
if (isset($val['content']))
|
||||
{
|
||||
$val['answer'] = $val['content'];
|
||||
unset($val['content']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare the FAQ
|
||||
$payload = [
|
||||
'mode' => 'manual',
|
||||
'faq_repeater_fields' => json_decode(json_encode($value))
|
||||
];
|
||||
$payload = new Registry($payload);
|
||||
$faq = new \GSD\Schemas\Schemas\FAQ($payload);
|
||||
|
||||
// Get the JSON/LD code of the FAQ
|
||||
$json = new \GSD\Json($faq->get());
|
||||
|
||||
// Return the code
|
||||
return $json->generate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the CSS for the widget.
|
||||
*
|
||||
* @param array $exclude_breakpoints Define breakpoints to exclude their CSS
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getWidgetCSS($exclude_breakpoints = [])
|
||||
{
|
||||
$border_color = $this->options['border_color'] !== 'none' ? $this->options['border_color'] : 'transparent';
|
||||
|
||||
$controls = [];
|
||||
|
||||
// If no density is set, set the padding-top to 5px
|
||||
if (!$this->options['density'])
|
||||
{
|
||||
$controls[] = [
|
||||
'property' => '--content-padding-top',
|
||||
'value' => '5px'
|
||||
];
|
||||
}
|
||||
|
||||
if (!$this->options['gap'])
|
||||
{
|
||||
$controls[] = [
|
||||
'property' => '--container-border-color',
|
||||
'value' => $border_color
|
||||
];
|
||||
|
||||
$this->options['css_class'] .= ' no-gap';
|
||||
}
|
||||
|
||||
$controls = array_merge($controls, [
|
||||
// CSS Variables
|
||||
[
|
||||
'property' => '--panel-background-color',
|
||||
'value' => $this->options['panel_background_color']
|
||||
],
|
||||
[
|
||||
'property' => '--text-color',
|
||||
'value' => $this->options['text_color']
|
||||
],
|
||||
[
|
||||
'property' => '--panel-border-color',
|
||||
'value' => $border_color
|
||||
],
|
||||
|
||||
// CSS
|
||||
[
|
||||
'property' => '--padding',
|
||||
'type' => 'Spacing',
|
||||
'value' => $this->options['density']
|
||||
],
|
||||
[
|
||||
'property' => '--gap',
|
||||
'value' => $this->options['gap'],
|
||||
'unit' => 'px'
|
||||
],
|
||||
[
|
||||
'property' => '--rounded-corners',
|
||||
'type' => 'Spacing',
|
||||
'value' => $this->options['rounded_corners'],
|
||||
'unit' => 'px'
|
||||
],
|
||||
[
|
||||
'property' => '--font-size',
|
||||
'value' => $this->options['font_size'],
|
||||
'unit' => 'px'
|
||||
]
|
||||
]);
|
||||
|
||||
$selector = '.tf-accordion-widget.' . $this->options['id'];
|
||||
|
||||
$controlsInstance = new \NRFramework\Controls\Controls(null, $selector, $exclude_breakpoints);
|
||||
|
||||
if (!$controlsCSS = $controlsInstance->generateCSS($controls))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
return $controlsCSS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all CSS files.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getCSS()
|
||||
{
|
||||
return [
|
||||
'plg_system_nrframework/widgets/accordion.css'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all JS files.
|
||||
*
|
||||
* @param string $theme
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getJS()
|
||||
{
|
||||
return [
|
||||
'plg_system_nrframework/widgets/accordion.js'
|
||||
];
|
||||
}
|
||||
}
|
||||
30
plugins/system/nrframework/NRFramework/Widgets/BingMap.php
Normal file
30
plugins/system/nrframework/NRFramework/Widgets/BingMap.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?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\Widgets;
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\HTML\HTMLHelper;
|
||||
|
||||
class BingMap extends Map
|
||||
{
|
||||
/**
|
||||
* Loads media files
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function loadMedia()
|
||||
{
|
||||
parent::loadMedia();
|
||||
|
||||
HTMLHelper::script('plg_system_nrframework/widgets/bingmap.js', ['relative' => true, 'version' => 'auto']);
|
||||
HTMLHelper::script('https://www.bing.com/api/maps/mapcontrol?callback=TFBingMapsCallback&key=' . $this->options['provider_key']);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
<?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\Widgets;
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
/**
|
||||
* Color picker
|
||||
*/
|
||||
class ColorPicker extends Widget
|
||||
{
|
||||
/**
|
||||
* Widget default options
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $widget_options = [
|
||||
// The default value of the widget.
|
||||
'value' => '#dedede',
|
||||
|
||||
// The input border color
|
||||
'input_border_color' => '#dedede',
|
||||
|
||||
// The input border color on focus
|
||||
'input_border_color_focus' => '#dedede',
|
||||
|
||||
// The input background color
|
||||
'input_bg_color' => '#fff',
|
||||
|
||||
// Input text color
|
||||
'input_text_color' => '#333'
|
||||
];
|
||||
}
|
||||
542
plugins/system/nrframework/NRFramework/Widgets/Countdown.php
Normal file
542
plugins/system/nrframework/NRFramework/Widgets/Countdown.php
Normal file
@ -0,0 +1,542 @@
|
||||
<?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\Widgets;
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\CMS\Factory;
|
||||
|
||||
/**
|
||||
* Countdown
|
||||
*/
|
||||
class Countdown extends Widget
|
||||
{
|
||||
/**
|
||||
* Widget default options
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $widget_options = [
|
||||
/**
|
||||
* The Countdown type:
|
||||
*
|
||||
* - static: Counts down to a specific date and time. Universal deadline for all visitors.
|
||||
* - evergreen: Set-and-forget solution. The countdown starts when your visitor sees the offer.
|
||||
*/
|
||||
'countdown_type' => 'static',
|
||||
|
||||
// The Static Countdown Date
|
||||
'value' => '',
|
||||
|
||||
/**
|
||||
* The timezone that will be used.
|
||||
*
|
||||
* - server - Use server's timezone
|
||||
* - client - Use client's timezone
|
||||
*/
|
||||
'timezone' => 'server',
|
||||
|
||||
// Dynamic Days
|
||||
'dynamic_days' => 0,
|
||||
|
||||
// Dynamic Hours
|
||||
'dynamic_hours' => 0,
|
||||
|
||||
// Dynamic Minutes
|
||||
'dynamic_minutes' => 0,
|
||||
|
||||
// Dynamic Seconds
|
||||
'dynamic_seconds' => 0,
|
||||
|
||||
/**
|
||||
* The countdown format.
|
||||
*
|
||||
* Available tags:
|
||||
* {years}
|
||||
* {months}
|
||||
* {days}
|
||||
* {hours}
|
||||
* {minutes}
|
||||
* {seconds}
|
||||
*/
|
||||
'format' => '{days} days, {hours} hours, {minutes} minutes and {seconds} seconds',
|
||||
|
||||
/**
|
||||
* The countdown theme.
|
||||
*
|
||||
* Available themes:
|
||||
* default
|
||||
* oneline
|
||||
* custom
|
||||
*/
|
||||
'theme' => 'default',
|
||||
|
||||
/**
|
||||
* Set the action once countdown finishes.
|
||||
*
|
||||
* Available values:
|
||||
* keep - Keep the countdown visible
|
||||
* hide - Hide the countdown
|
||||
* restart - Restart the countdown
|
||||
* message - Show a message
|
||||
* redirect - Redirect to a URL
|
||||
*/
|
||||
'countdown_action' => 'keep',
|
||||
|
||||
/**
|
||||
* The message appearing after the countdown has finished.
|
||||
*
|
||||
* Requires `countdown_action` to be set to `message`
|
||||
*
|
||||
* Example: Countdown finished.
|
||||
*/
|
||||
'finish_text' => '',
|
||||
|
||||
/**
|
||||
* The redirect URL once the countdown expires.
|
||||
*
|
||||
* Requires `countdown_action` to be set to `redirect`
|
||||
*/
|
||||
'redirect_url' => '',
|
||||
|
||||
/**
|
||||
* Widget Settings
|
||||
*/
|
||||
// Gap
|
||||
'gap' => 20,
|
||||
|
||||
// Background Color
|
||||
'background_color' => '',
|
||||
|
||||
/**
|
||||
* Unit Display Settings
|
||||
*/
|
||||
// Whether to display Days
|
||||
'days' => true,
|
||||
|
||||
// Days Label
|
||||
'days_label' => 'Days',
|
||||
|
||||
// Whether to display Hours
|
||||
'hours' => true,
|
||||
|
||||
// Hours Label
|
||||
'hours_label' => 'Hrs',
|
||||
|
||||
// Whether to display Minutes
|
||||
'minutes' => true,
|
||||
|
||||
// Minutes Label
|
||||
'minutes_label' => 'Mins',
|
||||
|
||||
// Whether to display Seconds
|
||||
'seconds' => true,
|
||||
|
||||
// Seconds Label
|
||||
'seconds_label' => 'Secs',
|
||||
|
||||
// Whether to display a separator between the units
|
||||
'separator' => false,
|
||||
|
||||
// Whether to display numbers in 00 or 0 format
|
||||
'double_zeroes_format' => true,
|
||||
|
||||
/**
|
||||
* Unit Item Settings
|
||||
*/
|
||||
// The size (width, height) of the unit item in pixels
|
||||
'item_size' => null,
|
||||
|
||||
// Each item padding
|
||||
'item_padding' => null,
|
||||
|
||||
// The unit item border width
|
||||
'item_border_width' => '',
|
||||
|
||||
// The unit item border style
|
||||
'item_border_style' => '',
|
||||
|
||||
// The unit item border color
|
||||
'item_border_color' => '',
|
||||
|
||||
// The unit item border radius
|
||||
'item_border_radius' => null,
|
||||
|
||||
// Item Background Color
|
||||
'item_background_color' => '',
|
||||
|
||||
/**
|
||||
* Unit Digits Container Settings
|
||||
*/
|
||||
// Digits wrapper Min Width
|
||||
'digits_wrapper_min_width' => 0,
|
||||
|
||||
// The digits wrapper padding
|
||||
'digits_wrapper_padding' => null,
|
||||
|
||||
// The digits wrapper border radius
|
||||
'digits_wrapper_border_radius' => null,
|
||||
|
||||
// The digits wrapper background color.
|
||||
'digits_wrapper_background_color' => '',
|
||||
|
||||
/**
|
||||
* Unit Digit Settings
|
||||
*/
|
||||
// Digits Font Size
|
||||
'digits_font_size' => 25,
|
||||
|
||||
// Digits Font Weight
|
||||
'digits_font_weight' => '400',
|
||||
|
||||
// Digit Min Width
|
||||
'digit_min_width' => 0,
|
||||
|
||||
// The digits padding
|
||||
'digits_padding' => null,
|
||||
|
||||
// The digits border radius
|
||||
'digit_border_radius' => null,
|
||||
|
||||
// Digits Gap
|
||||
'digits_gap' => null,
|
||||
|
||||
// Digit Item Background Color. This applies for each of the 2 digits on a unit.
|
||||
'digit_background_color' => '',
|
||||
|
||||
// Digit Item Text Color
|
||||
'digit_text_color' => '',
|
||||
|
||||
/**
|
||||
* Unit Label Settings
|
||||
*/
|
||||
// Label Font Size
|
||||
'label_font_size' => 13,
|
||||
|
||||
// Label Font Weight
|
||||
'label_font_weight' => '400',
|
||||
|
||||
// Unit Label Margin Top. The spacing between the unit and its label.
|
||||
'unit_label_margin_top' => 5,
|
||||
|
||||
// Unit Label Color
|
||||
'unit_label_text_color' => '',
|
||||
|
||||
// Extra attributes added to the widget
|
||||
'atts' => '',
|
||||
|
||||
// TODO: Remove in the future
|
||||
'css_vars' => [],
|
||||
|
||||
// Preview HTML used prior to JS initializing the Countdown
|
||||
'preview_html' => ''
|
||||
];
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct($options = [])
|
||||
{
|
||||
parent::__construct($options);
|
||||
|
||||
Text::script('NR_AND_LC');
|
||||
|
||||
$this->prepare();
|
||||
|
||||
if ($this->options['load_css_vars'] && $this->options['theme'] !== 'custom')
|
||||
{
|
||||
$this->options['custom_css'] .= $this->getWidgetCSS();
|
||||
|
||||
/**
|
||||
* TODO: Remove in the future
|
||||
*
|
||||
* For compatibility purposes for old customers using old
|
||||
* ACF version used by ACF Previewer which is required to
|
||||
* style the Countdown in the previewer.
|
||||
*/
|
||||
$this->options['css_vars'] = $this->options['custom_css'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the countdown.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function prepare()
|
||||
{
|
||||
$this->options['css_class'] .= ' is-preview ' . $this->options['theme'];
|
||||
|
||||
if (!empty($this->options['value']) && $this->options['value'] !== '0000-00-00 00:00:00')
|
||||
{
|
||||
if ($this->options['countdown_type'] === 'static' && $this->options['timezone'] === 'server')
|
||||
{
|
||||
// Get timezone
|
||||
$tz = new \DateTimeZone(Factory::getApplication()->getCfg('offset', 'UTC'));
|
||||
|
||||
// Convert given date time to UTC
|
||||
$this->options['value'] = date_create($this->options['value'], $tz)->setTimezone(new \DateTimeZone('UTC'))->format('c');
|
||||
|
||||
// Apply server timezone
|
||||
$this->options['value'] = (new \DateTime($this->options['value']))->setTimezone($tz)->format('c');
|
||||
}
|
||||
}
|
||||
|
||||
$this->options['preview_html'] = $this->getPreviewHTML();
|
||||
|
||||
// Set countdown payload
|
||||
$payload = [
|
||||
'data-countdown-type="' . $this->options['countdown_type'] . '"',
|
||||
'data-value="' . $this->options['value'] . '"',
|
||||
'data-timezone="' . $this->options['timezone'] . '"',
|
||||
'data-separator="' . (json_decode($this->options['separator']) ? 'true' : 'false') . '"',
|
||||
'data-double-zeroes-format="' . (json_decode($this->options['double_zeroes_format']) ? 'true' : 'false') . '"',
|
||||
'data-dynamic-days="' . $this->options['dynamic_days'] . '"',
|
||||
'data-dynamic-hours="' . $this->options['dynamic_hours'] . '"',
|
||||
'data-dynamic-minutes="' . $this->options['dynamic_minutes'] . '"',
|
||||
'data-dynamic-seconds="' . $this->options['dynamic_seconds'] . '"',
|
||||
'data-finish-text="' . htmlspecialchars($this->options['finish_text']) . '"',
|
||||
'data-redirect-url="' . $this->options['redirect_url'] . '"',
|
||||
'data-theme="' . $this->options['theme'] . '"',
|
||||
'data-countdown-action="' . $this->options['countdown_action'] . '"',
|
||||
'data-days="' . (json_decode($this->options['days']) ? 'true' : 'false') . '"',
|
||||
'data-days-label="' . $this->options['days_label'] . '"',
|
||||
'data-hours="' . (json_decode($this->options['hours']) ? 'true' : 'false') . '"',
|
||||
'data-hours-label="' . $this->options['hours_label'] . '"',
|
||||
'data-minutes="' . (json_decode($this->options['minutes']) ? 'true' : 'false') . '"',
|
||||
'data-minutes-label="' . $this->options['minutes_label'] . '"',
|
||||
'data-seconds="' . (json_decode($this->options['seconds']) ? 'true' : 'false') . '"',
|
||||
'data-seconds-label="' . $this->options['seconds_label'] . '"'
|
||||
];
|
||||
|
||||
// Only set the format for custom-themed countdown instances
|
||||
if ($this->options['theme'] === 'custom')
|
||||
{
|
||||
$payload[] = 'data-format="' . htmlspecialchars($this->options['format']) . '"';
|
||||
}
|
||||
|
||||
$this->options['atts'] = implode(' ', $payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the CSS for the widget.
|
||||
*
|
||||
* @param array $exclude_breakpoints Define breakpoints to exclude their CSS
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getWidgetCSS($exclude_breakpoints = [])
|
||||
{
|
||||
$controls = [
|
||||
// CSS Variables
|
||||
[
|
||||
'property' => '--digits-background-color',
|
||||
'value' => $this->options['digits_wrapper_background_color']
|
||||
],
|
||||
[
|
||||
'property' => '--background-color',
|
||||
'value' => $this->options['background_color']
|
||||
],
|
||||
[
|
||||
'property' => '--item-background-color',
|
||||
'value' => $this->options['item_background_color']
|
||||
],
|
||||
[
|
||||
'property' => '--unit-label-text-color',
|
||||
'value' => $this->options['unit_label_text_color']
|
||||
],
|
||||
[
|
||||
'property' => '--digit-background-color',
|
||||
'value' => $this->options['digit_background_color']
|
||||
],
|
||||
[
|
||||
'property' => '--digit-text-color',
|
||||
'value' => $this->options['digit_text_color']
|
||||
],
|
||||
[
|
||||
'property' => '--unit-label-margin-top',
|
||||
'value' => $this->options['unit_label_margin_top'],
|
||||
'unit' => 'px'
|
||||
],
|
||||
[
|
||||
'property' => '--digits-wrapper-min-width',
|
||||
'value' => $this->options['digits_wrapper_min_width'],
|
||||
'unit' => 'px'
|
||||
],
|
||||
[
|
||||
'property' => '--digit-min-width',
|
||||
'value' => $this->options['digit_min_width'],
|
||||
'unit' => 'px'
|
||||
],
|
||||
[
|
||||
'property' => '--digits-font-weight',
|
||||
'value' => $this->options['digits_font_weight']
|
||||
],
|
||||
[
|
||||
'property' => '--label-font-weight',
|
||||
'value' => $this->options['label_font_weight']
|
||||
],
|
||||
[
|
||||
'property' => '--item-border',
|
||||
'type' => 'Border',
|
||||
'value' => [
|
||||
'width' => $this->options['item_border_width'],
|
||||
'style' => $this->options['item_border_style'],
|
||||
'color' => $this->options['item_border_color'],
|
||||
'unit' => 'px'
|
||||
]
|
||||
],
|
||||
|
||||
// CSS
|
||||
[
|
||||
'type' => 'Spacing',
|
||||
'property' => '--item-padding',
|
||||
'value' => $this->options['item_padding'],
|
||||
'unit' => 'px'
|
||||
],
|
||||
[
|
||||
'type' => 'Spacing',
|
||||
'property' => '--digits-padding',
|
||||
'value' => $this->options['digits_wrapper_padding'],
|
||||
'unit' => 'px'
|
||||
],
|
||||
[
|
||||
'property' => '--gap',
|
||||
'value' => $this->options['gap'],
|
||||
'unit' => 'px'
|
||||
],
|
||||
[
|
||||
'property' => '--digits-gap',
|
||||
'value' => $this->options['digits_gap'],
|
||||
'unit' => 'px'
|
||||
],
|
||||
[
|
||||
'property' => '--item-size',
|
||||
'value' => $this->options['item_size'],
|
||||
'unit' => 'px'
|
||||
],
|
||||
[
|
||||
'property' => '--digits-font-size',
|
||||
'value' => $this->options['digits_font_size'],
|
||||
'unit' => 'px'
|
||||
],
|
||||
[
|
||||
'property' => '--label-font-size',
|
||||
'value' => $this->options['label_font_size'],
|
||||
'unit' => 'px'
|
||||
],
|
||||
[
|
||||
'type' => 'Spacing',
|
||||
'property' => '--digit-padding',
|
||||
'value' => $this->options['digits_padding'],
|
||||
'unit' => 'px'
|
||||
],
|
||||
[
|
||||
'type' => 'Spacing',
|
||||
'property' => '--item-border-radius',
|
||||
'value' => $this->options['item_border_radius'],
|
||||
'unit' => 'px'
|
||||
],
|
||||
[
|
||||
'type' => 'Spacing',
|
||||
'property' => '--digits-border-radius',
|
||||
'value' => $this->options['digits_wrapper_border_radius'],
|
||||
'unit' => 'px'
|
||||
],
|
||||
[
|
||||
'type' => 'Spacing',
|
||||
'property' => '--digit-border-radius',
|
||||
'value' => $this->options['digit_border_radius'],
|
||||
'unit' => 'px'
|
||||
],
|
||||
];
|
||||
|
||||
$selector = '.nrf-countdown.' . $this->options['id'];
|
||||
|
||||
$controlsInstance = new \NRFramework\Controls\Controls(null, $selector, $exclude_breakpoints);
|
||||
|
||||
if (!$controlsCSS = $controlsInstance->generateCSS($controls))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
return $controlsCSS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns preview HTML.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getPreviewHTML()
|
||||
{
|
||||
if ($this->options['theme'] === 'custom')
|
||||
{
|
||||
return $this->options['format'];
|
||||
}
|
||||
|
||||
$format_items = [
|
||||
'days' => $this->options['days'],
|
||||
'hours' => $this->options['hours'],
|
||||
'minutes' => $this->options['minutes'],
|
||||
'seconds' => $this->options['seconds']
|
||||
];
|
||||
|
||||
$html = '';
|
||||
|
||||
foreach ($format_items as $key => $value)
|
||||
{
|
||||
$labelStr = !empty($this->options[$key . '_label']) ? '<span class="countdown-digit-label">' . $this->options[$key . '_label'] . '</span>' : '';
|
||||
$html .= '<span class="countdown-item"><span class="countdown-digit ' . $key . '"><span class="digit-number digit-1">0</span><span class="digit-number digit-2">0</span></span>' . $labelStr . '</span>';
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all CSS files.
|
||||
*
|
||||
* @param string $theme
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getCSS($theme = 'default')
|
||||
{
|
||||
$css = [];
|
||||
|
||||
if ($theme !== 'custom')
|
||||
{
|
||||
$css[] = 'plg_system_nrframework/widgets/countdown.css';
|
||||
}
|
||||
else
|
||||
{
|
||||
$css[] = 'plg_system_nrframework/widgets/widget.css';
|
||||
}
|
||||
|
||||
return $css;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all JS files.
|
||||
*
|
||||
* @param string $theme
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getJS()
|
||||
{
|
||||
return [
|
||||
'plg_system_nrframework/widgets/countdown.js'
|
||||
];
|
||||
}
|
||||
}
|
||||
119
plugins/system/nrframework/NRFramework/Widgets/Dailymotion.php
Normal file
119
plugins/system/nrframework/NRFramework/Widgets/Dailymotion.php
Normal file
@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2020 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace NRFramework\Widgets;
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\Uri\Uri;
|
||||
use Joomla\CMS\HTML\HTMLHelper;
|
||||
|
||||
class Dailymotion extends Video
|
||||
{
|
||||
/**
|
||||
* Widget default options
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $video_widget_options = [
|
||||
// Start the video from X seconds
|
||||
'start' => null,
|
||||
|
||||
// End the video at X seconds
|
||||
'end' => null,
|
||||
|
||||
// Loop
|
||||
'loop' => false,
|
||||
|
||||
// Mute
|
||||
'mute' => false,
|
||||
|
||||
// Whether controls will appear in the video
|
||||
'controls' => false,
|
||||
|
||||
/**
|
||||
* Set the cover image type.
|
||||
*
|
||||
* Allowed Values:
|
||||
* - none
|
||||
* - auto
|
||||
* - custom
|
||||
*/
|
||||
'coverImageType' => 'none',
|
||||
|
||||
// The Cover Image URL when coverImage="custom"
|
||||
'coverImage' => '',
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* Prepares the widget.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function prepare()
|
||||
{
|
||||
$videoDetails = \NRFramework\Helpers\Video::getDetails($this->options['value']);
|
||||
$videoProvider = isset($videoDetails['provider']) ? $videoDetails['provider'] : '';
|
||||
|
||||
// Abort
|
||||
if ($videoProvider !== 'dailymotion')
|
||||
{
|
||||
$this->options['value'] = null;
|
||||
return;
|
||||
}
|
||||
|
||||
$this->options['css_class'] .= ' dailymotion';
|
||||
|
||||
$videoID = isset($videoDetails['id']) ? $videoDetails['id'] : '';
|
||||
|
||||
if ($this->options['coverImageType'] === 'auto')
|
||||
{
|
||||
$this->options['coverImage'] = 'url("https://www.dailymotion.com/thumbnail/video/' . $videoID . '")';
|
||||
}
|
||||
else if ($this->options['coverImageType'] === 'custom' && !empty($this->options['coverImage']))
|
||||
{
|
||||
$coverImage = explode('#', $this->options['coverImage']);
|
||||
$this->options['coverImage'] = 'url("' . Uri::base() . reset($coverImage) . '")';
|
||||
}
|
||||
|
||||
$atts = [
|
||||
'data-video-id="' . $videoID . '"',
|
||||
'data-video-type="' . $videoProvider . '"',
|
||||
'data-video-mute="' . var_export($this->options['mute'], true) . '"',
|
||||
'data-video-loop="' . var_export($this->options['loop'], true) . '"',
|
||||
'data-video-start="' . $this->options['start'] . '"',
|
||||
'data-video-end="' . $this->options['end'] . '"',
|
||||
'data-video-autoplay="' . var_export($this->options['autoplay'], true) . '"',
|
||||
'data-video-autopause="' . var_export($this->options['autopause'], true) . '"',
|
||||
];
|
||||
|
||||
$this->options['atts'] = implode(' ', $atts);
|
||||
}
|
||||
|
||||
/**
|
||||
* We use the video widget layout file.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'video';
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads media files
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function videoAssets()
|
||||
{
|
||||
HTMLHelper::script('plg_system_nrframework/widgets/video/dailymotion.js', ['relative' => true, 'version' => 'auto']);
|
||||
}
|
||||
}
|
||||
392
plugins/system/nrframework/NRFramework/Widgets/FAQ.php
Normal file
392
plugins/system/nrframework/NRFramework/Widgets/FAQ.php
Normal file
@ -0,0 +1,392 @@
|
||||
<?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\Widgets;
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\Registry\Registry;
|
||||
|
||||
class FAQ extends Widget
|
||||
{
|
||||
/**
|
||||
* Widget default options
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $widget_options = [
|
||||
/**
|
||||
* FAQ Settings
|
||||
*/
|
||||
|
||||
/**
|
||||
* The questions and answers.
|
||||
*
|
||||
* Format:
|
||||
*
|
||||
* [
|
||||
* [
|
||||
* 'question' => 'Question 1',
|
||||
* 'answer' => 'Answer 1'
|
||||
* ],
|
||||
* [
|
||||
* 'question' => 'Question 2',
|
||||
* 'answer' => 'Answer 2'
|
||||
* ],
|
||||
* ]
|
||||
*/
|
||||
'value' => '',
|
||||
|
||||
/**
|
||||
* Requires "show_toggle_icon" to be enabled to work.
|
||||
*
|
||||
* Define the initial state of the FAQ.
|
||||
*
|
||||
* Available values:
|
||||
*
|
||||
* - first-open: Open the first question
|
||||
* - all-open: Opens all questions
|
||||
* - all-closed: Closes all questions
|
||||
*/
|
||||
'initial_state' => 'first-open',
|
||||
|
||||
// Set whether to have one question open at a time
|
||||
'keep_one_question_open' => true,
|
||||
|
||||
// Set the columns.
|
||||
'columns' => 1,
|
||||
|
||||
// Set the gap between the items.
|
||||
'item_gap' => 16,
|
||||
|
||||
// Set the gap between the columns.
|
||||
'column_gap' => 16,
|
||||
|
||||
// Set whether to display a separator between items
|
||||
'separator' => false,
|
||||
|
||||
// Set the separator color
|
||||
'separator_color' => '',
|
||||
|
||||
/**
|
||||
* Item Settings
|
||||
*/
|
||||
// Each item background color.
|
||||
'item_background_color' => null,
|
||||
|
||||
// Each item border radius.
|
||||
'item_border_radius' => null,
|
||||
|
||||
// Each item padding.
|
||||
'item_padding' => null,
|
||||
|
||||
/**
|
||||
* Question
|
||||
*/
|
||||
// Question font size
|
||||
'question_font_size' => null,
|
||||
|
||||
// Each question text color.
|
||||
'question_text_color' => null,
|
||||
|
||||
/**
|
||||
* Answer
|
||||
*/
|
||||
// Answer font size
|
||||
'answer_font_size' => null,
|
||||
|
||||
// Each answer text color.
|
||||
'answer_text_color' => null,
|
||||
|
||||
/**
|
||||
* Icon Settings
|
||||
*/
|
||||
/**
|
||||
* Whether to show an icon that can toggle the open/close state of the answer.
|
||||
*
|
||||
* If disabled, all answers will appear by default.
|
||||
* If enabled, all answers will be hidden by default.
|
||||
*/
|
||||
'show_toggle_icon' => false,
|
||||
|
||||
/**
|
||||
* Set the icon that will be used.
|
||||
*
|
||||
* Available values:
|
||||
* - arrow
|
||||
* - plus_minus
|
||||
* - circle_arrow
|
||||
* - circle_plus_minus
|
||||
*/
|
||||
'icon' => 'arrow',
|
||||
|
||||
/**
|
||||
* Set the icon position.
|
||||
*
|
||||
* Available values:
|
||||
*
|
||||
* - right
|
||||
* - left
|
||||
*/
|
||||
'icon_position' => 'right',
|
||||
|
||||
/**
|
||||
* FAQ Schema
|
||||
*/
|
||||
// Set whether to generate the FAQ Schema on the page.
|
||||
'generate_faq' => false,
|
||||
|
||||
// Custom Item CSS Classes
|
||||
'item_css_class' => ''
|
||||
];
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct($options = [])
|
||||
{
|
||||
parent::__construct($options);
|
||||
|
||||
$this->prepare();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the FAQ.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function prepare()
|
||||
{
|
||||
if ($this->options['show_toggle_icon'])
|
||||
{
|
||||
$this->options['show_toggle_icon'] = true;
|
||||
$this->options['css_class'] .= ' has-icons';
|
||||
$this->options['css_class'] .= ' position-icon-' . $this->options['icon_position'];
|
||||
$this->options['css_class'] .= ' has-icon-' . $this->options['icon'];
|
||||
}
|
||||
|
||||
if (!empty($this->options['item_background_color']) && $this->options['item_background_color'] !== 'none')
|
||||
{
|
||||
$this->options['css_class'] .= ' has-item-bg-color';
|
||||
}
|
||||
|
||||
if ($this->options['separator'])
|
||||
{
|
||||
$this->options['css_class'] .= ' has-separator';
|
||||
}
|
||||
|
||||
$this->options['css_class'] .= ' ' . $this->options['initial_state'];
|
||||
|
||||
if ($this->options['keep_one_question_open'])
|
||||
{
|
||||
$this->options['css_class'] .= ' keep-one-question-open';
|
||||
}
|
||||
|
||||
if ((int) $this->options['columns'] > 1)
|
||||
{
|
||||
$this->options['css_class'] .= ' has-columns';
|
||||
}
|
||||
|
||||
$this->generateFAQ();
|
||||
|
||||
if ($this->options['load_css_vars'])
|
||||
{
|
||||
$this->options['custom_css'] = $this->getWidgetCSS();
|
||||
}
|
||||
}
|
||||
|
||||
private function generateFAQ()
|
||||
{
|
||||
// Ensure "generate_faq" is enabled
|
||||
if (!$this->options['generate_faq'])
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure we have questions and answers
|
||||
if (!is_array($this->options['value']) && !count($this->options['value']))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Abort if FAQ cannot be compiled
|
||||
if (!$faq = $this->getFAQ())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Hook into GSD to add the FAQ
|
||||
Factory::getApplication()->registerEvent('onGSDBeforeRender', function(&$data) use ($faq)
|
||||
{
|
||||
try
|
||||
{
|
||||
// get the data
|
||||
$tmpData = $data;
|
||||
if (defined('nrJ4'))
|
||||
{
|
||||
$tmpData = $data->getArgument('0');
|
||||
}
|
||||
|
||||
// Append the FAQ Schema
|
||||
$tmpData[] = $faq;
|
||||
|
||||
// Ensure unique FAQ
|
||||
$tmpData = array_unique($tmpData);
|
||||
|
||||
// Set back the new value to $data object
|
||||
if (defined('nrJ4'))
|
||||
{
|
||||
$data->setArgument(0, $tmpData);
|
||||
}
|
||||
else
|
||||
{
|
||||
$data = $tmpData;
|
||||
}
|
||||
|
||||
} catch (\Throwable $th)
|
||||
{
|
||||
$this->throwError($th->getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the FAQ JSON/LD code.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getFAQ()
|
||||
{
|
||||
$autoload_file = JPATH_ADMINISTRATOR . '/components/com_gsd/autoload.php';
|
||||
if (!file_exists($autoload_file))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
require_once $autoload_file;
|
||||
|
||||
// Prepare the FAQ
|
||||
$payload = [
|
||||
'mode' => 'manual',
|
||||
'faq_repeater_fields' => json_decode(json_encode($this->options['value']))
|
||||
];
|
||||
$payload = new Registry($payload);
|
||||
$faq = new \GSD\Schemas\Schemas\FAQ($payload);
|
||||
|
||||
// Get the JSON/LD code of the FAQ
|
||||
$json = new \GSD\Json($faq->get());
|
||||
|
||||
// Return the code
|
||||
return $json->generate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the CSS for the widget.
|
||||
*
|
||||
* @param array $exclude_breakpoints Define breakpoints to exclude their CSS
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getWidgetCSS($exclude_breakpoints = [])
|
||||
{
|
||||
$controls = [
|
||||
// CSS Variables
|
||||
[
|
||||
'property' => '--item-background-color',
|
||||
'value' => $this->options['item_background_color']
|
||||
],
|
||||
[
|
||||
'property' => '--question-text-color',
|
||||
'value' => $this->options['question_text_color']
|
||||
],
|
||||
[
|
||||
'property' => '--answer-text-color',
|
||||
'value' => $this->options['answer_text_color']
|
||||
],
|
||||
[
|
||||
'property' => '--separator-color',
|
||||
'value' => $this->options['separator_color']
|
||||
],
|
||||
|
||||
// CSS
|
||||
[
|
||||
'property' => '--item-padding',
|
||||
'type' => 'Spacing',
|
||||
'value' => $this->options['item_padding'],
|
||||
'unit' => 'px'
|
||||
],
|
||||
[
|
||||
'property' => '--item-gap',
|
||||
'value' => $this->options['item_gap'],
|
||||
'unit' => 'px'
|
||||
],
|
||||
[
|
||||
'property' => '--column-gap',
|
||||
'value' => $this->options['column_gap'],
|
||||
'unit' => 'px'
|
||||
],
|
||||
[
|
||||
'property' => '--item-border-radius',
|
||||
'type' => 'Spacing',
|
||||
'value' => $this->options['item_border_radius'],
|
||||
'unit' => 'px'
|
||||
],
|
||||
[
|
||||
'property' => '--question-font-size',
|
||||
'value' => $this->options['question_font_size'],
|
||||
'unit' => 'px'
|
||||
],
|
||||
[
|
||||
'property' => '--answer-font-size',
|
||||
'value' => $this->options['answer_font_size'],
|
||||
'unit' => 'px'
|
||||
],
|
||||
];
|
||||
|
||||
$selector = '.tf-faq-widget.' . $this->options['id'];
|
||||
|
||||
$controlsInstance = new \NRFramework\Controls\Controls(null, $selector, $exclude_breakpoints);
|
||||
|
||||
if (!$controlsCSS = $controlsInstance->generateCSS($controls))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
return $controlsCSS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all CSS files.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getCSS()
|
||||
{
|
||||
return [
|
||||
'plg_system_nrframework/widgets/faq.css'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all JS files.
|
||||
*
|
||||
* @param string $theme
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getJS()
|
||||
{
|
||||
return [
|
||||
'plg_system_nrframework/widgets/faq.js'
|
||||
];
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2020 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace NRFramework\Widgets;
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\HTML\HTMLHelper;
|
||||
|
||||
class FacebookVideo extends Video
|
||||
{
|
||||
/**
|
||||
* Widget default options
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $video_widget_options = [
|
||||
// Whether we allow fullscreen
|
||||
'fs' => false,
|
||||
|
||||
// Set to include the text from the Facebook post associated with the video, if any. Only available for desktop sites.
|
||||
'show_text' => false,
|
||||
|
||||
// Set to show captions (if available) by default. Captions are only available on desktop.
|
||||
'show_captions' => false,
|
||||
];
|
||||
|
||||
/**
|
||||
* We use the video widget layout file.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'video';
|
||||
}
|
||||
|
||||
protected function prepare()
|
||||
{
|
||||
$videoDetails = \NRFramework\Helpers\Video::getDetails($this->options['value']);
|
||||
$videoProvider = isset($videoDetails['provider']) ? $videoDetails['provider'] : '';
|
||||
|
||||
// Abort
|
||||
if ($videoProvider !== 'facebookvideo')
|
||||
{
|
||||
$this->options['value'] = null;
|
||||
return;
|
||||
}
|
||||
|
||||
$this->options['css_class'] .= ' facebookvideo';
|
||||
|
||||
$videoID = isset($videoDetails['id']) ? $videoDetails['id'] : '';
|
||||
|
||||
$atts = [
|
||||
'data-video-id="' . $videoID . '"',
|
||||
'data-video-type="' . $videoProvider . '"',
|
||||
'data-video-width="auto"',
|
||||
'data-video-show-text="' . var_export($this->options['show_text'], true) . '"',
|
||||
'data-video-show-captions="' . var_export($this->options['show_captions'], true) . '"',
|
||||
'data-video-fs="' . var_export($this->options['fs'], true) . '"',
|
||||
'data-video-autopause="' . var_export($this->options['autopause'], true) . '"',
|
||||
'data-video-autoplay="' . var_export($this->options['autoplay'], true) . '"'
|
||||
];
|
||||
|
||||
$this->options['atts'] = implode(' ', $atts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads media files
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function videoAssets()
|
||||
{
|
||||
HTMLHelper::script('plg_system_nrframework/widgets/video/facebookvideo.js', ['relative' => true, 'version' => 'auto']);
|
||||
}
|
||||
}
|
||||
1095
plugins/system/nrframework/NRFramework/Widgets/Gallery.php
Normal file
1095
plugins/system/nrframework/NRFramework/Widgets/Gallery.php
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,609 @@
|
||||
<?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\Widgets;
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\Registry\Registry;
|
||||
use Joomla\CMS\Helper\TagsHelper;
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Uri\Uri;
|
||||
use NRFramework\Helpers\Widgets\GalleryManager as GalleryManagerHelper;
|
||||
use NRFramework\Image;
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\Filesystem\File;
|
||||
|
||||
/**
|
||||
* Gallery Manager
|
||||
*/
|
||||
class GalleryManager extends Widget
|
||||
{
|
||||
/**
|
||||
* Widget default options
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $widget_options = [
|
||||
// The input name
|
||||
'name' => '',
|
||||
|
||||
// Context of the field
|
||||
// module, default
|
||||
'context' => 'default',
|
||||
|
||||
// The field ID associated to this Gallery Manager, used to retrieve the field settings on AJAX actions
|
||||
'field_id' => null,
|
||||
|
||||
// The item ID associated to this Gallery Manager, used to retrieve the field settings on AJAX actions
|
||||
'item_id' => null,
|
||||
|
||||
/**
|
||||
* Max file size in MB.
|
||||
*
|
||||
* Defults to 0 (no limit).
|
||||
*/
|
||||
'max_file_size' => 0,
|
||||
|
||||
/**
|
||||
* How many files we can upload.
|
||||
*
|
||||
* Defaults to 0 (no limit).
|
||||
*/
|
||||
'limit_files' => 0,
|
||||
|
||||
// Allowed upload file types
|
||||
'allowed_file_types' => '.jpg, .jpeg, .png, .gif, .webp, image/webp',
|
||||
|
||||
/**
|
||||
* Original Image
|
||||
*/
|
||||
// Original image resize width
|
||||
'original_image_resize_width' => null,
|
||||
|
||||
// Original image resize height
|
||||
'original_image_resize_height' => null,
|
||||
|
||||
/**
|
||||
* Thumbnails
|
||||
*/
|
||||
// Thumbnails width
|
||||
'thumb_width' => null,
|
||||
|
||||
// Thumbnails height
|
||||
'thumb_height' => null,
|
||||
|
||||
// Thumbnails resize method (crop, stretch, fit)
|
||||
'thumb_resize_method' => 'crop',
|
||||
|
||||
// The list of tags already available for this gallery
|
||||
'tags' => [],
|
||||
|
||||
// Open AI API Key
|
||||
'openai_api_key' => '',
|
||||
|
||||
// The widget name
|
||||
'widget' => 'GalleryManager'
|
||||
];
|
||||
|
||||
public function __construct($options = [])
|
||||
{
|
||||
parent::__construct($options);
|
||||
|
||||
$this->prepare();
|
||||
}
|
||||
|
||||
private function prepare()
|
||||
{
|
||||
// Set gallery items
|
||||
$this->options['gallery_items'] = is_array($this->options['value']) ? $this->options['value'] : [];
|
||||
|
||||
// Set css class for readonly state
|
||||
if ($this->options['readonly'])
|
||||
{
|
||||
$this->options['css_class'] .= ' readonly';
|
||||
}
|
||||
|
||||
// Adds a css class when the gallery contains at least one item
|
||||
if (count($this->options['gallery_items']))
|
||||
{
|
||||
$this->options['css_class'] .= ' dz-has-items';
|
||||
}
|
||||
|
||||
// Get the Open AI API key
|
||||
$this->options['openai_api_key'] = \NRFramework\Helpers\Settings::getValue('openai_api_key');
|
||||
|
||||
// Load translation strings
|
||||
Text::script('NR_GALLERY_MANAGER_CONFIRM_REGENERATE_IMAGES');
|
||||
Text::script('NR_GALLERY_MANAGER_CONFIRM_DELETE_ALL_SELECTED');
|
||||
Text::script('NR_GALLERY_MANAGER_CONFIRM_DELETE_ALL');
|
||||
Text::script('NR_GALLERY_MANAGER_CONFIRM_DELETE');
|
||||
Text::script('NR_GALLERY_MANAGER_FILE_MISSING');
|
||||
Text::script('NR_GALLERY_MANAGER_REACHED_FILES_LIMIT');
|
||||
Text::script('NR_GENERATE_IMAGE_DESC_TO_ALL_IMAGES_CONFIRM');
|
||||
|
||||
$this->prepareTags();
|
||||
}
|
||||
|
||||
private function prepareTags()
|
||||
{
|
||||
if (!is_array($this->options['gallery_items']))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$db = Factory::getDbo();
|
||||
$query = $db->getQuery(true)
|
||||
->select([$db->quoteName('id'), $db->quoteName('title')])
|
||||
->from($db->quoteName('#__tags'))
|
||||
->where($db->quoteName('published') . ' = 1')
|
||||
->where($db->quoteName('level') . ' > 0');
|
||||
|
||||
$db->setQuery($query);
|
||||
$tags = $db->loadAssocList('id', 'title');
|
||||
|
||||
$this->options['tags'] = $tags;
|
||||
}
|
||||
|
||||
private function getSettings($context)
|
||||
{
|
||||
// Make sure we have a valid context
|
||||
if (!$context)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$field_data = [];
|
||||
|
||||
$input = Factory::getApplication()->input;
|
||||
|
||||
if ($context === 'default')
|
||||
{
|
||||
// Make sure we have a valid field id
|
||||
if (!$field_id = $input->getInt('field_id'))
|
||||
{
|
||||
$this->exitWithMessage('NR_GALLERY_MANAGER_FIELD_ID_ERROR');
|
||||
}
|
||||
|
||||
if (!$field_data = \NRFramework\Helpers\CustomField::getData($field_id))
|
||||
{
|
||||
$this->exitWithMessage('NR_GALLERY_MANAGER_INVALID_FIELD_DATA');
|
||||
}
|
||||
}
|
||||
else if ($context === 'module')
|
||||
{
|
||||
// Make sure we have a valid item id
|
||||
if (!$item_id = $input->getInt('item_id'))
|
||||
{
|
||||
$this->exitWithMessage('NR_GALLERY_MANAGER_ITEM_ID_ERROR');
|
||||
}
|
||||
|
||||
if (!$field_data = \NRFramework\Helpers\Module::getData($item_id))
|
||||
{
|
||||
$this->exitWithMessage('NR_GALLERY_MANAGER_INVALID_FIELD_DATA');
|
||||
}
|
||||
|
||||
$field_data->set('style', $field_data->get('provider', 'grid'));
|
||||
}
|
||||
|
||||
return $field_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* The upload task called by the AJAX hanler
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function ajax_upload()
|
||||
{
|
||||
$input = Factory::getApplication()->input;
|
||||
|
||||
// Make sure we have a valid context
|
||||
if (!$context = $input->get('context'))
|
||||
{
|
||||
$this->exitWithMessage('NR_GALLERY_MANAGER_CONTEXT_ERROR');
|
||||
}
|
||||
|
||||
// Make sure we have a valid file passed
|
||||
if (!$file = $input->files->get('file'))
|
||||
{
|
||||
$this->exitWithMessage('NR_GALLERY_MANAGER_ERROR_INVALID_FILE');
|
||||
}
|
||||
|
||||
if (!$field_data = $this->getSettings($context))
|
||||
{
|
||||
$this->exitWithMessage('NR_GALLERY_MANAGER_INVALID_FIELD_DATA');
|
||||
}
|
||||
|
||||
// get the media uploader file data, values are passed when we upload a file using the Media Uploader
|
||||
$media_uploader_file_data = [
|
||||
'is_media_uploader_file' => $input->get('media_uploader', false) == '1',
|
||||
'media_uploader_filename' => $input->getString('media_uploader_filename', '')
|
||||
];
|
||||
|
||||
// In case we allow multiple uploads the file parameter is a 2 levels array.
|
||||
$first_property = array_pop($file);
|
||||
if (is_array($first_property))
|
||||
{
|
||||
$file = $first_property;
|
||||
}
|
||||
|
||||
$style = $field_data->get('style', 'grid');
|
||||
|
||||
$uploadSettings = [
|
||||
'allow_unsafe' => false,
|
||||
'allowed_types' => $field_data->get('allowed_file_types', $this->widget_options['allowed_file_types']),
|
||||
'style' => $style
|
||||
];
|
||||
|
||||
// Add watermark
|
||||
if ($field_data->get('watermark.type', 'disabled') !== 'disabled')
|
||||
{
|
||||
$uploadSettings['watermark'] = (array) $field_data->get('watermark', []);
|
||||
$uploadSettings['watermark']['image'] = !empty($uploadSettings['watermark']['image']) ? explode('#', JPATH_SITE . DIRECTORY_SEPARATOR . $uploadSettings['watermark']['image'])[0] : null;
|
||||
$uploadSettings['watermark']['apply_on_thumbnails'] = $field_data->get('watermark.apply_on_thumbnails', false) === '1';
|
||||
}
|
||||
|
||||
$field_data_array = $field_data->toArray();
|
||||
|
||||
$resize_method = $field_data->get('resize_method', 'crop');
|
||||
$thumb_height = $field_data->get('thumb_height', null);
|
||||
|
||||
switch ($style)
|
||||
{
|
||||
case 'slideshow':
|
||||
if (isset($field_data_array['slideshow_thumb_height']))
|
||||
{
|
||||
$thumb_height = $field_data_array['slideshow_thumb_height'];
|
||||
}
|
||||
|
||||
if ($slideshow_resize_method = $field_data->get('slideshow_resize_method'))
|
||||
{
|
||||
$resize_method = $slideshow_resize_method;
|
||||
}
|
||||
break;
|
||||
case 'masonry':
|
||||
$thumb_height = null;
|
||||
break;
|
||||
case 'zjustified':
|
||||
case 'justified':
|
||||
$thumb_height = $field_data->get('justified_item_height', 200);
|
||||
break;
|
||||
}
|
||||
|
||||
// resize image settings
|
||||
$resizeSettings = [
|
||||
'thumb_height' => $thumb_height,
|
||||
'thumb_resize_method' => $resize_method,
|
||||
|
||||
// TODO: Remove this line when ACF is also updated, so we don't rely on this to resize the original image
|
||||
'original_image_resize' => false,
|
||||
|
||||
'original_image_resize_width' => $field_data->get('original_image_resize_width'),
|
||||
'original_image_resize_height' => $field_data->get('original_image_resize_height')
|
||||
];
|
||||
|
||||
/**
|
||||
* For backwards compatibility.
|
||||
*
|
||||
* TODO: Update this code block to not rely on "original_image_resize" to resize original image when removed from ACF.
|
||||
*/
|
||||
$resize_original_image_setting_value = $field_data->get('original_image_resize', null);
|
||||
if ($style === 'slideshow' && ($resizeSettings['original_image_resize_width'] || $resizeSettings['original_image_resize_height']))
|
||||
{
|
||||
$resize_original_image_setting_value = true;
|
||||
}
|
||||
|
||||
if ($resize_original_image_setting_value)
|
||||
{
|
||||
$resizeSettings['original_image_resize_height'] = $style === 'slideshow' ? $resizeSettings['original_image_resize_height'] : null;
|
||||
$resizeSettings['original_image_resize'] = $style === 'slideshow' ? true : $resize_original_image_setting_value;
|
||||
}
|
||||
else if (is_null($resize_original_image_setting_value) && ($resizeSettings['original_image_resize_width'] || $resizeSettings['original_image_resize_height']))
|
||||
{
|
||||
$resizeSettings['original_image_resize'] = true;
|
||||
}
|
||||
if (!$resizeSettings['original_image_resize'])
|
||||
{
|
||||
$resizeSettings['original_image_resize_width'] = null;
|
||||
$resizeSettings['original_image_resize_height'] = null;
|
||||
}
|
||||
|
||||
if (in_array($style, ['grid', 'masonry', 'slideshow']))
|
||||
{
|
||||
$resizeSettings['thumb_width'] = $field_data->get('thumb_width');
|
||||
|
||||
$slideshow_thumb_width = $field_data->get('slideshow_thumb_width');
|
||||
if (!is_null($slideshow_thumb_width) && $style === 'slideshow')
|
||||
{
|
||||
$resizeSettings['thumb_width'] = $slideshow_thumb_width;
|
||||
}
|
||||
}
|
||||
|
||||
// Upload the file and resize the images as required
|
||||
if (!$uploaded_filenames = GalleryManagerHelper::upload($file, $uploadSettings, $media_uploader_file_data, $resizeSettings))
|
||||
{
|
||||
$this->exitWithMessage('NR_GALLERY_MANAGER_ERROR_CANNOT_UPLOAD_FILE');
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'source' => $uploaded_filenames['source'],
|
||||
'original' => $uploaded_filenames['original'],
|
||||
'thumbnail' => $uploaded_filenames['thumbnail'],
|
||||
'is_media_uploader_file' => $media_uploader_file_data['is_media_uploader_file']
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* The delete task called by the AJAX hanlder
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function ajax_delete()
|
||||
{
|
||||
$input = Factory::getApplication()->input;
|
||||
|
||||
// Get source image path.
|
||||
$source = $input->getString('source');
|
||||
|
||||
// Make sure we have a valid file passed
|
||||
if (!$original = $input->getString('original'))
|
||||
{
|
||||
$this->exitWithMessage('NR_GALLERY_MANAGER_ERROR_INVALID_FILE');
|
||||
}
|
||||
|
||||
// Make sure we have a valid file passed
|
||||
if (!$thumbnail = $input->getString('thumbnail'))
|
||||
{
|
||||
$this->exitWithMessage('NR_GALLERY_MANAGER_ERROR_INVALID_FILE');
|
||||
}
|
||||
|
||||
if (!$context = $input->get('context'))
|
||||
{
|
||||
$this->exitWithMessage('NR_GALLERY_MANAGER_CONTEXT_ERROR');
|
||||
}
|
||||
|
||||
if (!$field_data = $this->getSettings($context))
|
||||
{
|
||||
$this->exitWithMessage('NR_GALLERY_MANAGER_INVALID_FIELD_DATA');
|
||||
}
|
||||
|
||||
// Delete the source, original, and thumbnail file
|
||||
$deleted = GalleryManagerHelper::deleteFile($source, $original, $thumbnail);
|
||||
|
||||
echo json_encode(['success' => $deleted]);
|
||||
}
|
||||
|
||||
/**
|
||||
* This task allows us to regenerate the images.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function ajax_regenerate_images()
|
||||
{
|
||||
$input = Factory::getApplication()->input;
|
||||
|
||||
// Make sure we have a valid context
|
||||
if (!$context = $input->get('context'))
|
||||
{
|
||||
echo json_encode(['success' => false, 'message' => Text::_('NR_GALLERY_MANAGER_CONTEXT_ERROR')]);
|
||||
die();
|
||||
}
|
||||
|
||||
if (!$field_data = $this->getSettings($context))
|
||||
{
|
||||
echo json_encode(['success' => false, 'message' => Text::_('NR_GALLERY_MANAGER_INVALID_FIELD_DATA')]);
|
||||
die();
|
||||
}
|
||||
|
||||
$field_id = $input->getInt('field_id');
|
||||
$item_id = $input->getInt('item_id');
|
||||
|
||||
$field_data_array = $field_data->toArray();
|
||||
|
||||
$style = $field_data->get('style', 'grid');
|
||||
|
||||
$resize_method = $field_data->get('resize_method', 'crop');
|
||||
$thumb_height = $field_data->get('thumb_height', null);
|
||||
|
||||
switch ($style)
|
||||
{
|
||||
case 'slideshow':
|
||||
if (isset($field_data_array['slideshow_thumb_height']))
|
||||
{
|
||||
$thumb_height = $field_data_array['slideshow_thumb_height'];
|
||||
}
|
||||
|
||||
if ($slideshow_resize_method = $field_data->get('slideshow_resize_method'))
|
||||
{
|
||||
$resize_method = $slideshow_resize_method;
|
||||
}
|
||||
break;
|
||||
case 'masonry':
|
||||
$thumb_height = null;
|
||||
break;
|
||||
case 'zjustified':
|
||||
case 'justified':
|
||||
$thumb_height = $field_data->get('justified_item_height', 200);
|
||||
break;
|
||||
}
|
||||
|
||||
$resizeSettings = [
|
||||
'thumb_height' => $thumb_height,
|
||||
'thumb_resize_method' => $resize_method
|
||||
];
|
||||
|
||||
if (in_array($style, ['grid', 'masonry', 'slideshow']))
|
||||
{
|
||||
$resizeSettings['thumb_width'] = $field_data->get('thumb_width');
|
||||
|
||||
$slideshow_thumb_width = $field_data->get('slideshow_thumb_width');
|
||||
if (!is_null($slideshow_thumb_width) && $style === 'slideshow')
|
||||
{
|
||||
$resizeSettings['thumb_width'] = $slideshow_thumb_width;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Remove this line when ACF is also updated, so we don't rely on this to resize the original image
|
||||
$original_image_resize = false;
|
||||
|
||||
$original_image_resize_width = $field_data->get('original_image_resize_width');
|
||||
$original_image_resize_height = $field_data->get('original_image_resize_height');
|
||||
|
||||
/**
|
||||
* For backwards compatibility.
|
||||
*
|
||||
* TODO: Update this code block to not rely on "original_image_resize" to resize original image when removed from ACF.
|
||||
*/
|
||||
$resize_original_image_setting_value = $field_data->get('original_image_resize', null);
|
||||
if ($style === 'slideshow' && ($original_image_resize_width || $original_image_resize_height))
|
||||
{
|
||||
$resize_original_image_setting_value = true;
|
||||
}
|
||||
|
||||
if ($resize_original_image_setting_value)
|
||||
{
|
||||
$original_image_resize_height = $style === 'slideshow' ? $original_image_resize_height : null;
|
||||
$original_image_resize = $style === 'slideshow' ? true : $resize_original_image_setting_value;
|
||||
}
|
||||
else if (is_null($resize_original_image_setting_value) && ($original_image_resize_width || $original_image_resize_height))
|
||||
{
|
||||
$original_image_resize = true;
|
||||
}
|
||||
if (!$original_image_resize)
|
||||
{
|
||||
$original_image_resize_width = null;
|
||||
$original_image_resize_height = null;
|
||||
}
|
||||
|
||||
$watermarkSettings = [];
|
||||
// Add watermark
|
||||
if ($field_data->get('watermark.type', 'disabled') !== 'disabled')
|
||||
{
|
||||
$watermarkSettings = (array) $field_data->get('watermark', []);
|
||||
$watermarkSettings['image'] = !empty($watermarkSettings['image']) ? explode('#', JPATH_SITE . DIRECTORY_SEPARATOR . $watermarkSettings['image'])[0] : null;
|
||||
$watermarkSettings['apply_on_thumbnails'] = $field_data->get('watermark.apply_on_thumbnails', false) === '1';
|
||||
}
|
||||
$watermarkEnabled = isset($watermarkSettings['type']) && $watermarkSettings['type'] !== 'disabled';
|
||||
$thumbnailWatermarkEnabled = isset($watermarkSettings['type']) && $watermarkSettings['type'] !== 'disabled' && $watermarkSettings['apply_on_thumbnails'];
|
||||
|
||||
$items = $input->get('items', null, 'ARRAY');
|
||||
$items = json_decode($items[0], true);
|
||||
|
||||
$ds = DIRECTORY_SEPARATOR;
|
||||
|
||||
// Parse all images
|
||||
if (is_array($items) && count($items))
|
||||
{
|
||||
foreach ($items as &$item)
|
||||
{
|
||||
$sourceImage = isset($item['source']) ? $item['source'] : '';
|
||||
$originalImage = isset($item['original']) ? $item['original'] : '';
|
||||
$thumbnailImage = isset($item['thumbnail']) ? $item['thumbnail'] : '';
|
||||
$thumbnailImagePath = implode($ds, [JPATH_ROOT, $thumbnailImage]);
|
||||
|
||||
$sourceImagePath = $sourceImage ? implode($ds, [JPATH_ROOT, $sourceImage]) : false;
|
||||
$sourceImageExists = $sourceImagePath && file_exists($sourceImagePath);
|
||||
$originalImagePath = implode($ds, [JPATH_ROOT, $originalImage]);
|
||||
$originalImageExists = $originalImagePath && file_exists($originalImagePath);
|
||||
|
||||
// If source image does not exist, watermark is enabled, create it by clothing the original image
|
||||
if (!$sourceImageExists && $watermarkEnabled && $originalImage && file_exists($originalImagePath))
|
||||
{
|
||||
// Create source from original image
|
||||
$sourceImagePath = \NRFramework\File::copy($originalImagePath, $originalImagePath, false, true);
|
||||
$sourceImageExists = true;
|
||||
|
||||
// Modify the database entry and add "source" image to item
|
||||
// We just need the relative path to file
|
||||
$_sourceImagePath = str_replace(JPATH_ROOT . DIRECTORY_SEPARATOR, '', $sourceImagePath);
|
||||
$item['source'] = $_sourceImagePath;
|
||||
$_originalImagePath = str_replace(JPATH_ROOT . DIRECTORY_SEPARATOR, '', $originalImagePath);
|
||||
GalleryManagerHelper::setItemFieldSource($item_id, $field_id, $_sourceImagePath, $_originalImagePath);
|
||||
}
|
||||
|
||||
if (!$originalImageExists)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$sourceImageExists)
|
||||
{
|
||||
$sourceImagePath = $originalImagePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle original image.
|
||||
*/
|
||||
// Generate original image by using the source image
|
||||
if ($original_image_resize_width && $original_image_resize_height)
|
||||
{
|
||||
$originalImagePath = Image::resize($sourceImagePath, $original_image_resize_width, $original_image_resize_height, 70, 'crop', $originalImagePath);
|
||||
}
|
||||
else if ($original_image_resize_width)
|
||||
{
|
||||
$originalImagePath = Image::resizeAndKeepAspectRatio($sourceImagePath, $original_image_resize_width, 70, $originalImagePath);
|
||||
}
|
||||
else if ($original_image_resize_height)
|
||||
{
|
||||
$originalImagePath = Image::resizeByHeight($sourceImagePath, $original_image_resize_height, $originalImagePath, 70);
|
||||
}
|
||||
|
||||
$originalImageSourcePath = $originalImagePath;
|
||||
|
||||
if ($watermarkEnabled)
|
||||
{
|
||||
$payload = array_merge($watermarkSettings, ['source' => $sourceImagePath, 'destination' => $originalImagePath]);
|
||||
Image::applyWatermark($payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle thumbnail image.
|
||||
*/
|
||||
// Generate thumbnail image by using the source image
|
||||
GalleryManagerHelper::generateThumbnail($sourceImagePath, $thumbnailImagePath, $resizeSettings, null, false);
|
||||
|
||||
// Apply watermark to thumbnail image
|
||||
if ($watermarkEnabled && $thumbnailWatermarkEnabled)
|
||||
{
|
||||
$payload = array_merge($watermarkSettings, ['source' => $thumbnailImagePath]);
|
||||
Image::applyWatermark($payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode(['success' => true, 'message' => Text::_('NR_GALLERY_MANAGER_IMAGES_REGENERATED'), 'items' => $items]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exits the page with given message.
|
||||
*
|
||||
* @param string $translation_string
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function exitWithMessage($translation_string)
|
||||
{
|
||||
http_response_code('500');
|
||||
die(Text::_($translation_string));
|
||||
}
|
||||
|
||||
public function ajax_generate_caption()
|
||||
{
|
||||
set_time_limit(300); // 5 Minutes
|
||||
ini_set('memory_limit', '-1');
|
||||
|
||||
$fullURL = Uri::root() . Factory::getApplication()->input->getString('image');
|
||||
|
||||
$imageToText = new \NRFramework\AI\TextGeneration\ImageToText();
|
||||
$generated = $imageToText->generate($fullURL);
|
||||
|
||||
echo json_encode($generated);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,320 @@
|
||||
<?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\Widgets;
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\Registry\Registry;
|
||||
use Joomla\CMS\Helper\TagsHelper;
|
||||
use Joomla\CMS\Factory;
|
||||
use NRFramework\Helpers\Widgets\GalleryManager2 as GalleryManagerHelper;
|
||||
use NRFramework\Image;
|
||||
use Joomla\CMS\Uri\Uri;
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\Filesystem\File;
|
||||
use Joomla\Filesystem\Folder;
|
||||
|
||||
/**
|
||||
* Gallery Manager
|
||||
*/
|
||||
class GalleryManager2 extends Widget
|
||||
{
|
||||
/**
|
||||
* Widget default options
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $widget_options = [
|
||||
// The uploaded images
|
||||
'value' => [],
|
||||
|
||||
// The input name
|
||||
'name' => '',
|
||||
|
||||
// Context of the field
|
||||
// module, default
|
||||
'context' => 'default',
|
||||
|
||||
// The field ID associated to this Gallery Manager, used to retrieve the field settings on AJAX actions
|
||||
'field_id' => null,
|
||||
|
||||
// The item ID associated to this Gallery Manager, used to retrieve the field settings on AJAX actions
|
||||
'item_id' => null,
|
||||
|
||||
/**
|
||||
* Max file size in MB.
|
||||
*
|
||||
* Defults to 0 (no limit).
|
||||
*/
|
||||
'max_file_size' => 0,
|
||||
|
||||
/**
|
||||
* How many files we can upload.
|
||||
*
|
||||
* Defaults to 0 (no limit).
|
||||
*/
|
||||
'limit_files' => 0,
|
||||
|
||||
// Allowed upload file types
|
||||
'allowed_file_types' => '.jpg, .jpeg, .png, .webp, image/*',
|
||||
|
||||
/**
|
||||
* Original Image
|
||||
*/
|
||||
// Original image resize width
|
||||
'original_image_resize_width' => null,
|
||||
|
||||
// Original image resize height
|
||||
'original_image_resize_height' => null,
|
||||
|
||||
/**
|
||||
* Thumbnails
|
||||
*/
|
||||
// Thumbnails width
|
||||
'thumb_width' => null,
|
||||
|
||||
// Thumbnails height
|
||||
'thumb_height' => null,
|
||||
|
||||
// Thumbnails resize method (crop, stretch, fit)
|
||||
'thumb_resize_method' => 'crop',
|
||||
|
||||
// The list of tags already available for this gallery
|
||||
'tags' => [],
|
||||
|
||||
// Open AI API Key
|
||||
'openai_api_key' => '',
|
||||
|
||||
// The widget name
|
||||
'widget' => 'GalleryManager2'
|
||||
];
|
||||
|
||||
public function __construct($options = [])
|
||||
{
|
||||
parent::__construct($options);
|
||||
|
||||
$this->prepare();
|
||||
}
|
||||
|
||||
private function prepare()
|
||||
{
|
||||
$this->includeTempFiles();
|
||||
|
||||
// Set css class for readonly state
|
||||
if ($this->options['readonly'])
|
||||
{
|
||||
$this->options['css_class'] .= ' readonly';
|
||||
}
|
||||
|
||||
// Adds a css class when the gallery contains at least one item
|
||||
if (is_array($this->options['value']) && count($this->options['value']))
|
||||
{
|
||||
$this->options['css_class'] .= ' dz-has-items';
|
||||
}
|
||||
|
||||
// Get the Open AI API key
|
||||
$this->options['openai_api_key'] = \NRFramework\Helpers\Settings::getValue('openai_api_key');
|
||||
|
||||
// Load translation strings
|
||||
Text::script('NR_GALLERY_MANAGER_CONFIRM_DELETE_ALL_SELECTED');
|
||||
Text::script('NR_GALLERY_MANAGER_CONFIRM_DELETE_ALL');
|
||||
Text::script('NR_GALLERY_MANAGER_CONFIRM_DELETE');
|
||||
Text::script('NR_GALLERY_MANAGER_FILE_MISSING');
|
||||
Text::script('NR_GALLERY_MANAGER_REACHED_FILES_LIMIT');
|
||||
Text::script('NR_GENERATE_IMAGE_DESC_TO_ALL_IMAGES_CONFIRM');
|
||||
|
||||
$this->prepareTags();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find and include temp files in the gallery.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function includeTempFiles()
|
||||
{
|
||||
$ds = DIRECTORY_SEPARATOR;
|
||||
|
||||
$tempFolder = GalleryManagerHelper::getFullTempFolder($this->options['context'], $this->options['field_id'], $this->options['item_id']);
|
||||
|
||||
if (!is_dir($tempFolder))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$files = Folder::files($tempFolder, '.', false, false, ['.', '..', 'index.html', 'index.php']);
|
||||
|
||||
if (!$files)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$relativeTempFolder = ltrim(str_replace(JPATH_ROOT, '', $tempFolder), $ds);
|
||||
|
||||
foreach ($files as $filename)
|
||||
{
|
||||
$this->options['value'][] = [
|
||||
'source' => implode($ds, [$relativeTempFolder, $filename]),
|
||||
'original' =>'',
|
||||
'exists' => true,
|
||||
'caption' => '',
|
||||
'thumbnail' => '',
|
||||
'slideshow' => '',
|
||||
'alt' => '',
|
||||
'tags' => json_encode([]),
|
||||
'temp' => true
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
private function prepareTags()
|
||||
{
|
||||
if (!is_array($this->options['value']))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$db = Factory::getDbo();
|
||||
$query = $db->getQuery(true)
|
||||
->select([$db->quoteName('id'), $db->quoteName('title')])
|
||||
->from($db->quoteName('#__tags'))
|
||||
->where($db->quoteName('published') . ' = 1')
|
||||
->where($db->quoteName('level') . ' > 0');
|
||||
|
||||
$db->setQuery($query);
|
||||
$tags = $db->loadAssocList('id', 'title');
|
||||
|
||||
$this->options['tags'] = $tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* The upload task called by the AJAX hanler
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function ajax_upload()
|
||||
{
|
||||
// Increase memory size and execution time to prevent PHP errors on datasets > 20K
|
||||
set_time_limit(300); // 5 Minutes
|
||||
ini_set('memory_limit', '-1');
|
||||
|
||||
$input = Factory::getApplication()->input;
|
||||
|
||||
$random_suffix = $input->get('random_suffix', 'false') === 'true' ? true : false;
|
||||
|
||||
// Make sure we have a valid context
|
||||
if (!$context = $input->get('context'))
|
||||
{
|
||||
$this->exitWithMessage('NR_GALLERY_MANAGER_CONTEXT_ERROR');
|
||||
}
|
||||
|
||||
// Make sure we have a valid file passed
|
||||
if (!$file = $input->files->get('file'))
|
||||
{
|
||||
$this->exitWithMessage('NR_GALLERY_MANAGER_ERROR_INVALID_FILE');
|
||||
}
|
||||
|
||||
// In case we allow multiple uploads the file parameter is a 2 levels array.
|
||||
$first_property = array_pop($file);
|
||||
if (is_array($first_property))
|
||||
{
|
||||
$file = $first_property;
|
||||
}
|
||||
|
||||
$uploadSettings = [
|
||||
'context' => $context,
|
||||
'field_id' => $input->getInt('field_id'),
|
||||
'item_id' => $input->getInt('item_id'),
|
||||
'allow_unsafe' => false,
|
||||
'allowed_types' => $this->widget_options['allowed_file_types'],
|
||||
'random_suffix' => $random_suffix
|
||||
];
|
||||
|
||||
// Upload the file and resize the images as required
|
||||
if (!$source = GalleryManagerHelper::upload($file, $uploadSettings))
|
||||
{
|
||||
$this->exitWithMessage('NR_GALLERY_MANAGER_ERROR_CANNOT_UPLOAD_FILE');
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'source' => $source
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* The delete task called by the AJAX hanlder
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function ajax_delete()
|
||||
{
|
||||
// Increase memory size and execution time to prevent PHP errors on datasets > 20K
|
||||
set_time_limit(300); // 5 Minutes
|
||||
ini_set('memory_limit', '-1');
|
||||
|
||||
$input = Factory::getApplication()->input;
|
||||
|
||||
// Get source image path.
|
||||
$source = $input->getString('source');
|
||||
|
||||
// Get the slideshow image path.
|
||||
$slideshow = $input->getString('slideshow', '');
|
||||
|
||||
// Get the original image
|
||||
$original = $input->getString('original');
|
||||
|
||||
// Get the thumbnail image
|
||||
$thumbnail = $input->getString('thumbnail');
|
||||
|
||||
if (!$context = $input->get('context'))
|
||||
{
|
||||
$this->exitWithMessage('NR_GALLERY_MANAGER_CONTEXT_ERROR');
|
||||
}
|
||||
|
||||
$field_id = $input->getInt('field_id');
|
||||
$item_id = $input->getInt('item_id');
|
||||
|
||||
if (!$field_data = GalleryManagerHelper::getSettings($context, $field_id, $item_id))
|
||||
{
|
||||
$this->exitWithMessage('NR_GALLERY_MANAGER_INVALID_FIELD_DATA');
|
||||
}
|
||||
|
||||
// Delete the source, original, and thumbnail file
|
||||
$deleted = GalleryManagerHelper::deleteFile($source, $slideshow, $original, $thumbnail);
|
||||
|
||||
echo json_encode(['success' => $deleted]);
|
||||
}
|
||||
|
||||
public function ajax_generate_caption()
|
||||
{
|
||||
set_time_limit(300); // 5 Minutes
|
||||
ini_set('memory_limit', '-1');
|
||||
|
||||
$fullURL = Uri::root() . Factory::getApplication()->input->getString('image');
|
||||
|
||||
$imageToText = new \NRFramework\AI\TextGeneration\ImageToText();
|
||||
$generated = $imageToText->generate($fullURL);
|
||||
|
||||
echo json_encode($generated);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exits the page with given message.
|
||||
*
|
||||
* @param string $translation_string
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function exitWithMessage($translation_string)
|
||||
{
|
||||
http_response_code('500');
|
||||
die(Text::_($translation_string));
|
||||
}
|
||||
}
|
||||
30
plugins/system/nrframework/NRFramework/Widgets/GoogleMap.php
Normal file
30
plugins/system/nrframework/NRFramework/Widgets/GoogleMap.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?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\Widgets;
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\HTML\HTMLHelper;
|
||||
|
||||
class GoogleMap extends Map
|
||||
{
|
||||
/**
|
||||
* Loads media files
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function loadMedia()
|
||||
{
|
||||
parent::loadMedia();
|
||||
|
||||
HTMLHelper::script('plg_system_nrframework/widgets/googlemap.js', ['relative' => true, 'version' => 'auto']);
|
||||
HTMLHelper::script('https://maps.googleapis.com/maps/api/js?callback=Function.prototype&key=' . $this->options['provider_key'], ['relative' => false, 'version' => false]);
|
||||
}
|
||||
}
|
||||
125
plugins/system/nrframework/NRFramework/Widgets/Helper.php
Normal file
125
plugins/system/nrframework/NRFramework/Widgets/Helper.php
Normal file
@ -0,0 +1,125 @@
|
||||
<?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\Widgets;
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\Filesystem\Folder;
|
||||
|
||||
class Helper
|
||||
{
|
||||
/**
|
||||
* This is a map with all widgets used for caching the widget's class name
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $widgets_map = [];
|
||||
|
||||
/**
|
||||
* Renders a Widget and returns
|
||||
*
|
||||
* @param array $options A list of attributes passed to the layout
|
||||
*
|
||||
* @return string The widget's final HTML layout
|
||||
*/
|
||||
public static function render($widget_name, $options = [])
|
||||
{
|
||||
if (!$widgetClass = self::find($widget_name))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$class = __NAMESPACE__ . '\\' . $widgetClass;
|
||||
|
||||
// ensure class exists
|
||||
if (!class_exists($class))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
return (new $class($options))->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the real class name of a widget by a case-insensitive name.
|
||||
*
|
||||
* @param string $name The widget's name
|
||||
*
|
||||
* @return mixed Null when the class name is not found, string when the class name is found.
|
||||
*/
|
||||
public static function find($name)
|
||||
{
|
||||
if (!$name)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$name = strtolower($name);
|
||||
|
||||
if (empty(self::$widgets_map) || !isset(self::$widgets_map[$name]))
|
||||
{
|
||||
$widgetClasses = Folder::files(__DIR__);
|
||||
foreach ($widgetClasses as $widgetClass)
|
||||
{
|
||||
$widgetClass = str_replace('.php', '', $widgetClass);
|
||||
|
||||
self::$widgets_map[strtolower($widgetClass)] = $widgetClass;
|
||||
}
|
||||
}
|
||||
|
||||
return isset(self::$widgets_map[$name]) ? self::$widgets_map[$name] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all layout overrides of a widget by its name.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getLayoutOverrides($name = '')
|
||||
{
|
||||
if (!$name)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$path = self::getLayoutOverridePath($name);
|
||||
|
||||
if (!is_dir($path))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$labels = array_diff(scandir($path), ['.', '..', '.DS_Store']);
|
||||
$values = array_map(function($value) {
|
||||
return rtrim($value, '.php');
|
||||
}, $labels);
|
||||
|
||||
return array_combine($values, $labels);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the layout override path of a widget by its name.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getLayoutOverridePath($name = '')
|
||||
{
|
||||
if (!$name)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
return implode(DIRECTORY_SEPARATOR, [JPATH_SITE, 'templates', \NRFramework\Helpers\Template::getTemplateName(), 'html', 'tassos', 'widgets', $name]);
|
||||
}
|
||||
}
|
||||
278
plugins/system/nrframework/NRFramework/Widgets/Map.php
Normal file
278
plugins/system/nrframework/NRFramework/Widgets/Map.php
Normal file
@ -0,0 +1,278 @@
|
||||
<?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\Widgets;
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\CMS\Uri\Uri;
|
||||
use Joomla\CMS\HTML\HTMLHelper;
|
||||
use Joomla\CMS\Router\Route;
|
||||
|
||||
abstract class Map extends Widget
|
||||
{
|
||||
/**
|
||||
* Widget default options
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $widget_options = [
|
||||
/**
|
||||
* The value of the widget.
|
||||
* Format: latitude,longitude
|
||||
*
|
||||
* i.e. 36.891319,27.283480
|
||||
*
|
||||
* Otherwise, set the markers property
|
||||
*/
|
||||
'value' => '',
|
||||
|
||||
// Map tile provider key (if needed) to use the provider tiles
|
||||
'provider_key' => null,
|
||||
|
||||
// Default map width
|
||||
'width' => 500,
|
||||
|
||||
// Default map height
|
||||
'height' => 400,
|
||||
|
||||
/**
|
||||
* The Zoom Level.
|
||||
*
|
||||
* - preset: Set a fixed zoom.
|
||||
* - fitbounds: Allow the map provider to auto-zoom and center the map around the markers.
|
||||
*/
|
||||
'zoom_level' => 'preset',
|
||||
|
||||
// Default map zoon
|
||||
'zoom' => 4,
|
||||
|
||||
// Define lat,long format which will be used to center the map when zoom_level=preset is used.
|
||||
'map_center' => null,
|
||||
|
||||
// Map scale. Values: metric, imperial, false
|
||||
'scale' => false,
|
||||
|
||||
// View mode of the map.
|
||||
'view' => '',
|
||||
|
||||
/**
|
||||
* Set whether to show or not the map marker info window.
|
||||
*
|
||||
* Defaults to the map marker address (if not empty).
|
||||
* If a map makrer label and/or description is set, these will be used.
|
||||
*/
|
||||
'enable_info_window' => true,
|
||||
|
||||
/**
|
||||
* Map Marker
|
||||
*/
|
||||
/**
|
||||
* The markers.
|
||||
*
|
||||
* An array of markers.
|
||||
*
|
||||
* [
|
||||
* [
|
||||
* latitude: 36.891319,
|
||||
* longitude: 27.283480,
|
||||
* label: 'Marker label',
|
||||
* description: 'Marker description'
|
||||
* ]
|
||||
* ]
|
||||
*/
|
||||
'markers' => [],
|
||||
|
||||
// Marker image relative to Joomla installation
|
||||
'markerImage' => '',
|
||||
|
||||
// Set whether to replace marker label/description smart tags
|
||||
'replaceSmartTags' => false
|
||||
];
|
||||
|
||||
public function __construct($options = [])
|
||||
{
|
||||
parent::__construct($options);
|
||||
|
||||
$this->prepare();
|
||||
}
|
||||
|
||||
private function prepare()
|
||||
{
|
||||
$this->options['markerImage'] = $this->options['markerImage'] ? Uri::root() . ltrim($this->options['markerImage'], DIRECTORY_SEPARATOR) : '';
|
||||
|
||||
// Set the marker if a single value was given
|
||||
if ($this->options['value'] && empty($this->options['markers']))
|
||||
{
|
||||
$coords = array_filter(array_map('trim', explode(',', $this->options['value'])));
|
||||
if (count($coords) === 2)
|
||||
{
|
||||
$this->options['markers'] = [
|
||||
[
|
||||
'id' => 1,
|
||||
'latitude' => $coords[0],
|
||||
'longitude' => $coords[1]
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Make markers an array if a JSON string was given
|
||||
if (is_string($this->options['markers']))
|
||||
{
|
||||
$this->options['markers'] = json_decode($this->options['markers'], true);
|
||||
}
|
||||
|
||||
// Set as value the first marker so the JS library can have an initial center of the map
|
||||
if (is_array($this->options['markers']))
|
||||
{
|
||||
$latitude = isset($this->options['markers'][0]['latitude']) ? $this->options['markers'][0]['latitude'] : false;
|
||||
$longitude = isset($this->options['markers'][0]['longitude']) ? $this->options['markers'][0]['longitude'] : false;
|
||||
|
||||
if ($latitude && $longitude)
|
||||
{
|
||||
$this->options['value'] = implode(',', [$latitude, $longitude]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->options['load_css_vars'])
|
||||
{
|
||||
$this->options['custom_css'] = $this->getWidgetCSS();
|
||||
}
|
||||
|
||||
// Transform title/description translation strings
|
||||
$this->prepareMarkerText();
|
||||
}
|
||||
|
||||
private function prepareMarkerText()
|
||||
{
|
||||
if (!is_array($this->options['markers']) || !count($this->options['markers']))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$st = new \NRFramework\SmartTags();
|
||||
|
||||
foreach ($this->options['markers'] as &$marker)
|
||||
{
|
||||
if (isset($marker['label']) && $marker['label'])
|
||||
{
|
||||
$marker['label'] = Text::_($marker['label']);
|
||||
|
||||
// Replace Smart tags
|
||||
if ($this->options['replaceSmartTags'])
|
||||
{
|
||||
$marker['label'] = $st->replace($marker['label']);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($marker['description']) && $marker['description'])
|
||||
{
|
||||
$marker['description'] = Text::_($marker['description']);
|
||||
$marker['description'] = \Joomla\CMS\HTML\HTMLHelper::_('content.prepare', $marker['description'], null, 'tassos.widget.map');
|
||||
|
||||
// Replace Smart tags
|
||||
if ($this->options['replaceSmartTags'])
|
||||
{
|
||||
$marker['description'] = $st->replace($marker['description']);
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($marker['label']) && isset($marker['address']))
|
||||
{
|
||||
$marker['label'] = $marker['address'];
|
||||
}
|
||||
|
||||
// Link to the item
|
||||
if (!empty($marker['label']) && isset($marker['item_id']) && $marker['item_id'] && isset($marker['context']) && $marker['context'] && $marker['context'] !== 'com_users.user')
|
||||
{
|
||||
$context = explode('.', $marker['context']);
|
||||
|
||||
$routerHelper = $routerMethod = null;
|
||||
|
||||
// Content
|
||||
if ($marker['context'] === 'com_content.article')
|
||||
{
|
||||
$routerHelper = defined('nrJ4') ? 'Joomla\Component\Content\Site\Helper\RouteHelper' : 'ContentHelperRoute';
|
||||
$routerMethod = 'getArticleRoute';
|
||||
}
|
||||
// Contact
|
||||
else
|
||||
{
|
||||
if (!defined('nrJ4'))
|
||||
{
|
||||
\JLoader::register('ContactHelperRoute', JPATH_SITE . '/components/com_contact/helpers/route.php');
|
||||
}
|
||||
$routerHelper = defined('nrJ4') ? 'Joomla\Component\Contact\Site\Helper\RouteHelper' : 'ContactHelperRoute';
|
||||
$routerMethod = 'getContactRoute';
|
||||
}
|
||||
|
||||
$url = Route::_($routerHelper::$routerMethod($marker['item_id'], $marker['cat_id'], $marker['language']));
|
||||
|
||||
$marker['label'] = '<a href="' . $url . '">' . $marker['label'] . '</a>';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the CSS for the widget.
|
||||
*
|
||||
* @param array $exclude_breakpoints Define breakpoints to exclude their CSS
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getWidgetCSS($exclude_breakpoints = [])
|
||||
{
|
||||
$controls = [
|
||||
// CSS Variables
|
||||
[
|
||||
'property' => '--width',
|
||||
'value' => $this->options['width'],
|
||||
'unit' => 'px'
|
||||
],
|
||||
[
|
||||
'property' => '--height',
|
||||
'value' => $this->options['height'],
|
||||
'unit' => 'px'
|
||||
],
|
||||
];
|
||||
|
||||
$selector = '.nrf-widget.map-widget.' . $this->options['id'];
|
||||
|
||||
$controlsInstance = new \NRFramework\Controls\Controls(null, $selector, $exclude_breakpoints);
|
||||
|
||||
if (!$controlsCSS = $controlsInstance->generateCSS($controls))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
return $controlsCSS;
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
$this->loadMedia();
|
||||
|
||||
return parent::render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads media files
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function loadMedia()
|
||||
{
|
||||
if ($this->options['load_stylesheet'])
|
||||
{
|
||||
HTMLHelper::stylesheet('plg_system_nrframework/widgets/map.css', ['relative' => true, 'version' => 'auto']);
|
||||
}
|
||||
}
|
||||
}
|
||||
171
plugins/system/nrframework/NRFramework/Widgets/MapAddress.php
Normal file
171
plugins/system/nrframework/NRFramework/Widgets/MapAddress.php
Normal file
@ -0,0 +1,171 @@
|
||||
<?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\Widgets;
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
class MapAddress extends Widget
|
||||
{
|
||||
/**
|
||||
* Widget default options
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $widget_options = [
|
||||
/**
|
||||
* The map coordinates.
|
||||
* Format: latitude,longitude
|
||||
*
|
||||
* i.e. 36.891319,27.283480
|
||||
*/
|
||||
'value' => '',
|
||||
|
||||
/**
|
||||
* Set whether and where to show the map.
|
||||
*
|
||||
* Available values:
|
||||
*
|
||||
* false
|
||||
* backend
|
||||
* frontend
|
||||
* both
|
||||
*/
|
||||
'show_map' => false,
|
||||
|
||||
// The map HTML (If can be rendered)
|
||||
'map' => false,
|
||||
|
||||
// Set what information the user can see.
|
||||
'showAddressDetails' => [
|
||||
'address' => true,
|
||||
'latitude' => false,
|
||||
'longitude' => false,
|
||||
'country' => true,
|
||||
'country_code' => false,
|
||||
'city' => false,
|
||||
'postal_code' => true,
|
||||
'county' => false,
|
||||
'state' => false,
|
||||
'municipality' => false,
|
||||
'town' => false,
|
||||
'road' => false,
|
||||
],
|
||||
|
||||
/**
|
||||
* The address details.
|
||||
*
|
||||
* Supported data:
|
||||
*
|
||||
* address
|
||||
* latitude
|
||||
* longitude
|
||||
* country
|
||||
* country_code
|
||||
* city
|
||||
* postal_code
|
||||
* county
|
||||
* state
|
||||
* municipality
|
||||
* town
|
||||
* road
|
||||
*/
|
||||
'address' => [
|
||||
'address' => '',
|
||||
'latitude' => '',
|
||||
'longitude' => '',
|
||||
'country' => '',
|
||||
'country_code' => '',
|
||||
'city' => '',
|
||||
'postal_code' => '',
|
||||
'county' => '',
|
||||
'state' => '',
|
||||
'municipality' => '',
|
||||
'town' => '',
|
||||
'road' => '',
|
||||
],
|
||||
|
||||
/**
|
||||
* The layout type of the output.
|
||||
*
|
||||
* Available values:
|
||||
*
|
||||
* - default
|
||||
* - custom
|
||||
*/
|
||||
'layout_type' => 'default',
|
||||
|
||||
/**
|
||||
* The custom layout code (HTML + Smart Tags).
|
||||
*
|
||||
* Available Smart Tags:
|
||||
*
|
||||
* Allowed Smart Tags:
|
||||
*
|
||||
* {address.map}
|
||||
* {address.address} - {address.address.label}
|
||||
* {address.latitude} - {address.latitude.label}
|
||||
* {address.longitude} - {address.longitude.label}
|
||||
* {address.country} - {address.country.label}
|
||||
* {address.country_code} - {address.country_code.label}
|
||||
* {address.city} - {address.city.label}
|
||||
* {address.county} - {address.county.label}
|
||||
* {address.postal_code} - {address.postal_code.label}
|
||||
* {address.state} - {address.state.label}
|
||||
* {address.municipality} - {address.municipality.label}
|
||||
* {address.town} - {address.town.label}
|
||||
* {address.road} - {address.road.label}
|
||||
*/
|
||||
'custom_layout' => '{address.address.label}: {address.address}',
|
||||
|
||||
/**
|
||||
* Map location in correlation with the address details.
|
||||
*
|
||||
* Note: This takes effect only if no custom layout is used.
|
||||
*
|
||||
* Available values:
|
||||
*
|
||||
* - above (Above the address details)
|
||||
* - below (Below the address details)
|
||||
*/
|
||||
'map_location' => 'below',
|
||||
|
||||
// The map HTML which will return the map HTML only if a map is set and current layout is not custom
|
||||
'map_html' => ''
|
||||
];
|
||||
|
||||
/**
|
||||
* Renders the widget
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
$this->options['enable_info_window'] = false;
|
||||
|
||||
if (in_array($this->options['show_map'], ['frontend', 'both']) || (in_array($this->options['show_map'], ['frontend', 'both']) && $this->options['layout_type'] === 'custom' && !empty($this->options['custom_layout']) && strpos($this->options['custom_layout'], '{address.map}') !== false))
|
||||
{
|
||||
$this->options['markerImage'] = $this->options['marker_image'];
|
||||
// Get the map
|
||||
$map_options = $this->options;
|
||||
// Remove unneeded props.
|
||||
// The custom layout can include Smart Tags and this breaks the Smart Tags replace method as it tries to replace inner Smart Tags too
|
||||
unset($map_options['custom_layout']);
|
||||
unset($map_options['showAddressDetails']);
|
||||
unset($map_options['address']);
|
||||
$map = new OpenStreetMap($map_options);
|
||||
$map->loadMedia();
|
||||
$this->options['map'] = $map->render();
|
||||
}
|
||||
|
||||
$this->options['map_html'] = in_array($this->options['show_map'], ['frontend', 'both']) && $this->options['layout_type'] !== 'custom' ? $this->options['map'] : '';
|
||||
|
||||
return parent::render();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,177 @@
|
||||
<?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\Widgets;
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\HTML\HTMLHelper;
|
||||
|
||||
class MapAddressEditor extends Widget
|
||||
{
|
||||
/**
|
||||
* Default latitude.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $default_lat = '38.24921060739844';
|
||||
|
||||
/**
|
||||
* Default longitude.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $default_long = '25.314512745029823';
|
||||
|
||||
/**
|
||||
* Widget default options
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $widget_options = [
|
||||
/**
|
||||
* The map coordinates.
|
||||
* Format: latitude,longitude
|
||||
*
|
||||
* i.e. 36.891319,27.283480
|
||||
*/
|
||||
'value' => '0,0',
|
||||
|
||||
/**
|
||||
* Set whether and where to show the map.
|
||||
*
|
||||
* Available values:
|
||||
*
|
||||
* false
|
||||
* backend
|
||||
* frontend
|
||||
* both
|
||||
*/
|
||||
'show_map' => false,
|
||||
|
||||
// The actual map HTML
|
||||
'map' => false,
|
||||
|
||||
// Whether autocomplete is enabled for the address field
|
||||
'autocomplete' => false,
|
||||
|
||||
// Set what information the user can see/edit when selecting an address.
|
||||
'showAddressDetails' => [
|
||||
'address' => false,
|
||||
'latitude' => false,
|
||||
'longitude' => false,
|
||||
'country' => false,
|
||||
'country_code' => false,
|
||||
'city' => false,
|
||||
'postal_code' => false,
|
||||
'county' => false,
|
||||
'state' => false,
|
||||
'municipality' => false,
|
||||
'town' => false,
|
||||
'road' => false,
|
||||
],
|
||||
|
||||
/**
|
||||
* The address details.
|
||||
*
|
||||
* Supported data:
|
||||
*
|
||||
* address
|
||||
* latitude
|
||||
* longitude
|
||||
* country
|
||||
* country_code
|
||||
* city
|
||||
* postal_code
|
||||
* county
|
||||
* state
|
||||
* municipality
|
||||
* town
|
||||
* road
|
||||
*/
|
||||
'address' => [
|
||||
'address' => '',
|
||||
'latitude' => '',
|
||||
'longitude' => '',
|
||||
'country' => '',
|
||||
'country_code' => '',
|
||||
'city' => '',
|
||||
'postal_code' => '',
|
||||
'county' => '',
|
||||
'state' => '',
|
||||
'municipality' => '',
|
||||
'town' => '',
|
||||
'road' => '',
|
||||
],
|
||||
|
||||
/**
|
||||
* Map location in correlation with the address details.
|
||||
*
|
||||
* Note: This takes effect only if no custom layout is used.
|
||||
*
|
||||
* Available values:
|
||||
*
|
||||
* - above (Above the address details)
|
||||
* - below (Below the address details)
|
||||
*/
|
||||
'map_location' => 'below'
|
||||
];
|
||||
|
||||
public function __construct($options = [])
|
||||
{
|
||||
parent::__construct($options);
|
||||
|
||||
if (isset($options['_showAddressDetails']))
|
||||
{
|
||||
$this->options['showAddressDetails'] = array_merge($this->options['showAddressDetails'], $this->options['_showAddressDetails']);
|
||||
}
|
||||
|
||||
if ($options['required'])
|
||||
{
|
||||
$this->options['css_class'] = ' is-required';
|
||||
}
|
||||
|
||||
$this->options['enable_info_window'] = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the widget
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
$this->loadMedia();
|
||||
|
||||
$show_map = in_array($this->options['show_map'], ['backend', 'both']);
|
||||
|
||||
// Get the map editor
|
||||
$map_options = array_merge($this->options, [
|
||||
'show_map' => $show_map,
|
||||
'autocomplete' => $this->options['autocomplete'],
|
||||
'address' => isset($this->options['address']['address']) ? $this->options['address']['address'] : ''
|
||||
]);
|
||||
$map = new MapAddressEditorView($map_options);
|
||||
$map->loadMedia();
|
||||
$this->options['map'] = $map->render();
|
||||
|
||||
return parent::render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads media files
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function loadMedia()
|
||||
{
|
||||
HTMLHelper::stylesheet('plg_system_nrframework/widgets/mapaddresseditor.css', ['relative' => true, 'version' => 'auto']);
|
||||
HTMLHelper::script('plg_system_nrframework/widgets/mapaddresseditor.js', ['relative' => true, 'version' => 'auto']);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,108 @@
|
||||
<?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\Widgets;
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\HTML\HTMLHelper;
|
||||
use Joomla\CMS\Language\Text;
|
||||
|
||||
class MapAddressEditorView extends OpenStreetMap
|
||||
{
|
||||
/**
|
||||
* Widget default options
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_widget_options = [
|
||||
// Whether autocomplete is enabled for the address field
|
||||
'autocomplete' => false,
|
||||
|
||||
// Whether to show the map
|
||||
'show_map' => true,
|
||||
|
||||
/**
|
||||
* Set whether & where to display the address input.
|
||||
*
|
||||
* Available values:
|
||||
* - before_map: Show it before the map
|
||||
* - true/after_map: Show it after the map
|
||||
* - false: Hide the address field
|
||||
*/
|
||||
'show_address' => 'after_map',
|
||||
|
||||
// The address value
|
||||
'address' => '',
|
||||
|
||||
/**
|
||||
* Markers
|
||||
*/
|
||||
// Show the markers list
|
||||
'show_markers_list' => false,
|
||||
|
||||
// Max markers allowed
|
||||
'max_markers' => 1
|
||||
];
|
||||
|
||||
public function __construct($options = [])
|
||||
{
|
||||
$this->widget_options = array_merge($this->widget_options, $this->_widget_options);
|
||||
|
||||
parent::__construct($options);
|
||||
|
||||
$this->prepare();
|
||||
}
|
||||
|
||||
private function prepare()
|
||||
{
|
||||
// We do not show the map
|
||||
if (!$this->options['show_map'])
|
||||
{
|
||||
$this->options['css_class'] .= ' no-map';
|
||||
}
|
||||
|
||||
// Hide "Clear" button if no markers exists
|
||||
if (empty($this->options['markers']))
|
||||
{
|
||||
$this->options['css_class'] .= ' clear-is-hidden';
|
||||
}
|
||||
|
||||
if ((!$this->options['pro'] && count($this->options['markers']) >= 1) || ($this->options['max_markers'] !== 0 && count($this->options['markers']) >= $this->options['max_markers']))
|
||||
{
|
||||
$this->options['css_class'] .= ' markers-limit-reached';
|
||||
}
|
||||
|
||||
Text::script('NR_ARE_YOU_SURE_YOU_WANT_TO_DELETE_ALL_MARKERS');
|
||||
Text::script('NR_ARE_YOU_SURE_YOU_WANT_TO_DELETE_THIS_MARKER');
|
||||
Text::script('NR_ADD_MARKER');
|
||||
Text::script('NR_EDIT_MARKER');
|
||||
Text::script('NR_DELETE_MARKER');
|
||||
Text::script('NR_UNKNOWN_LOCATION');
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads media files
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function loadMedia()
|
||||
{
|
||||
if ($this->options['show_map'])
|
||||
{
|
||||
parent::loadMedia();
|
||||
|
||||
HTMLHelper::stylesheet('plg_system_nrframework/vendor/leaflet.contextmenu.min.css', ['relative' => true, 'version' => 'auto']);
|
||||
HTMLHelper::script('plg_system_nrframework/vendor/leaflet.contextmenu.min.js', ['relative' => true, 'version' => 'auto']);
|
||||
}
|
||||
|
||||
HTMLHelper::stylesheet('plg_system_nrframework/widgets/mapaddresseditorview.css', ['relative' => true, 'version' => 'auto']);
|
||||
HTMLHelper::script('plg_system_nrframework/widgets/mapaddresseditorview.js', ['relative' => true, 'version' => 'auto']);
|
||||
}
|
||||
}
|
||||
145
plugins/system/nrframework/NRFramework/Widgets/MapEditor.php
Normal file
145
plugins/system/nrframework/NRFramework/Widgets/MapEditor.php
Normal file
@ -0,0 +1,145 @@
|
||||
<?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\Widgets;
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\HTML\HTMLHelper;
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Uri\Uri;
|
||||
use Joomla\CMS\Language\Text;
|
||||
|
||||
class MapEditor extends Widget
|
||||
{
|
||||
/**
|
||||
* Widget default options
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $widget_options = [
|
||||
/**
|
||||
* The list of markers added to the map.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* [
|
||||
* 'lat' => 37.9838,
|
||||
* 'lng' => 23.7275,
|
||||
* 'title' => 'Athens',
|
||||
* 'description' => 'The capital of Greece',
|
||||
* ]
|
||||
*/
|
||||
'value' => [],
|
||||
|
||||
// The default map latitude. Where it points when no markers are added.
|
||||
'lat' => null,
|
||||
|
||||
// The default map longitude. Where it points when no markers are added.
|
||||
'lng' => null,
|
||||
|
||||
// Max markers allowed
|
||||
'maxMarkers' => 1,
|
||||
|
||||
// Set whether to show the map editor sidebar
|
||||
'showSidebar' => true,
|
||||
|
||||
// Set the marker image, relative path to an image file
|
||||
'markerImage' => '',
|
||||
|
||||
// TODO: Remove this once ACF is updated and after a reasonable time
|
||||
'hide_input' => false
|
||||
];
|
||||
|
||||
public function __construct($options = [])
|
||||
{
|
||||
parent::__construct($options);
|
||||
|
||||
$this->prepare();
|
||||
|
||||
$this->loadMedia();
|
||||
}
|
||||
|
||||
private function prepare()
|
||||
{
|
||||
if (!$this->options['pro'] && is_array($this->options['value']) && count($this->options['value']) >= 1)
|
||||
{
|
||||
$this->options['css_class'] .= ' markers-limit-reached';
|
||||
}
|
||||
|
||||
if ($this->options['markerImage'])
|
||||
{
|
||||
$markerImage = explode('#', ltrim($this->options['markerImage'], DIRECTORY_SEPARATOR));
|
||||
$this->options['markerImage'] = Uri::root() . reset($markerImage);
|
||||
}
|
||||
|
||||
Text::script('NR_ENTER_AN_ADDRESS_OR_COORDINATES');
|
||||
Text::script('NR_ARE_YOU_SURE_YOU_WANT_TO_DELETE_ALL_SELECTED_MARKERS');
|
||||
Text::script('NR_ARE_YOU_SURE_YOU_WANT_TO_DELETE_THIS_MARKER');
|
||||
Text::script('NR_ADD_MARKER');
|
||||
Text::script('NR_EDIT_MARKER');
|
||||
Text::script('NR_DELETE_MARKER');
|
||||
Text::script('NR_UNKNOWN_LOCATION');
|
||||
Text::script('NR_UNLIMITED_MARKERS');
|
||||
Text::script('NR_ADD_MORE_MARKERS_UPGRADE_TO_PRO');
|
||||
Text::script('NR_MARKERS');
|
||||
Text::script('NR_YOU_HAVENT_ADDED_ANY_MARKERS_YET');
|
||||
Text::script('NR_ADD_YOUR_FIRST_MARKER');
|
||||
Text::script('NR_NO_MARKERS_FOUND');
|
||||
Text::script('NR_LOCATION_ADDRESS');
|
||||
Text::script('NR_ADD_TO_MAP');
|
||||
Text::script('NR_COORDINATES');
|
||||
Text::script('NR_ADDRESS_ADDRESS_HINT');
|
||||
Text::script('NR_LATITUDE');
|
||||
Text::script('NR_LONGITUDE');
|
||||
Text::script('NR_MARKER_INFO');
|
||||
Text::script('NR_LABEL');
|
||||
Text::script('NR_DESCRIPTION');
|
||||
Text::script('NR_MARKER_LABEL');
|
||||
Text::script('NR_MARKER_DESCRIPTION');
|
||||
Text::script('NR_SAVE');
|
||||
Text::script('NR_PLEASE_SELECT_A_LOCATION');
|
||||
Text::script('NR_IMPORT');
|
||||
Text::script('NR_IMPORT_MARKERS');
|
||||
Text::script('NR_IMPORT_LOCATIONS_DESC');
|
||||
Text::script('NR_IMPORT_LOCATIONS_DESC2');
|
||||
Text::script('NR_PLEASE_ENTER_LOCATIONS_TO_IMPORT');
|
||||
Text::script('NR_COULDNT_IMPORT_LOCATIONS');
|
||||
Text::script('NR_ADDING_MARKERS');
|
||||
Text::script('NR_SAVE_YOUR_FIRST_MARKER');
|
||||
Text::script('NR_OUT_OF');
|
||||
Text::script('NR_MARKERS_ADDED');
|
||||
Text::script('NR_MARKERS_LIMIT_REACHED_DELETE_MARKER_TO_ADD');
|
||||
Text::script('NR_EXPORT_MARKERS');
|
||||
Text::script('NR_EXPORT_MARKERS_DESC');
|
||||
Text::script('NR_THERE_ARE_NO_LOCATIONS_TO_EXPORT');
|
||||
Text::script('NR_LOCATIONS_IMPORTED');
|
||||
|
||||
Factory::getDocument()->addScriptOptions('TFMapEditor', [
|
||||
'images_url' => Uri::root() . 'media/plg_system_nrframework/css/vendor/images/',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads media files
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function loadMedia()
|
||||
{
|
||||
HTMLHelper::script('plg_system_nrframework/vendor/react.min.js', ['relative' => true, 'version' => 'auto']);
|
||||
HTMLHelper::script('plg_system_nrframework/vendor/react-dom.min.js', ['relative' => true, 'version' => 'auto']);
|
||||
|
||||
HTMLHelper::stylesheet('plg_system_nrframework/vendor/leaflet.min.css', ['relative' => true, 'version' => 'auto']);
|
||||
HTMLHelper::script('plg_system_nrframework/vendor/leaflet.min.js', ['relative' => true, 'version' => 'auto']);
|
||||
|
||||
HTMLHelper::stylesheet('plg_system_nrframework/widgets/mapeditor.css', ['relative' => true, 'version' => 'auto']);
|
||||
HTMLHelper::script('plg_system_nrframework/mapeditor.js', ['relative' => true, 'version' => 'auto']);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
<?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\Widgets;
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\HTML\HTMLHelper;
|
||||
|
||||
class OpenStreetMap extends Map
|
||||
{
|
||||
/**
|
||||
* Loads media files
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function loadMedia()
|
||||
{
|
||||
parent::loadMedia();
|
||||
|
||||
HTMLHelper::stylesheet('plg_system_nrframework/vendor/leaflet.min.css', ['relative' => true, 'version' => 'auto']);
|
||||
HTMLHelper::script('plg_system_nrframework/vendor/leaflet.min.js', ['relative' => true, 'version' => 'auto']);
|
||||
|
||||
if ($this->options['load_stylesheet'])
|
||||
{
|
||||
HTMLHelper::stylesheet('plg_system_nrframework/widgets/openstreetmap.css', ['relative' => true, 'version' => 'auto']);
|
||||
}
|
||||
|
||||
if ($this->options['view'] === 'satellite')
|
||||
{
|
||||
HTMLHelper::script('plg_system_nrframework/vendor/esri-leaflet.min.js', ['relative' => true, 'version' => 'auto']);
|
||||
HTMLHelper::script('plg_system_nrframework/vendor/esri-leaflet-vector.min.js', ['relative' => true, 'version' => 'auto']);
|
||||
}
|
||||
|
||||
HTMLHelper::script('plg_system_nrframework/widgets/openstreetmap.js', ['relative' => true, 'version' => 'auto']);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,65 @@
|
||||
<?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\Widgets;
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
/**
|
||||
* The Range Slider widget
|
||||
*/
|
||||
class RangeSlider extends Widget
|
||||
{
|
||||
/**
|
||||
* Widget default options
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $widget_options = [
|
||||
// The default value of the widget.
|
||||
'value' => 0,
|
||||
|
||||
// The minimum value of the slider
|
||||
'min' => 0,
|
||||
|
||||
// The maximum value of the slider
|
||||
'max' => 100,
|
||||
|
||||
// The step of the slider
|
||||
'step' => 1,
|
||||
|
||||
// The main slider color
|
||||
'color' => '#1976d2',
|
||||
|
||||
// The input border color of the slider inputs
|
||||
'input_border_color' => '#bdbdbd',
|
||||
|
||||
// The input background color of the slider inputs
|
||||
'input_bg_color' => 'transparent'
|
||||
];
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct($options = [])
|
||||
{
|
||||
parent::__construct($options);
|
||||
|
||||
// Base color is 20% of given color
|
||||
$this->options['base_color'] = $this->options['color'] . '33';
|
||||
|
||||
// Calculate value
|
||||
$this->options['value'] = (float) $this->options['value'] < $this->options['min'] ? $this->options['min'] : ((float) $this->options['value'] > $this->options['max'] ? $this->options['max'] : (float) $this->options['value']);
|
||||
|
||||
// Calculate bar percentage
|
||||
$this->options['bar_percentage'] = $this->options['max'] ? floor(100 * ($this->options['value'] - $this->options['min']) / ($this->options['max'] - $this->options['min'])) : $this->options['value'];
|
||||
}
|
||||
}
|
||||
62
plugins/system/nrframework/NRFramework/Widgets/Rating.php
Normal file
62
plugins/system/nrframework/NRFramework/Widgets/Rating.php
Normal file
@ -0,0 +1,62 @@
|
||||
<?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\Widgets;
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\Uri\Uri;
|
||||
|
||||
/**
|
||||
* The Rating Widget
|
||||
*/
|
||||
class Rating extends Widget
|
||||
{
|
||||
/**
|
||||
* Widget default options
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $widget_options = [
|
||||
// The SVG icon representing the rating icon. Available values: check, circle, flag, heart, smiley, square, star, thumbs_up
|
||||
'icon' => 'star',
|
||||
|
||||
// The default value of the widget.
|
||||
'value' => 0,
|
||||
|
||||
// How many stars to show?
|
||||
'max_rating' => 5,
|
||||
|
||||
// Whether to show half ratings
|
||||
'half_ratings' => false,
|
||||
|
||||
// The size of the rating icon in pixels.
|
||||
'size' => 24,
|
||||
|
||||
// The color of the icon in the default state
|
||||
'selected_color' => '#f6cc01',
|
||||
|
||||
// The color of the icon in the selected and hover state
|
||||
'unselected_color' => '#bdbdbd'
|
||||
];
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct($options = [])
|
||||
{
|
||||
parent::__construct($options);
|
||||
|
||||
$this->options['value'] = $this->options['value'] > $this->options['max_rating'] ? $this->options['max_rating'] : $this->options['value'];
|
||||
$this->options['icon_url'] = Uri::root() . 'media/plg_system_nrframework/svg/rating/' . $this->options['icon'] . '.svg';
|
||||
$this->options['max_rating'] = $this->options['half_ratings'] ? 2 * $this->options['max_rating'] : $this->options['max_rating'];
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2020 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace NRFramework\Widgets;
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\HTML\HTMLHelper;
|
||||
|
||||
class SelfHostedVideo extends Video
|
||||
{
|
||||
/**
|
||||
* Widget default options
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $video_widget_options = [
|
||||
/**
|
||||
* Specify how the video should be loaded when the page loads.
|
||||
*
|
||||
* Allowed values:
|
||||
* - metadata
|
||||
* - auto
|
||||
* - none
|
||||
*/
|
||||
'preload' => 'auto',
|
||||
|
||||
// Whether to mute the video
|
||||
'mute' => false,
|
||||
|
||||
// Whether to display controls on the video
|
||||
'controls' => true,
|
||||
|
||||
// Whether to loop the video
|
||||
'loop' => false,
|
||||
|
||||
// Stores the given video details
|
||||
'video' => ''
|
||||
];
|
||||
|
||||
protected function prepare()
|
||||
{
|
||||
if (isset($this->options['value']) && !empty($this->options['value']))
|
||||
{
|
||||
$videos = \NRFramework\Helpers\File::getFileSources($this->options['value'], ['mp4', 'webm', 'ogg', 'mov']);
|
||||
$this->options['video'] = is_array($videos) && isset($videos[0]) ? $videos[0] : false;
|
||||
}
|
||||
|
||||
$atts = [
|
||||
'data-video-id="' . $this->options['value'] . '"',
|
||||
'data-video-type="selfhostedvideo"',
|
||||
'data-video-mute="' . var_export($this->options['mute'], true) . '"',
|
||||
'data-video-controls="' . var_export($this->options['controls'], true) . '"',
|
||||
'data-video-loop="' . var_export($this->options['loop'], true) . '"',
|
||||
'data-video-autoplay="' . var_export($this->options['autoplay'], true) . '"',
|
||||
'data-video-autopause="' . var_export($this->options['autopause'], true) . '"',
|
||||
];
|
||||
|
||||
$this->options['atts'] = implode(' ', $atts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads media files
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function videoAssets()
|
||||
{
|
||||
HTMLHelper::script('plg_system_nrframework/widgets/video/selfhostedvideo.js', ['relative' => true, 'version' => 'auto']);
|
||||
}
|
||||
}
|
||||
92
plugins/system/nrframework/NRFramework/Widgets/Signature.php
Normal file
92
plugins/system/nrframework/NRFramework/Widgets/Signature.php
Normal file
@ -0,0 +1,92 @@
|
||||
<?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\Widgets;
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
/**
|
||||
* Signature
|
||||
*/
|
||||
class Signature extends Widget
|
||||
{
|
||||
/**
|
||||
* Widget default options
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $widget_options = [
|
||||
// The base64 image data of the signature.
|
||||
'value' => '',
|
||||
|
||||
// The width of the signature in pixels or empty for auto width. The width will be taken from the signature container.
|
||||
'width' => '',
|
||||
|
||||
// The height of the signature in pixels.
|
||||
'height' => '300px',
|
||||
|
||||
// The background color of the signature.
|
||||
'background_color' => '#ffffff',
|
||||
|
||||
// The border color of the canvas.
|
||||
'border_color' => '#dedede',
|
||||
|
||||
/**
|
||||
* The border radius of the canvas.
|
||||
*
|
||||
* Example values: 0, 0px, 50px, 50%
|
||||
*/
|
||||
'border_radius' => 0,
|
||||
|
||||
/**
|
||||
* The border width of the canvas.
|
||||
*
|
||||
* Example values: 0, 1px, 5px
|
||||
*/
|
||||
'border_width' => '1px',
|
||||
|
||||
// Whether to show the horizontal line within the canvas
|
||||
'show_line' => true,
|
||||
|
||||
/**
|
||||
* The line color.
|
||||
*
|
||||
* If `null`, retrieves the value from `border_color`
|
||||
*/
|
||||
'line_color' => null,
|
||||
|
||||
// The pen color
|
||||
'pen_color' => '#000'
|
||||
];
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct($options = [])
|
||||
{
|
||||
parent::__construct($options);
|
||||
|
||||
if ($this->options['readonly'])
|
||||
{
|
||||
$this->options['css_class'] .= ' readonly';
|
||||
}
|
||||
|
||||
if (!empty($this->options['value']))
|
||||
{
|
||||
$this->options['css_class'] .= ' painted has-value';
|
||||
}
|
||||
|
||||
if ($this->options['show_line'])
|
||||
{
|
||||
$this->options['css_class'] .= ' show-line';
|
||||
}
|
||||
}
|
||||
}
|
||||
292
plugins/system/nrframework/NRFramework/Widgets/Slideshow.php
Normal file
292
plugins/system/nrframework/NRFramework/Widgets/Slideshow.php
Normal file
@ -0,0 +1,292 @@
|
||||
<?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\Widgets;
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use \NRFramework\Helpers\Widgets\Gallery as GalleryHelper;
|
||||
use NRFramework\Mimes;
|
||||
use NRFramework\File;
|
||||
use NRFramework\Image;
|
||||
use Joomla\CMS\Factory;
|
||||
|
||||
class Slideshow extends Widget
|
||||
{
|
||||
/**
|
||||
* Widget default options
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $widget_options = [
|
||||
// Slides per view
|
||||
'slides_per_view' => [
|
||||
'desktop' => 1
|
||||
],
|
||||
|
||||
// Space between slides in px
|
||||
'space_between_slides' => [
|
||||
'desktop' => 10
|
||||
],
|
||||
|
||||
// Enable Infinite Loop
|
||||
'infinite_loop' => false,
|
||||
|
||||
// Enable Keyboard Control
|
||||
'keyboard_control' => false,
|
||||
|
||||
/**
|
||||
* Set the ordering.
|
||||
*
|
||||
* Available values:
|
||||
* - default
|
||||
* - alphabetical
|
||||
* - reverse_alphabetical
|
||||
* - random
|
||||
*/
|
||||
'ordering' => 'default',
|
||||
|
||||
/**
|
||||
* The transition effect.
|
||||
*
|
||||
* Available values:
|
||||
* - slide
|
||||
* - fade
|
||||
* - cube
|
||||
* - coverflow
|
||||
* - flip
|
||||
*/
|
||||
'transition_effect' => 'slide',
|
||||
|
||||
// Enable Autoplay
|
||||
'autoplay' => false,
|
||||
|
||||
// Autoplay delay
|
||||
'autoplay_delay' => 3000,
|
||||
|
||||
// Enable autoplay circular progress
|
||||
'autoplay_progress' => false,
|
||||
|
||||
// Show thumbnails below the slideshow
|
||||
'show_thumbnails' => false,
|
||||
|
||||
// Set whether to show arrows in the thumbnails slider
|
||||
'show_thumbnails_arrows' => false,
|
||||
|
||||
/**
|
||||
* Navigation controls.
|
||||
*
|
||||
* Accepted values:
|
||||
* - arrows
|
||||
* - dots
|
||||
* - arrows_dots
|
||||
*/
|
||||
'nav_controls' => false,
|
||||
|
||||
// Theme color
|
||||
'theme_color' => '#007aff',
|
||||
|
||||
// Set whether to display a lightbox
|
||||
'lightbox' => false,
|
||||
|
||||
// Set the module key to display whenever we are viewing a single item's lightbox, appearing after the image
|
||||
'module' => '',
|
||||
|
||||
// Options
|
||||
'options' => []
|
||||
];
|
||||
|
||||
public function __construct($options = [])
|
||||
{
|
||||
parent::__construct($options);
|
||||
|
||||
$this->prepare();
|
||||
|
||||
$this->prepareItems();
|
||||
|
||||
$this->setOrdering();
|
||||
|
||||
$this->setCSSVars();
|
||||
}
|
||||
|
||||
private function prepare()
|
||||
{
|
||||
if ($this->options['lightbox'])
|
||||
{
|
||||
$this->options['css_class'] .= ' lightbox';
|
||||
}
|
||||
|
||||
$options = [
|
||||
'transition_effect' => $this->options['transition_effect'],
|
||||
'infinite_loop' => $this->options['infinite_loop'],
|
||||
'keyboard_control' => $this->options['keyboard_control'],
|
||||
'autoplay' => $this->options['autoplay'],
|
||||
'autoplay_delay' => $this->options['autoplay_delay'],
|
||||
'autoplay_progress' => $this->options['autoplay_progress'],
|
||||
'show_thumbnails' => $this->options['show_thumbnails'],
|
||||
'show_thumbnails_arrows' => $this->options['show_thumbnails_arrows'],
|
||||
'lightbox' => $this->options['lightbox'],
|
||||
'breakpoints' => \NRFramework\Helpers\Responsive::getBreakpointsSettings(),
|
||||
'slides_per_view' => $this->options['slides_per_view'],
|
||||
'space_between_slides' => $this->getSpaceBetweenSlides(),
|
||||
'nav_controls' => $this->options['nav_controls']
|
||||
];
|
||||
|
||||
$this->options['options'] = $options;
|
||||
}
|
||||
|
||||
private function getSpaceBetweenSlides()
|
||||
{
|
||||
$space_between_slides = $this->options['space_between_slides'];
|
||||
|
||||
if (is_array($space_between_slides))
|
||||
{
|
||||
foreach ($space_between_slides as $key => &$value)
|
||||
{
|
||||
$value = \NRFramework\Helpers\Controls\Control::getCSSValue($value['value']);
|
||||
}
|
||||
}
|
||||
|
||||
return $space_between_slides;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the items.
|
||||
*
|
||||
* - Sets the thumbnails image dimensions.
|
||||
* - Assures caption property exist.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
private function prepareItems()
|
||||
{
|
||||
if (!is_array($this->options['items']) || !count($this->options['items']))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$smartTagsInstance = \NRFramework\SmartTags::getInstance();
|
||||
|
||||
foreach ($this->options['items'] as $key => &$item)
|
||||
{
|
||||
// Initialize image atts
|
||||
$item['img_atts'] = '';
|
||||
|
||||
// Initializes caption if none given
|
||||
if (!isset($item['caption']))
|
||||
{
|
||||
$item['caption'] = '';
|
||||
}
|
||||
|
||||
if (!isset($item['alt']) || empty($item['alt']))
|
||||
{
|
||||
$item['alt'] = !empty($item['caption']) ? mb_substr($item['caption'], 0, 100) : pathinfo($item['url'], PATHINFO_FILENAME);
|
||||
}
|
||||
|
||||
// Replace Smart Tags in alt
|
||||
$item['alt'] = $smartTagsInstance->replace($item['alt']);
|
||||
|
||||
// Ensure a thumbnail is given
|
||||
if (!isset($item['thumbnail_url']))
|
||||
{
|
||||
// If no thumbnail is given, set it to the full image
|
||||
$item['thumbnail_url'] = $item['url'];
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the thumbnail size for this item is given, set the image attributes
|
||||
if (isset($item['thumbnail_size']))
|
||||
{
|
||||
$item['img_atts'] = 'width="' . $item['thumbnail_size']['width'] . '" height="' . $item['thumbnail_size']['height'] . '"';
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the ordering of the gallery.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function setOrdering()
|
||||
{
|
||||
switch ($this->options['ordering']) {
|
||||
case 'random':
|
||||
shuffle($this->options['items']);
|
||||
break;
|
||||
case 'alphabetical':
|
||||
usort($this->options['items'], [$this, 'compareByThumbnailASC']);
|
||||
break;
|
||||
case 'reverse_alphabetical':
|
||||
usort($this->options['items'], [$this, 'compareByThumbnailDESC']);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares thumbnail file names in ASC order
|
||||
*
|
||||
* @param array $a
|
||||
* @param array $b
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function compareByThumbnailASC($a, $b)
|
||||
{
|
||||
return strcmp(basename($a['thumbnail']), basename($b['thumbnail']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares thumbnail file names in DESC order
|
||||
*
|
||||
* @param array $a
|
||||
* @param array $b
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function compareByThumbnailDESC($a, $b)
|
||||
{
|
||||
return strcmp(basename($b['thumbnail']), basename($a['thumbnail']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the CSS variables.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function setCSSVars()
|
||||
{
|
||||
if (!$this->options['load_css_vars'])
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$controls = [
|
||||
[
|
||||
'property' => '--slideshow-slides-per-view',
|
||||
'value' => $this->options['slides_per_view']
|
||||
],
|
||||
[
|
||||
'property' => '--slideshow-space-between-slides',
|
||||
'value' => $this->options['space_between_slides']
|
||||
]
|
||||
];
|
||||
|
||||
$selector = '.nrf-widget.tf-slideshow-wrapper.' . $this->options['id'];
|
||||
|
||||
$controlsInstance = new \NRFramework\Controls\Controls(null, $selector);
|
||||
|
||||
if (!$controlsCSS = $controlsInstance->generateCSS($controls))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Factory::getDocument()->addStyleDeclaration($controlsCSS);
|
||||
}
|
||||
}
|
||||
113
plugins/system/nrframework/NRFramework/Widgets/Video.php
Normal file
113
plugins/system/nrframework/NRFramework/Widgets/Video.php
Normal file
@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2020 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace NRFramework\Widgets;
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\HTML\HTMLHelper;
|
||||
use Joomla\CMS\Factory;
|
||||
|
||||
abstract class Video extends Widget
|
||||
{
|
||||
/**
|
||||
* Widget default options
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $widget_options = [
|
||||
// Video URL
|
||||
'value' => '',
|
||||
|
||||
// Video width
|
||||
'width' => '480px',
|
||||
|
||||
// Video height
|
||||
'height' => '270px',
|
||||
|
||||
// Whether the video will autoplay
|
||||
'autoplay' => false,
|
||||
|
||||
// Whether the video will autopause whenever we scroll and it hides from our viewport
|
||||
'autopause' => false
|
||||
];
|
||||
|
||||
public function __construct($options = [])
|
||||
{
|
||||
$this->widget_options = array_merge($this->widget_options, $this->video_widget_options);
|
||||
|
||||
parent::__construct($options);
|
||||
|
||||
$this->prepare();
|
||||
|
||||
$this->styles();
|
||||
}
|
||||
|
||||
protected function prepare()
|
||||
{}
|
||||
|
||||
public function render()
|
||||
{
|
||||
$this->loadMedia();
|
||||
|
||||
return parent::render();
|
||||
}
|
||||
|
||||
public function styles()
|
||||
{
|
||||
if (!$this->options['load_css_vars'])
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$controls = [
|
||||
[
|
||||
'property' => '--video-width',
|
||||
'value' => $this->options['width']
|
||||
],
|
||||
[
|
||||
'property' => '--video-height',
|
||||
'value' => $this->options['height']
|
||||
]
|
||||
];
|
||||
|
||||
$selector = '.nrf-widget.tf-video.' . $this->options['id'];
|
||||
|
||||
$controlsInstance = new \NRFramework\Controls\Controls(null, $selector);
|
||||
|
||||
if (!$controlsCSS = $controlsInstance->generateCSS($controls))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Factory::getDocument()->addStyleDeclaration($controlsCSS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads media files
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function loadMedia()
|
||||
{
|
||||
if ($this->options['load_stylesheet'])
|
||||
{
|
||||
HTMLHelper::stylesheet('plg_system_nrframework/widgets/video.css', ['relative' => true, 'version' => 'auto']);
|
||||
}
|
||||
|
||||
HTMLHelper::script('plg_system_nrframework/widgets/video.js', ['relative' => true, 'version' => 'auto']);
|
||||
|
||||
if (method_exists($this, 'videoAssets'))
|
||||
{
|
||||
$this->videoAssets();
|
||||
}
|
||||
|
||||
HTMLHelper::script('plg_system_nrframework/widgets/videos.js', ['relative' => true, 'version' => 'auto']);
|
||||
}
|
||||
}
|
||||
152
plugins/system/nrframework/NRFramework/Widgets/Vimeo.php
Normal file
152
plugins/system/nrframework/NRFramework/Widgets/Vimeo.php
Normal file
@ -0,0 +1,152 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2020 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace NRFramework\Widgets;
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\HTML\HTMLHelper;
|
||||
use Joomla\CMS\Uri\Uri;
|
||||
|
||||
class Vimeo extends Video
|
||||
{
|
||||
/**
|
||||
* Widget default options
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $video_widget_options = [
|
||||
/**
|
||||
* Set the cover image type.
|
||||
*
|
||||
* Allowed Values:
|
||||
* - none
|
||||
* - auto
|
||||
* - custom
|
||||
*/
|
||||
'coverImageType' => 'none',
|
||||
|
||||
// The Cover Image URL when coverImage="custom"
|
||||
'coverImage' => '',
|
||||
|
||||
// Whether controls will appear in the video
|
||||
'controls' => true,
|
||||
|
||||
// Loop
|
||||
'loop' => false,
|
||||
|
||||
// Mute
|
||||
'mute' => false,
|
||||
|
||||
/**
|
||||
* Set whether to load the video in privacy-enhanced mode.
|
||||
*
|
||||
* When this is enabled, Vimeo will block the player from tracking
|
||||
* any session data, including all cookies and analytics.
|
||||
*/
|
||||
'privacy' => false,
|
||||
|
||||
// Whether to show the video title
|
||||
'title' => false,
|
||||
|
||||
// Whether to show the author of the video
|
||||
'byline' => false,
|
||||
|
||||
// Whether to show the author's profile image
|
||||
'portrait' => false,
|
||||
|
||||
// The color of the video controls
|
||||
'color' => '#00adef',
|
||||
|
||||
// Whether to allow keyboard inputs
|
||||
'keyboard' => false,
|
||||
|
||||
// Enable to show the picture-in-picture button in the control bar
|
||||
'pip' => false,
|
||||
|
||||
// Set the start time
|
||||
'start' => null,
|
||||
|
||||
// Set the end time
|
||||
'end' => null,
|
||||
];
|
||||
|
||||
/**
|
||||
* Prepares the widget.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function prepare()
|
||||
{
|
||||
$videoDetails = \NRFramework\Helpers\Video::getDetails($this->options['value']);
|
||||
$videoProvider = isset($videoDetails['provider']) ? $videoDetails['provider'] : '';
|
||||
|
||||
// Abort
|
||||
if ($videoProvider !== 'vimeo')
|
||||
{
|
||||
$this->options['value'] = null;
|
||||
return;
|
||||
}
|
||||
|
||||
$this->options['css_class'] .= ' vimeo';
|
||||
|
||||
$videoID = isset($videoDetails['id']) ? $videoDetails['id'] : '';
|
||||
|
||||
if ($this->options['coverImageType'] === 'auto')
|
||||
{
|
||||
$this->options['coverImage'] = 'url("https://vumbnail.com/' . $videoID . '.jpg")';
|
||||
}
|
||||
else if ($this->options['coverImageType'] === 'custom' && !empty($this->options['coverImage']))
|
||||
{
|
||||
$coverImage = explode('#', $this->options['coverImage']);
|
||||
$this->options['coverImage'] = 'url("' . Uri::base() . reset($coverImage) . '")';
|
||||
}
|
||||
|
||||
$atts = [
|
||||
'data-video-id="' . $videoID . '"',
|
||||
'data-video-type="' . $videoProvider . '"',
|
||||
'data-video-mute="' . var_export($this->options['mute'], true) . '"',
|
||||
'data-video-controls="' . var_export($this->options['controls'], true) . '"',
|
||||
'data-video-loop="' . var_export($this->options['loop'], true) . '"',
|
||||
'data-video-autoplay="' . var_export($this->options['autoplay'], true) . '"',
|
||||
'data-video-autopause="' . var_export($this->options['autopause'], true) . '"',
|
||||
'data-video-privacy="' . var_export($this->options['privacy'], true) . '"',
|
||||
'data-video-title="' . var_export($this->options['title'], true) . '"',
|
||||
'data-video-byline="' . var_export($this->options['byline'], true) . '"',
|
||||
'data-video-portrait="' . var_export($this->options['portrait'], true) . '"',
|
||||
'data-video-keyboard="' . var_export($this->options['keyboard'], true) . '"',
|
||||
'data-video-pip="' . var_export($this->options['pip'], true) . '"',
|
||||
'data-video-color="' . $this->options['color'] . '"',
|
||||
'data-video-start="' . $this->options['start'] . '"',
|
||||
'data-video-end="' . $this->options['end'] . '"',
|
||||
];
|
||||
|
||||
$this->options['atts'] = implode(' ', $atts);
|
||||
}
|
||||
|
||||
/**
|
||||
* We use the video widget layout file.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'video';
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads media files
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function videoAssets()
|
||||
{
|
||||
HTMLHelper::script('plg_system_nrframework/widgets/video/vimeo.js', ['relative' => true, 'version' => 'auto']);
|
||||
}
|
||||
}
|
||||
209
plugins/system/nrframework/NRFramework/Widgets/Widget.php
Normal file
209
plugins/system/nrframework/NRFramework/Widgets/Widget.php
Normal file
@ -0,0 +1,209 @@
|
||||
<?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\Widgets;
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Layout\FileLayout;
|
||||
use Joomla\CMS\Session\Session;
|
||||
|
||||
class Widget
|
||||
{
|
||||
protected $widget_options = [];
|
||||
|
||||
/**
|
||||
* Widget's default options
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $options = [
|
||||
// Set whether to load the CSS variables
|
||||
'load_css_vars' => true,
|
||||
|
||||
// Set whether to load the default stylesheet
|
||||
'load_stylesheet' => true,
|
||||
|
||||
// If true, the widget will be rended in read-only mode.
|
||||
'readonly' => false,
|
||||
|
||||
// If true, the widget will be rended in disabled mode.
|
||||
'disabled' => false,
|
||||
|
||||
// Indicates the widget's input field must be filled out before submitting the form.
|
||||
'required' => false,
|
||||
|
||||
// The CSS class to be used on the widget's wrapper
|
||||
'css_class' => '',
|
||||
|
||||
// The CSS class to be used on the input
|
||||
'input_class' => '',
|
||||
|
||||
// The input name
|
||||
'name' => '',
|
||||
|
||||
// The default widget value
|
||||
'value' => '',
|
||||
|
||||
// Extra attributes
|
||||
'atts' => '',
|
||||
|
||||
// Custom CSS
|
||||
'custom_css' => '',
|
||||
|
||||
// A short hint that describes the expected value
|
||||
'placeholder' => '',
|
||||
|
||||
// The name of the layout to be used to render the widget
|
||||
'layout' => 'default',
|
||||
|
||||
// The extension name where the widget is loaded from.
|
||||
'extension' => '',
|
||||
|
||||
// The aria-label attribute
|
||||
'aria_label' => '',
|
||||
|
||||
// Whether we are rendering the Pro version of the widget
|
||||
'pro' => false
|
||||
];
|
||||
|
||||
/**
|
||||
* If no name is provided, this counter is appended to the widget's name to prevent name conflicts
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected static $counter = 0;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct($options = [])
|
||||
{
|
||||
// Merge Widget class default options with given Widget default options
|
||||
$this->options = array_merge($this->options, $this->widget_options, $options);
|
||||
|
||||
// Set ID if none given
|
||||
if (!isset($this->options['id']))
|
||||
{
|
||||
$this->options['id'] = $this->getName() . self::$counter;
|
||||
}
|
||||
|
||||
// Help developers target the whole widget by applying the widget's ID to the CSS class list.
|
||||
// Do not use the id="xx" attribute in the HTML to prevent conflicts with the input's ID.
|
||||
$this->options['css_class'] .= ' ' . $this->options['id'];
|
||||
|
||||
// Set name if none given
|
||||
if (!isset($this->options['name']))
|
||||
{
|
||||
$this->options['name'] = $this->options['id'];
|
||||
}
|
||||
|
||||
// Set disabled class if widget is disabled
|
||||
if ($this->options['disabled'])
|
||||
{
|
||||
$this->options['css_class'] .= ' disabled';
|
||||
}
|
||||
|
||||
self::$counter++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the widget with the given layout
|
||||
*
|
||||
* Layouts can be overriden in the following folder: /templates/TEMPLATE_NAME/html/tassos/WIDGET_NAME/LAYOUT_NAME.php
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
$defaultPath = implode(DIRECTORY_SEPARATOR, [JPATH_PLUGINS, 'system', 'nrframework', 'layouts']);
|
||||
$overridePath = implode(DIRECTORY_SEPARATOR, [JPATH_THEMES, Factory::getApplication()->getTemplate(), 'html', 'tassos']);
|
||||
|
||||
$layout = new FileLayout('widgets.' . $this->getName() . '.' . $this->options['layout'], null, ['debug' => false]);
|
||||
$layout->addIncludePaths($defaultPath);
|
||||
$layout->addIncludePaths($overridePath);
|
||||
|
||||
return $layout->render($this->options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the widget
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return strtolower((new \ReflectionClass($this))->getShortName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the options key value.
|
||||
*
|
||||
* @param string $key
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getOption($key)
|
||||
{
|
||||
return isset($this->options[$key]) ? $this->options[$key] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages ajax requests for the widget.
|
||||
*
|
||||
* @param string $task
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function onAjax($task)
|
||||
{
|
||||
Session::checkToken('request') or die('Invalid Token');
|
||||
|
||||
if (!$task || !is_string($task))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$method = 'ajax_' . $task;
|
||||
|
||||
if (!method_exists($this, $method))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$this->$method();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the options key value.
|
||||
*
|
||||
* @param string $key
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setOption($key, $value)
|
||||
{
|
||||
$this->options[$key] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an option exists.
|
||||
*
|
||||
* @param string $key
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function optionExists($key)
|
||||
{
|
||||
return isset($this->options[$key]);
|
||||
}
|
||||
}
|
||||
161
plugins/system/nrframework/NRFramework/Widgets/YouTube.php
Normal file
161
plugins/system/nrframework/NRFramework/Widgets/YouTube.php
Normal file
@ -0,0 +1,161 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @author Tassos Marinos <info@tassos.gr>
|
||||
* @link http://www.tassos.gr
|
||||
* @copyright Copyright © 2020 Tassos Marinos All Rights Reserved
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
|
||||
*/
|
||||
|
||||
namespace NRFramework\Widgets;
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\Uri\Uri;
|
||||
use Joomla\CMS\HTML\HTMLHelper;
|
||||
|
||||
class YouTube extends Video
|
||||
{
|
||||
/**
|
||||
* Widget default options
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $video_widget_options = [
|
||||
/**
|
||||
* Set the cover image type.
|
||||
*
|
||||
* Allowed Values:
|
||||
* - none
|
||||
* - auto
|
||||
* - custom
|
||||
*/
|
||||
'coverImageType' => 'none',
|
||||
|
||||
// The Cover Image URL when coverImage="custom"
|
||||
'coverImage' => '',
|
||||
|
||||
// Whether we allow fullscreen
|
||||
'fs' => false,
|
||||
|
||||
// Whether controls will appear in the video
|
||||
'controls' => true,
|
||||
|
||||
// Loop
|
||||
'loop' => false,
|
||||
|
||||
// Mute
|
||||
'mute' => false,
|
||||
|
||||
// Closed Captions
|
||||
'cc_load_policy' => false,
|
||||
|
||||
/**
|
||||
* The color that will be used in the player's video progress bar to highlight
|
||||
* the amount of the video that the viewer has already seen.
|
||||
*
|
||||
* Allowed Values:
|
||||
* - red
|
||||
* - white
|
||||
*/
|
||||
'color' => 'red',
|
||||
|
||||
// Whether to allow or not keyboard shortcuts
|
||||
'disablekb' => false,
|
||||
|
||||
// Start the video from X seconds
|
||||
'start' => null,
|
||||
|
||||
// End the video at X seconds
|
||||
'end' => null,
|
||||
|
||||
/**
|
||||
* Set whether to show related videos.
|
||||
*
|
||||
* Allowed Values:
|
||||
* 0: Don't show related videos
|
||||
* 1: Show related videos from anywhere
|
||||
*/
|
||||
'rel' => '1',
|
||||
|
||||
/**
|
||||
* Set whether to load the video in privacy-enhanced mode.
|
||||
*
|
||||
* When this is enabled, YouTube won't store information about
|
||||
* visitors unless they play the video.
|
||||
*/
|
||||
'privacy' => false
|
||||
];
|
||||
|
||||
/**
|
||||
* Prepares the widget.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function prepare()
|
||||
{
|
||||
$videoDetails = \NRFramework\Helpers\Video::getDetails($this->options['value']);
|
||||
$videoProvider = isset($videoDetails['provider']) ? $videoDetails['provider'] : '';
|
||||
|
||||
// Abort
|
||||
if ($videoProvider !== 'youtube')
|
||||
{
|
||||
$this->options['value'] = null;
|
||||
return;
|
||||
}
|
||||
|
||||
$this->options['css_class'] .= ' youtube';
|
||||
|
||||
$videoID = isset($videoDetails['id']) ? $videoDetails['id'] : '';
|
||||
|
||||
if ($this->options['coverImageType'] === 'auto')
|
||||
{
|
||||
$this->options['coverImage'] = 'url("https://img.youtube.com/vi/' . $videoID . '/maxresdefault.jpg")';
|
||||
}
|
||||
else if ($this->options['coverImageType'] === 'custom' && !empty($this->options['coverImage']))
|
||||
{
|
||||
$coverImage = explode('#', $this->options['coverImage']);
|
||||
$this->options['coverImage'] = 'url("' . Uri::base() . reset($coverImage) . '")';
|
||||
}
|
||||
|
||||
$atts = [
|
||||
'data-video-id="' . $videoID . '"',
|
||||
'data-video-controls="' . var_export($this->options['controls'], true) . '"',
|
||||
'data-video-type="' . $videoProvider . '"',
|
||||
'data-video-mute="' . var_export($this->options['mute'], true) . '"',
|
||||
'data-video-loop="' . var_export($this->options['loop'], true) . '"',
|
||||
'data-video-start="' . $this->options['start'] . '"',
|
||||
'data-video-end="' . $this->options['end'] . '"',
|
||||
'data-video-autoplay="' . var_export($this->options['autoplay'], true) . '"',
|
||||
'data-video-fs="' . var_export($this->options['fs'], true) . '"',
|
||||
'data-video-autopause="' . var_export($this->options['autopause'], true) . '"',
|
||||
'data-video-cc="' . var_export($this->options['cc_load_policy'], true) . '"',
|
||||
'data-video-disablekb="' . var_export($this->options['disablekb'], true) . '"',
|
||||
'data-video-privacy="' . var_export($this->options['privacy'], true) . '"',
|
||||
'data-video-rel="' . $this->options['rel'] . '"',
|
||||
'data-video-color="' . $this->options['color'] . '"'
|
||||
];
|
||||
|
||||
$this->options['atts'] = implode(' ', $atts);
|
||||
}
|
||||
|
||||
/**
|
||||
* We use the video widget layout file.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'video';
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads media files
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function videoAssets()
|
||||
{
|
||||
HTMLHelper::script('plg_system_nrframework/widgets/video/youtube.js', ['relative' => true, 'version' => 'auto']);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user