508 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			508 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| 
 | |
| /**
 | |
|  * @author          Tassos Marinos <info@tassos.gr>
 | |
|  * @link            https://www.tassos.gr
 | |
|  * @copyright       Copyright © 2024 Tassos All Rights Reserved
 | |
|  * @license         GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
 | |
| */
 | |
| 
 | |
| namespace NRFramework;
 | |
| 
 | |
| // No direct access
 | |
| defined('_JEXEC') or die;
 | |
| 
 | |
| use NRFramework\Cache;
 | |
| use NRFramework\Functions;
 | |
| use NRFramework\Extension;
 | |
| use Joomla\CMS\Language\Text;
 | |
| use Joomla\CMS\HTML\HTMLHelper;
 | |
| use Joomla\CMS\Factory;
 | |
| use Joomla\CMS\Layout\LayoutHelper;
 | |
| use Joomla\CMS\Uri\Uri;
 | |
| use Joomla\CMS\Session\Session;
 | |
| 
 | |
| class HTML
 | |
| {
 | |
| 	/**
 | |
| 	 * Display field help text as tooltip in Joomla 4
 | |
| 	 *
 | |
| 	 * @return void
 | |
| 	 */
 | |
| 	public static function fixFieldTooltips()
 | |
| 	{
 | |
| 		// Run once
 | |
|         static $run;
 | |
| 
 | |
|         if ($run)
 | |
|         {
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         $run = true;
 | |
| 
 | |
| 		HTMLHelper::_('bootstrap.popover');
 | |
| 		HTMLHelper::_('jquery.framework');
 | |
| 
 | |
| 		$doc = Factory::getDocument();
 | |
| 
 | |
| 		$doc->addStyleDeclaration('
 | |
| 			.form-text, .form-control-feedback {
 | |
| 				display:none;
 | |
| 			}
 | |
| 			.tooltip .arrow:before {
 | |
| 				border-top-color:#444;
 | |
| 				border-bottom-color:#444;
 | |
| 			}
 | |
| 			.tooltip-inner {
 | |
| 				text-align: left;
 | |
| 				background-color: #444;
 | |
| 				padding: 7px 9px;
 | |
| 				max-width:300px;
 | |
| 			}
 | |
| 		');
 | |
| 
 | |
| 		$doc->addScriptDeclaration('
 | |
| 			document.addEventListener("DOMContentLoaded", function() {
 | |
| 				initPopover();
 | |
| 		
 | |
| 				document.addEventListener("joomla:updated", initPopover);
 | |
| 		
 | |
| 				function initPopover(event) {
 | |
| 					var target = event && event.target ? event.target : document;
 | |
| 					var fields = target.querySelectorAll(".control-group");
 | |
| 		
 | |
| 					fields.forEach(function(field) {
 | |
| 						var desc = field.querySelector(".form-text");
 | |
| 			
 | |
| 						if (desc) {
 | |
| 							var label = field.querySelector("label");
 | |
| 			
 | |
| 							if (label) {
 | |
| 								label.classList.add("tTooltip");
 | |
| 								label.setAttribute("title", desc.innerHTML);
 | |
| 							}
 | |
| 						}
 | |
| 					});
 | |
| 		
 | |
| 					jQuery(target).find(".tTooltip").tooltip({
 | |
| 						placement: "top",
 | |
| 						html: true,
 | |
| 						delay: {
 | |
| 							show: 200
 | |
| 						}
 | |
| 					});
 | |
| 				}
 | |
| 			});
 | |
| 		');
 | |
| 	}
 | |
| 	
 | |
| 	/**
 | |
| 	 * Renders the HTML layout
 | |
| 	 * 
 | |
| 	 * @param   string  $layout		The HTML class of the layout.
 | |
| 	 * @param   array   $options	A list of attributes passed to the layout
 | |
| 	 * 
 | |
| 	 * @return  string
 | |
| 	 */
 | |
| 	public static function render($layout, $options = [])
 | |
| 	{
 | |
| 		if (!$layout)
 | |
| 		{
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		$class = '\NRFramework\HTML\\' . $layout;
 | |
| 
 | |
| 		// ensure class exists
 | |
| 		if (!class_exists($class))
 | |
| 		{
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		return (new $class($options))->render();
 | |
| 	}
 | |
| 	
 | |
| 	/**
 | |
| 	 * Renders Pro Button
 | |
| 	 *
 | |
| 	 * @param string $feature_label  The text that will be used as the modal popup feature
 | |
| 	 * @param string $buttonClass       The class of the button
 | |
| 	 *
 | |
| 	 * @return void
 | |
| 	 */
 | |
| 	public static function renderProButton($feature_label = null, $buttonClass = 'btn-sm')
 | |
| 	{
 | |
| 		include_once JPATH_PLUGINS . '/system/nrframework/fields/pro.php';
 | |
| 
 | |
|         $field   = new \JFormFieldNR_PRO;
 | |
|         $element = new \SimpleXMLElement('
 | |
| 			<field name="pro" type="nr_pro"
 | |
| 				buttonClass="' . $buttonClass . '"
 | |
| 				label="' . $feature_label . '"
 | |
|             />');
 | |
| 
 | |
|         $field->setup($element, null);
 | |
| 
 | |
|         echo $field->__get('input');
 | |
| 	}
 | |
| 
 | |
|     /**
 | |
|      *  Renders a modal that will be shown on Pro only features
 | |
| 	 * 
 | |
| 	 *  @param    string  $extension_name
 | |
|      *
 | |
|      *  @return   void
 | |
|      */
 | |
|     public static function renderProOnlyModal($extension = null)
 | |
|     {
 | |
| 		$hash = 'proOnlyModal';
 | |
| 
 | |
| 		// Render modal once
 | |
| 		if (Cache::get($hash))
 | |
| 		{
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		$options = [
 | |
| 			'extension_name' => is_null($extension) ? Extension::getExtensionNameByRequest(true) : Extension::getExtensionName($extension),
 | |
| 			'upgrade_url'    => Extension::getTassosExtensionUpgradeURL($extension)
 | |
| 		];
 | |
| 
 | |
| 		$html = LayoutHelper::render('proonlymodal', $options, dirname(__DIR__) . '/layouts');
 | |
| 
 | |
| 		echo HTMLHelper::_('bootstrap.renderModal', 'proOnlyModal', ['backdrop' => 'static'], $html);
 | |
| 
 | |
| 		Cache::set($hash, true);
 | |
| 	}
 | |
| 
 | |
| 	public static function smartTagsBox($options = array())
 | |
| 	{
 | |
| 		HTMLHelper::_('jquery.framework');
 | |
| 
 | |
| 		include_once JPATH_PLUGINS . '/system/nrframework/fields/smarttagsbox.php';
 | |
| 
 | |
| 		$field   = new \JFormFieldSmartTagsBox;
 | |
| 		$element = new \SimpleXMLElement('<field name="pro" type="SmartTagsBox"/>');
 | |
| 		
 | |
| 		$field->setup($element, null);
 | |
| 
 | |
| 		return $field->__get('input');
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Construct the HTML for the input field in a tree
 | |
| 	 * Logic from administrator\components\com_modules\views\module\tmpl\edit_assignment.php
 | |
| 	 */
 | |
| 	public static function treeselect(&$options, $name, $value, $id, $size = 300, $simple = 0, $class = '')
 | |
| 	{
 | |
| 		Functions::loadLanguage('com_menus', JPATH_ADMINISTRATOR);
 | |
| 		Functions::loadLanguage('com_modules', JPATH_ADMINISTRATOR);
 | |
| 
 | |
| 		if (empty($options))
 | |
| 		{
 | |
| 			return '<fieldset class="radio">' . Text::_('NR_NO_ITEMS_FOUND') . '</fieldset>';
 | |
| 		}
 | |
| 
 | |
| 		if (!is_array($value))
 | |
| 		{
 | |
| 			$value = explode(',', $value);
 | |
| 		}
 | |
| 
 | |
| 		$count = 0;
 | |
| 		if ($options != -1)
 | |
| 		{
 | |
| 			foreach ($options as $option)
 | |
| 			{
 | |
| 				$count++;
 | |
| 				if (isset($option->links))
 | |
| 				{
 | |
| 					$count += count($option->links);
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if ($options == -1)
 | |
| 		{
 | |
| 			if (is_array($value))
 | |
| 			{
 | |
| 				$value = implode(',', $value);
 | |
| 			}
 | |
| 			if (!$value)
 | |
| 			{
 | |
| 				$input = '<textarea name="' . $name . '" id="' . $id . '" cols="40" rows="5">' . $value . '</textarea>';
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				$input = '<input type="text" name="' . $name . '" id="' . $id . '" value="' . $value . '" size="60">';
 | |
| 			}
 | |
| 
 | |
| 			return '<fieldset class="radio"><label for="' . $id . '">' . Text::_('NR_ITEM_IDS') . ':</label>' . $input . '</fieldset>';
 | |
| 		}
 | |
| 
 | |
| 		if ($simple)
 | |
| 		{
 | |
| 			$attr = 'style="width: ' . $size . 'px" multiple="multiple"';
 | |
| 
 | |
| 			if (!empty($class))
 | |
| 			{
 | |
| 				$attr .= ' class="' . $class . '"';
 | |
| 			}
 | |
| 
 | |
| 			$html = HTMLHelper::_('select.genericlist', $options, $name, trim($attr), 'value', 'text', $value, $id);
 | |
| 
 | |
| 			return $html;
 | |
| 		}
 | |
| 
 | |
| 		HTMLHelper::script('plg_system_nrframework/treeselect.js', ['relative' => true, 'version' => true]);
 | |
| 		HTMLHelper::stylesheet('plg_system_nrframework/treeselect.css', ['relative' => true, 'version' => true]);
 | |
| 
 | |
| 		$html = array();
 | |
| 
 | |
| 		$html[] = '<div class="nr_treeselect" id="' . $id . '">';
 | |
| 		$html[] = '
 | |
| 			<div class="form-inline nr_treeselect-controls">
 | |
| 
 | |
| 				<span class="nr_treeselect_control">' . Text::_('JSELECT') . ':
 | |
| 					<a class="nr_treeselect-checkall" href="javascript:;">' . Text::_('JALL') . '</a>,
 | |
| 					<a class="nr_treeselect-uncheckall" href="javascript:;">' . Text::_('JNONE') . '</a>,
 | |
| 					<a class="nr_treeselect-toggleall" href="javascript:;">' . Text::_('NR_TOGGLE') . '</a>
 | |
| 				</span>
 | |
| 				
 | |
| 				<span class="nr_treeselect_control">' . Text::_('NR_EXPAND') . ':
 | |
| 					<a class="nr_treeselect-expandall" href="javascript:;">' . Text::_('JALL') . '</a>,
 | |
| 					<a class="nr_treeselect-collapseall" href="javascript:;">' . Text::_('JNONE') . '</a>
 | |
| 				</span>
 | |
| 				
 | |
| 				<span class="nr_treeselect_control">' . Text::_('JSHOW') . ':
 | |
| 					<a class="nr_treeselect-showall" href="javascript:;">' . Text::_('JALL') . '</a>,
 | |
| 					<a class="nr_treeselect-showselected" href="javascript:;">' . Text::_('NR_SELECTED') . '</a>
 | |
| 				</span>
 | |
| 
 | |
| 				<span class="nr_treeselect_control nr_treeselect-filter right">
 | |
| 					<input type="text" name="nr_treeselect-filter" class="search-query nr_treeselect-filter" size="16"
 | |
| 					autocomplete="off" placeholder="' . Text::_('JSEARCH_FILTER') . '" aria-invalid="false" tabindex="-1">
 | |
| 				</span>
 | |
| 			</div>';
 | |
| 
 | |
| 		$o = array();
 | |
| 		foreach ($options as $option)
 | |
| 		{
 | |
| 			$option->level = isset($option->level) ? $option->level : 0;
 | |
| 			$o[]           = $option;
 | |
| 			if (isset($option->links))
 | |
| 			{
 | |
| 				foreach ($option->links as $link)
 | |
| 				{
 | |
| 					$link->level = $option->level + (isset($link->level) ? $link->level : 1);
 | |
| 					$o[]         = $link;
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		$html[]    = '<ul class="nr_treeselect-ul" style="max-height:300px;min-width:' . $size . 'px;overflow-x: hidden;">';
 | |
| 		$prevlevel = 0;
 | |
| 
 | |
| 		foreach ($o as $i => $option)
 | |
| 		{
 | |
| 			if ($prevlevel < $option->level)
 | |
| 			{
 | |
| 				// correct wrong level indentations
 | |
| 				$option->level = $prevlevel + 1;
 | |
| 
 | |
| 				$html[] = '<ul class="nr_treeselect-sub">';
 | |
| 			}
 | |
| 			else if ($prevlevel > $option->level)
 | |
| 			{
 | |
| 				$html[] = str_repeat('</li></ul>', $prevlevel - $option->level);
 | |
| 			}
 | |
| 			else if ($i)
 | |
| 			{
 | |
| 				$html[] = '</li>';
 | |
| 			}
 | |
| 
 | |
| 			$labelclass = trim('pull-left ' . (isset($option->labelclass) ? $option->labelclass : ''));
 | |
| 
 | |
| 			$html[] = '<li>';
 | |
| 
 | |
| 			$item = '<div class="' . trim('nr_treeselect-item pull-left ' . (isset($option->class) ? $option->class : '')) . '">';
 | |
| 			if (isset($option->title))
 | |
| 			{
 | |
| 				$labelclass .= ' nav-header';
 | |
| 			}
 | |
| 
 | |
| 			if (isset($option->title) && (!isset($option->value) || !$option->value))
 | |
| 			{
 | |
| 				$item .= '<label class="' . $labelclass . '">' . $option->title . '</label>';
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				$selected = in_array($option->value, $value) ? ' checked="checked"' : '';
 | |
| 				$disabled = (isset($option->disable) && $option->disable) ? ' readonly="readonly" style="visibility:hidden"' : '';
 | |
| 
 | |
| 				$item .= '<input type="checkbox" class="pull-left" name="' . $name . '" id="' . $id . $option->value . '" value="' . $option->value . '"' . $selected . $disabled . '>
 | |
| 					<label for="' . $id . $option->value . '" class="' . $labelclass . '">' . $option->text . '</label>';
 | |
| 			}
 | |
| 			$item .= '</div>';
 | |
| 			$html[] = $item;
 | |
| 
 | |
| 			if (!isset($o[$i + 1]) && $option->level > 0)
 | |
| 			{
 | |
| 				$html[] = str_repeat('</li></ul>', (int) $option->level);
 | |
| 			}
 | |
| 			$prevlevel = $option->level;
 | |
| 		}
 | |
| 		$html[] = '</ul>';
 | |
| 		$html[] = '
 | |
| 			<div style="display:none;" class="nr_treeselect-menu-block">
 | |
| 				<div class="pull-left nav-hover nr_treeselect-menu">
 | |
| 					<div class="btn-group">
 | |
| 						<a href="#" data-toggle="dropdown" data-bs-toggle="dropdown" class="dropdown-toggle btn btn-secondary">
 | |
| 							<span class="caret"></span>
 | |
| 						</a>
 | |
| 						<ul class="dropdown-menu">
 | |
| 							<li class="nav-header">' . Text::_('COM_MODULES_SUBITEMS') . '</li>
 | |
| 							<li class="divider"></li>
 | |
| 							<li>
 | |
| 								<a class="checkall" href="javascript:;">
 | |
| 									<span class="icon-checkbox"></span> 
 | |
| 									' . Text::_('JSELECT') . '
 | |
| 								</a>
 | |
| 							</li>
 | |
| 							<li>
 | |
| 								<a class="uncheckall" href="javascript:;">
 | |
| 									<span class="icon-checkbox-unchecked"></span>
 | |
| 									' . Text::_('COM_MODULES_DESELECT') . '
 | |
| 								</a>
 | |
| 							</li>
 | |
| 							<div class="nr_treeselect-menu-expand">
 | |
| 								<li class="divider"></li>
 | |
| 								<li><a class="expandall" href="javascript:;"><span class="icon-plus"></span> ' . Text::_('NR_EXPAND') . '</a></li>
 | |
| 								<li><a class="collapseall" href="javascript:;"><span class="icon-minus"></span> ' . Text::_('NR_COLLAPSE') . '</a></li>
 | |
| 							</div>
 | |
| 						</ul>
 | |
| 					</div>
 | |
| 				</div>
 | |
| 			</div>';
 | |
| 		$html[] = '</div>';
 | |
| 
 | |
| 		$html = implode('', $html);
 | |
| 		return $html;
 | |
| 	}
 | |
| 
 | |
| 	public static function treeselectSimple(&$options, $name, $value, $id, $size = 300, $class = '')
 | |
| 	{
 | |
| 		return self::treeselect($options, $name, $value, $id, $size, 1, $class);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Wrapper for the HTMLHelper::script method to support old method signatures in Joomla < 3.7.0.
 | |
| 	 *
 | |
| 	 * @param  string $path
 | |
| 	 *
 | |
| 	 * @deprecated Since we no longer support 3.7.0, use HTMLHelper::script directly. 
 | |
| 	 * @return void
 | |
| 	 */
 | |
| 	public static function script($path)
 | |
| 	{
 | |
| 		if (version_compare(JVERSION, '3.7.0', 'lt'))
 | |
| 		{
 | |
| 			HTMLHelper::script($path, false, true);
 | |
| 		} else 
 | |
| 		{
 | |
| 			HTMLHelper::script($path, ['relative' => true, 'version' => 'auto']);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Wrapper for the HTMLHelper::stylesheet method to support old method signatures in Joomla < 3.7.0.
 | |
| 	 *
 | |
| 	 * @param  string $path
 | |
| 	 *
 | |
| 	 * @return void
 | |
| 	 * @deprecated Since we no longer support 3.7.0, use HTMLHelper::script directly.
 | |
| 	 */
 | |
| 	public static function stylesheet($path)
 | |
| 	{
 | |
| 		if (version_compare(JVERSION, '3.7.0', 'lt'))
 | |
| 		{
 | |
| 			HTMLHelper::stylesheet($path, false, true);
 | |
| 		} else 
 | |
| 		{
 | |
| 			HTMLHelper::stylesheet($path, ['relative' => true, 'version' => 'auto']);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * For Backwards Compatibility
 | |
| 	 * 
 | |
| 	 * @deprecated 4.9.50
 | |
| 	 */
 | |
| 	public static function checkForOutdatedExtension($extension, $days_old = 120)
 | |
| 	{
 | |
| 		if (!Extension::isOutdated($extension, $days_old))
 | |
| 		{
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		// Load extension's language file
 | |
| 		Functions::loadLanguage($extension);
 | |
| 		
 | |
| 		$payload = [
 | |
| 			'extension' => Text::_($extension),
 | |
| 			'days_old' => $days_old
 | |
| 		];
 | |
| 		
 | |
| 		// load template
 | |
| 		return LayoutHelper::render('outdated_extension', $payload, dirname(__DIR__) . '/layouts');
 | |
| 	}
 | |
| 
 | |
| 	public static function updateNotification($extension)
 | |
| 	{
 | |
| 		$version_installed = Extension::getVersion($extension);
 | |
| 		$version_latest    = Extension::getLatestVersion($extension);
 | |
| 
 | |
| 		if (!$needsUpdate = version_compare($version_latest, $version_installed, 'gt'))
 | |
| 		{
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		// Load extension's language file
 | |
| 		Functions::loadLanguage($extension);
 | |
| 
 | |
| 		// Extension Title
 | |
| 		$title = Text::_($extension);
 | |
| 		$title = str_replace('System -', '', $title); // Remove plugin folder prefix from plugins
 | |
| 
 | |
| 		// Render Layout
 | |
| 		$data = [
 | |
| 			'title' 	        => $title,
 | |
| 			'version_installed' => $version_installed,
 | |
| 			'version_latest'    => $version_latest,
 | |
| 			'ispro' 	        => Extension::isPro($extension),
 | |
| 			'upgradeurl'        => Extension::getTassosExtensionUpgradeURL($extension),
 | |
| 			'product_url'       => Extension::getProductURL($extension)
 | |
| 		];
 | |
| 
 | |
| 		return LayoutHelper::render('updatechecker', $data, JPATH_PLUGINS . '/system/nrframework/layouts');
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * TODO: Not used anywhere, should delete.
 | |
| 	 * 
 | |
| 	 * @deprecated 4.11.7
 | |
| 	 */
 | |
| 	public static function checkForUpdates($element)
 | |
| 	{
 | |
| 		HTMLHelper::script('plg_system_nrframework/updatechecker.js', ['relative' => true, 'version' => true]);
 | |
| 		HTMLHelper::stylesheet('plg_system_nrframework/updatechecker.css', ['relative' => true, 'version' => true]);
 | |
| 
 | |
| 		return '
 | |
| 			<div class="nr_updatechecker"
 | |
| 				data-element="' . $element. '" 
 | |
| 				data-base=' . Uri::base() . ' 
 | |
| 				data-token=' . Session::getFormToken() . '>
 | |
| 			</div>
 | |
| 		';
 | |
| 	}
 | |
| } |